Ruby on Rails
Ruby on Rails may be the next “Killer-App” for web appliction programmers. Here is a basic recipe for creating your first Rails-based application.
Ruby and Rails make creation of these applications easier than ever before. By prefering convention to configuration, you will find its a lot easier to build an application following Rails’ conventions. However, since that is not always possible, there are workarounds for many of these convetions.
Installing Rails on your computer and your Brain
Okay, Intalling Ruby and Rails on your computer can be done by the following links:
- Ruby
- Gems
- Rails
The harder part is installing Rails on your Brain. While it was designed to be easy to use, Rails does still require some prerequisite talents and studying. While you can get by without all these at first, its always worthwhile to bone up on these subjects.
- Object-Oriented Programming
- Ruby Programming Language
- Relational Databases and Data Modeling
- MVC, The Model-View-Controller Architecture
- HTML, hopefully XHTML, CSS, JavaScript, and “AJAX“.
- Some basics of web design and usability are definitely a plus!
Starting your Rails Project
This command creates a Rails application structure at a directory under your current one. This creates a “myapp” named application.
$ rails myapp
This creates a directory structure named “myapp” with a slew of files and configurations. The structure is very intuitive and easy to grasp. Here is a table of the directory and basic files. Most of these files may not be there quite yet, they will be created after you “generate” parts of the application later.
| Location | Purpose |
|---|---|
| .app/controllers/application.rb | Application Controller Helper |
| .app/controllers/ccc_controller.rb | Application Controller File |
| .app/helpers/application_helper.rb | View Helper for all views |
| .app/helpers/ccc_helper.rb | View Helpers for controller ccc |
| .app/models/ttt.rb | Model definitions for table ttt (singluar) |
| .config/database.yml | Database Connection Settings |
| ./config/environment.rb | Application Level Settings |
| ./config/environments/eee_rb | Development/Test/Production Settings |
| ./config/routes.rb | Route (URL Naming) Rules |
| ./log/development.log | Framework actions running under development |
| ./test/fixtures/ttt.yml | Testing Records for table ttt |
| ./test/functional/ccc_test.rb | Controller Tests for ccc |
| ./test/mocks/eee/* | Mock-tests for environemtn eee |
| ./test/test_helper.rb | Application test helper module |
| ./test/unit/ttt_test.rb | Unit Tests for module ttt |
| ./vendor/ | 3rd Party logic |
| ./script/console | Interactive (irb) console with app libraries |
| ./script/generate | Generates Ruby code into this framework |
| ./script/server | Runs the webrick web server for development |
Your Database
Rails applications are primarily database-driven applications, so you will need a database with schema (tables, etc.) to use. While I can not determine your schema, you will need to follow a few convections as part of the Rails way of doing things. Note that these can be worked around, but it will save you work from the onset if you can abide by these rules.
- Database tables should be named as a plural form of a noun, like “pages”
- Tables are named like word_word. etc.
- Set a single primary key named “id” (lower case!) for the table.
- Sequence names (for advanced db’s) are named “pages_id_seq”
- There are special column names wich Rails will update automatically for you
- created_on - Date the record was inserted
- created_at - Timestamp (date and time) the record was inserted.
- updated_at - Timestamp (date and time) the record was inserted or updated
- Do not name tables with reserved or library names for Ruby or Rails like “Thread/s”
- Do not name columns with reserved Ruby symbols or standard names like “method”
- Keep a single column as a primary key.
- Foreign keys are named as “table_name_id”
Now, create your database and tables. Rails has a scheme to use 3 databases for an application. You should create at least 2 of them now! The popular databases like MySQL, PostgreSQL, Oracle, and DB2 are suppored. Rails was written to work primarily with MySQL, but feel free to choose a better database. I prefer Postgres for data integrity and licensing issues among other reasons.
- Development database (myapp_development)
- Test database (myapp_text)
- Production database (myapp_production). You can create this later, of course.
$ createdb myapp_development CREATE DATABASE $ createdb myapp_test CREATE DATABASE $ psql myapp_development < myapp/db/myapp.ddl.sql
Connecting the Application to the Database
Now you need to tell rails what the databases are. You can find that in the ./config/database.yml file in your Rails application. It lists database records for development, test, and production. Fill in your database adapter (mysql, postgresql, oracle, db2 or whatever) and connection infomation.
What if you need to connect to a second (or more) databases from your application? Add additional records into your database.yml file for each database. For a “security” database, create this entry for development, test, and production environments.
security_development: adapter: postgresql database: security host: localhost username: allen password: xxxxx
Next, edit the ./config/environment.rb file, just under the ActiveRecord::Base.establish_connection line add a line to connect from your model class to the name you used above. The RAILS_ENV variable contains either development, test, or production, so we can use that to differentiate depending on the environment. Say we have a model “Authorization” that needs to access a table in the security database, so we add:
Authorization.establish_connection "security_#{RAILS_ENV}”
Unknown: Do we have to do this for every table we need to access in non-application databases? Would it create multiple connections or cache the connection by its name? Does the security_test database get deleted and reconstructed during tests?
ActiveRecord
The ActiveRecord component of Rails is used to model the database in a Ruby Data Framework. This is an essential part of Ruby, but ActiveRecord is not essential to Rails. ActiveRecord utilizes what Ruby does best, integrating Object-Oriented abstact base classes with metaprogramming work.
For each table you wish to add to the model, run the generate command. change the table name into the model name by
- Make the Model Name singular (table name is plural)
- Make the Model Name CamelCase (table account_users becomes AccountUser)
$ .script/generate model AccountUser
This adds a file called ./app/models/account_user.rb to the application, containing:
class AccountUser < ActiveRecord::Base end
Do this for every table you wish to add to the application. (Of course, you can share a data model repository between application!) This basic definition gives you full access to a conventions-bases access to the table. At runtime, the dtabase catalogs are queries to yield the table columns and definitions to build upon.
Not every data model conforms to the Rails ways of doing things. So Rails provides the class decorator methods to dynamically change the model class If you don’t follow that gibberish, just watch... its easy to understand.
Each ActiveRecord Class assumes the following:
- Table Name is plural and “_” form of class name, “AccountUser” becomes “account_users”
- Primary key is “id” (lower case), usually an integer, and a single column
You can use decorators to override these rules:
class AccountUser < ActiveRecord::Base set_table_name "user_account" set_primary_key "employee_id" end
Overriding Implied Keys
If your database schema does not match the Rails way of doing things, that’s okay! Sure, it will require a couple lines of work, but even in the end you will still be ahead using Rails.
class Project < ActiveRecord::Base set_table_name "my_table" set_primary_key "project id" end
See? That wasn’t hard!
Associations
You can add foreign keys, and inter-table relationships into the records to preserve the integrity and give you easy access to these relationships in your code. You specify the table name in symbol form as the association
class Project < ActiveRecord::Base
belongs_to :portfolio
has_one :project_manager
has_many :milestones
has_and_belongs_to_many :categories
end
Defining these associations defines methods in your ActiveRecord class to access these related tables. Substitute your table or association name for assoc and the plural name for assocs in the list. They will be available for the expected singular or multiple association.
assoc()fetches associated record(s) from the databaseassoc=(record)to assign a a record to that associationassoc.nil?check if the association has a linked recordassocs.empty?check if the association has any linked record.assocs.sizeHow many records exist in the associationassocs«(record)Adds a record in the associationassocs.delete(record)Deletes a record from the associationassocs.find(id)Returns the matching record from the associationassocs.find_all(conditions)Returns the matching records from the associationassocs.build(attributes)Forms an association record objectassocs.create(attributes)Builds and saves the association record
When defining the association, the following optional parameters may be given when appropriate.
:association_name, names the association, usually the table_name.:order ⇒ “col, cols desc”, sets the order of returned records:conditions ⇒ [”where clause”], Any where-clause additions required:class_name ⇒ RecordClass, to override the implied association class name:foreign_key ⇒ foreign_key_name, override the implied foreign key name:dependent ⇒ parm, what happens to dependent records in the association:destroyortrue, destroys (cascade deletes) dependent records:nullify, sets the values to null in the dependent records
:exclusively_dependent ⇒true, deletes all dependent records at once from your class:finder_sql⇒“statement”, a complete statement to select rows for the association:counter_sql⇒“statement”, a complete statement to select rows counts for the association:extend ⇒ ModuleName, extends class with external module methods shared between classes:include ⇒ :association, loads a secondary association along with current data
Association records from the database are cached. Subsequent accesses do not hit the database again unless you spefify the force_relead parameter: record.assoc_record(true).