Ruby on Rails Basics
A basic crash course in understanding ruby on rails. Originally published on my Clinic Booking System site.
Rails (often called Ruby on Rails) is a rapid web-application development framework written in Ruby. It favours convention over configuration.
For information on how the source code of a rails application is structured, see Source Code Structure.
The simplest way to get started on rails is to download Instant Rails (for Windows) or Locomotive (for Mac).
RadRails
RadRails is an Eclipse-based development IDE for Rails. It is open source and free.
It is a powerful tool and some of it's most useful features are:
- Testing suite
- Server management (can run WebBrick and Lighttp servers)
- Syntax Highlighting
- Built in generators and rake tasks
NetBeans
Netbeans is another popular open-source Rails-capable IDE. I have not had a chance to use it yet, but I thought I'd add it here for your information.
Ruby
The most widely used Ruby book is called Programming Ruby, though it is more commonly known as the pickaxe.
For a more interesting (and bizarre) tutorial on ruby, there's also Why's (Poignant) Guide to Ruby.
Generally, ruby is a very easy language to read. Here's a trivial example:
class Client < Test::Unit::TestCase
def test_is_true
test = true
test = false if true == false
assert test
end
end
The first line defines a class which inherits from the class testcase from the unit module in the test name space.
The second line defines a method (with no paramaters) called test_is_true.
The third line sets the variable test to true. The next line will then set the test variable to false only if false equals true.
The fifth line then asserts that test is true.
There are two other concepts that are common in Ruby applications- blocks and hashes.
clinicians.each do |clinician|
clinician_names << clinician.full_name
end
This is a block which creates an array of clinician names from an array of clinicians. The curly brackets could used instead of the do .. end statements. The variable designated between the pipes (clinician in this case) contains the function values at each iteration (in this case, a clinician object). This could have been written with curly braces ( { and } ) instead of do and end. A better way to write this would be:
clinician_names = clinicians.map {|c| c.full_name }
A hash is a set of key-value pairs:
my_hash = {:a_key => 'a value', :another => 54 }
Variables
All variables in ruby are variants. Variables are not declared.
The scope of a variable depends on where it was first used. The exception to this are instance, class and global variables.
variable @instance_variable @@class_variable $global_variable
The symbol before the variable name determines whether it is a special variable. Instance variables are in scope throughout an object's lifecycle. They are often used in views to display information about the object. Class variables are in scope within all objects of the class. Global variables are in scope always.
Symbols
Ruby also contains symbols. A symbol is basically a string, except that it is easier for the computer to process (as it doesn't contain all the methods of the string class). A symbol is preceded by a colon.
Databases
Using the Rails philosophy of convention over configuration, rails allows the developer to create a database backed application without having to duplicate model information between the application and the database. More information about this can be found in the Models section.
List of compatable databases from the wiki:
- MySQL
- PostgreSQL
- SQLite
- SQL Server
- IBM DB2
- Oracle
- Firebird/Interbase
- LDAP - Not exactly a DB driver, but ActiveLDAP can be used with Rails
- SybaseASA (Sybase Adaptive Server Anywhere aka SQL Anywhere Studio)
Configuration
A rails application configures it's database connection through a file in the config directory called database.yml. This file is fairly straight forward. There are three databases defined, development, test and production, which correspond to the different environments (See Environments below).
The first line of each definition is adapter. This defines the type of database you are using. Note that some database configurations may require the use of the socket field. See the previous section on supported databases or view the Rails wiki.
The next four lines contain the database details:
- database
The name of the database. The default is app-name_environment - username
A user name which has full access to this database - password
The password of the nominated user. Can be left blank if there is no password. - host
The connection host name. Generally this will be localhost.
Environments
There are three environments available by default to a rails application. Each of these environments has it's own database and settings. The environment used is specified by the server when it is starting or by the environment config file.
The development environment is, as the name suggests, used when developing an application. The ensures that if you are working on changes to the application, it does not effect the live application. It also allows gives application traces when errors occur and does not use caching (to ensure any changes made are seen without needing to restart the server.
The test environment is used to run the tests in. For more information on testing in rails, see Testing.
The production environment is the environment that the application will run under after it has been released to users. It employs caching to make the application faster and also hides information from the end user if there is an error.
Migrations and Schema
Migrations define the structure of the database. They allow you to make incremental changes to the database structure (and then undo them if they caused problems!). The schema file (schema.rb) is a summary of the structure at the latest version. The schema file is under the db directory. The migrations are in the db/migrate directory.
Each migration file corresponds to a new increment. The first number in each file name defines the increment number (and of course the order of the migrations). Migration files are written in ruby and contain two methods: up and down. The up method defines the changes to be made, the down method undoes those changes (if possible).
An example migration:
class CreateDisciplines < ActiveRecord::Migration
def self.up
create_table :disciplines do |t|
t.column :name, :string
t.column :description, :text
end
end
def self.down
drop_table :disciplines
end
end
Here is a simple migration which creates a table. It uses language familiar to people with SQL experience. To break it down:
create_table :disciplines do |t|
Will create a database table with the name disciplines
t.column :name, :string
Will create a column with the name name of type string.
drop_table :disciplines
Will delete the table disciplines
Another common piece of migration code is the rename_column method:
rename_column :clients, :firstName, :first_name
This will rename the column firstName to first_name in the table clients.
Rails migrations are run using a rake task. From the console, go to the root directory of the application and run:
rake db:migrate
Models
Models define the rules that govern objects within the application, as well as any methods that object may have. Most methods (like find create and validate) are already defined in the active record class, from which the models all inherit.
When you create a model, Rails will automatically look for a table to match this. For example, if you create a model called discipline, Rails will use the data from the table disciplines to construct the model. So if the table disciplines has two fields, name and description, the class discipline will have two properties (methods more accurately) matching these. You could now call:
this_discipline = Discipline.find(:first)
name = this_discipline.name
description = this_discipline.description
This will find the first discipline in from the table and create and object matching that data. It then reads the name and description properties.
Validation
Validation enforces the rules of the model. Validation rules can be applied on creation and when updating (by default it's both). Rails supplies a number of validation helper methods.
Additional validation can be placed in the validate and validate_create protected methods.
For example, consider this model definition for discipline:
class Discipline < ActiveRecord::Base
has_many :clinicians
validates_presence_of :name
validates_uniqueness_of :name
end
The first line tells us that this class is a decendant of the ActiveRecord class. The second line defines a relationship (for further information see Relationships). The last two lines are validation rules (using the supplied helpers). The first rule tells Rails that the field name must be provided (ie not null) and the second that it must be unique.
If you wanted to ensure that the name and description were not the same, you could write an additional validation rule:
protected
def validate
errors.add :name, "problem" if name == description
end
This will add a validation error (on the field name) if the name and description are the same. Otherwise the validation will pass and the object will be created/updated.
Relationships
Defining relationships between objects is the other important role of the model. There are a number of available relationship types, such as:
- has_one
- has_many
- belongs_to
- has_many_and_belongs_to
Rails assumes that foreign keys have been defined in the relevant databases in the form foreign-table-name_id.
Once these relationships are established, the object will have new methods. For example, in the above discipline class you could now do this:
discipline = Discipline.find(:first)
discipline.clinicians.each do |clinician|
clinician_names << clinician.name
end
This would perform the rather pointless task of getting all the clinicians of a certain discipline and adding their names to an array.
Controllers
Controllers provide actions to the user (through Views) to the model.
You may have a controller that looks like:
class DisciplineController < ApplicationController
def list
@disciplines = Discipline.find(:all)
end
end
This is a simple controller, with one method (called an action). The list action in this case is very simple, it just creates an array of Discipline objects. Note that this doesn't define how the list should be displayed, only what data to display.
By default, this action could be accessed at http://localhost/discipline/list and would be rendered by /app/views/discipline/list.rhtml.
Views
Views are the interface to the user. A view might present information returned by a controller or may be a form that sends information to an action (or both).
The list view of the previous discipline controllers might look like:
<h2>Discipline List</h2>
<ul><% @disciplines.each do |discipline| -%>
<li><%= discipline.name %></li>
<% end -%>
</ul>
This will create an unordered list of discipline names. There are few things to note here:
- The
<%sign begins a block of Ruby code - The
%>sign ends a block of Ruby code
If there is a - symbol, it does not print an end of line character - The
<%=block will render the returned value
Views may also be in other forms, such as javascript templates (*.rjs) and XML (*.rxml).
Most rails applications should use templates, to render common parts of an interface. This means the views only contain the content specific to that page. These templates are called layouts in rails.
Rails also allows views to contain partials. A partial is a part of a view that can be reused by other views. All partials' file names start with an underscore (_). When a partial is called, you can also pass it an object (see below). Inside the partial, this object can be accessed through the partial array (with the same name as the partial).
Partial called from view:
render(:partial => 'mypartial',
:object => {:name => 'Jarrod', :surname => 'Swift'})
Partial code (_mypartial.rhtml):
<p><span class="first-name"><%= mypartial[:name] %></span>
<span class="surname"><%= mypartial[:surname] %></span></p>
Would render (with the help of a little css):
JarrodSwift
Testing
Tests can be run from the console. From the rails application root directory type:
rake test
This will run all the tests (functional, unit and integration). You can run only the unit tests using test:units, the functional tests with test:functionals and the integration tests with test:integration.
1 August 2006