Log in / create account Article Discussion History Go to the site toolbox

Ruby on Rails

From WikiNayak

Contents

Introduction

This article is a getting started article on ruby on rails. Most of the content for this article is sourced from many wikis and blogs, references to all of these are provided at the end. Instructions provided in this article may change with newer versions and alternate platforms. I run Ubuntu 7.10 on my Thinkpad R60.

Ruby

Ruby is a dynamic, reflective, general purpose object-oriented programming language that combines syntax inspired by Perl with Smalltalk-like features. Ruby originated in Japan during the mid-1990s and was initially developed and designed by Yukihiro "Matz" Matsumoto.

Ruby supports multiple programming paradigms, including functional, object oriented, imperative and reflection. It also has a dynamic type system and automatic memory management; it is therefore similar in varying respects to Python, Perl, Lisp, Dylan, and CLU.

In its current, official implementation, written in C, Ruby is a single-pass interpreted language. There is currently no specification of the Ruby language, so the original implementation is considered to be the de facto reference. As of 2008, there are a number of complete or upcoming alternative implementations of the Ruby language, including YARV, JRuby, Rubinius, IronRuby, and MacRuby, each of which takes a different approach, with JRuby and IronRuby providing just-in-time compilation functionality.

Ruby on Rails

Ruby on Rails is an open source web application framework for the Ruby programming language. It is often referred to as 'Rails' or 'RoR'. It is intended to be used with an Agile development methodology, which is often utilized by web developers for its suitability for short, client-driven projects.

Ruby on Rails was extracted by David Heinemeier Hansson from his work on Basecamp, a project management tool by now web application company 37signals.

Installation

Thankfully ruby and rails both have really good documentation and you should have no problems installing rails on any platform.

For platform specific installations instructions visit this page on the ror wiki

If you use rails on Ubuntu, like I do,visit this page. In that page, use the recommended way and not the quick start guide.

Its also advised that you get Ruby Gems, the package manager for ruby as most plugins needed for ror apps can be sourced using Gems. If you follow the instructions in the link above, Gems will be installed.

Its sufficient to use the pre-built SQLite for persistence and Brick server for serving pages. But if you want to use a database, please install mysql and also the drivers for the same from your package manager.

sudo apt-get install libmysql-ruby mysql-server

Quickest Rails Demo Ever

Just to check if your rails installation was successful and that you have no problem going ahead, you can try this. Although if you are reading this entire article, please skip this part and read the First Rails App section.

  1. create a folder to put all your rails apps in. Say RailsApps
  2. Inside RailsApps run the following commands
rails dummy
cd dummy 
ruby script/server
  1. Once this is done, you must see either the default WebBrick or the mongrel server starting up.
  2. Open your favorite browser and in that navigate to http://localhost:3000
  3. If you see a Welcome Aboard page then you have successfully installed ror !!

First Rails App

Lets begin building your first rails app. To use this tutorial, please make sure that you have mysql installed. If you have installed SQLite, then default configurations should work by itself.

First create a directory to put all your rails apps in, and navigate into the directory.

mkdir RailsApps
cd RailsApps

Once you have created the database. run the following command.

 For rails 2.x  run  rails -d mysql exchange
 For rails 1.x run rails exchange

You will see a lot of scrolling text followed by the command line again. This step generates framework required for the rails app. Navigate the folder structure and you will see lot of folders like app, config etc.


For Rails 1.0 version

Instructions in this section are relevant only to rails version 1.0. To see what version of rails you have installed, please run rails --version on the command line. For the latest version, ie Rails 2.0, please navigate to the section relevant for the same.

Database connection information

 You can skip this section if you have run rails with the -d mysql option 

Fire up your mysql client and create three databases called exchange_development, exchange_test and exchange_production.

To connect your new Rails application to your existing database you now need to open up the database.yml file. You will find this file inside the config folder in your Rails application ie exchange/config/

Find the code for test configuration and modify the code to look like this.

development:
 adapter: mysql
 database: contact
 username: root
 password:
 socket: /path/to/your/mysql.sock 

mysql.sock in Ubuntu is found at /var/run/mysqld/mysqld.sock. Make sure all the spacing is maintained else you will get error messages when using this file to migrate the database.

Migrating the database (Rails 1.0 )

Rails was built for agile development and writing SQL scripts for database changes in subsequent versions is not a very elegant way to be agile. Rails helps in creating and migrating database scripts automatically. If you remember you have only created the database, but no tables as yet.

Inside the Contact folder run the following command

ruby script/generate migration contact_db

you will see a folder called db/migrate created and a file inside called 2008xxxxx_contact_db.rb This file is important because it makes migrating your databases really simple. As your schema evolves, you can have other revisions of this file that can make db operations really simple.

Open that file in your favorite editor and add these lines inside the self.up and self.down methods

Between self.up and end

create_table "people", :primary_key => 'id' do |t|
 t.column "name", :string
 t.column "address", :string
 t.column "city", :string
 t.column "state", :string
 t.column "zipcode", :string
end 

Between self.down and end

drop_table :people

After this your file should look like this

class ContactDb < ActiveRecord::Migration
 def self.up
  create_table "people", :primary_key => 'id' do |t|
  t.column "name", :string
  t.column "address", :string
  t.column "city", :string
  t.column "state", :string
  t.column "zipcode", :string
  end 
 end
 def self.down
  drop_table :people
 end
end

The file is a method for creating scripts for table creation. Create_table creates the table called people. t.column specifies that its a column , quoted strings like name is the name of the column and :string is the datatype for the field. :string is equal to varchar(255).

Next, we run the migration script. In your command line inside the Contact folder run

rake db:migrate 

If you want to run it verbose, then run it with --trace option.

Scaffolding(Rails 1.0)

To build the framework for our new application, we shall use scaffolding. Scaffolding creates the model, view and controller classes for the application. It also adds pre-built code to our controller and view files. With this, we can now enter data into our database via our application.

In your command line run the following command.

ruby script/generate scaffold Person

Rails 2.0

Rails 2.0 instructions are phenomenally different from the prior version. The process has become much easier and more organized.

Database connection

Open the config/database.yml file and change the usernames and passwords of the three databases(ie exchange_development, exchange_test and exchange_production)

Once you have given the right credentials, run the following command in the exchange folder.

rake db:create:all 

If you see your mysql database now, you will see that three empty databases are created.

Scaffolding

The next steps show where differences between older Rails tutorials will become greatest. Older tutorials would script/generate a model then use the migrate file created to layout columns in the model's database table. Next you would script/generate a controller and add scaffolding. This will fail in Rails 2.0.

Start simple. Movies should have, at minimum a title, a description and a movie poster. Columns for other data like release date, rating or quantity on hand can be added later by altering the table through migrations. The next step is to create a model whose job will be to manage the data stored in the database.

The following command will generate the model, plus scaffolding, and the database migration script needed as well as a controller, helper, and testing support files

ruby script/generate scaffold Movie title:string description:text one_sheet_url:string

In the command you are specifying that you need a model for a entity called Movie which have the attributes which are title which is a string, description which is a text and one_sheet_url which is a string.

If you look at the file db/xxxx_create_movies.rb you will see the following class definition for creating the database.

class CreateMovies < ActiveRecord::Migration
 def self.up
   create_table :movies do |t|
     t.string :title
     t.text :description
     t.string :one_sheet_url
     t.timestamps
   end
 end
 def self.down
   drop_table :movies
 end
end

This is the same file that we had to manually write in Rails 1.0 , but in 2.0 its auto-generated. This file will create a table called movies that will be tied to the model Movie. If you see, in your command you specified the entity Movie, but the table is called Movies. This is the de-facto naming convention in rails.

Migrating the database

The next step is to actually apply this migration to create the database tables and columns. You can do this by running this command

rake db:migrate

The next step is to see the result yourself. Start your rails server (Webrick or Mongrel) by running ruby script/server . Open your favorite browser and point the url to http://localhost:3000/movies.

CRUD

By not writing a single line of code, we have created a CRUD operation on our entity. CRUD is an acronym for Create Read Update Delete. You can now click on the New Movie link and add a couple of movies. Once you are dont go back and you will see that these movies are listed in the movies page and if you check the backend tables they are populated too.

Rails Architecture Explained

Rails uses the MVC architecture to structure applications. The MVC architecture was proposed by Trygve Reenskaug while working at Xerox on Smalltalk.

MVC architecture makes it easy to separate concerns and helps in maintaining the system better. The design becomes clean and easy to understand. If you understand MVC, then rails will be a cinch.

Model The Model is all about the data. This includes getting the data in and out of the data store. The scaffolding we set up in part one gives us the four basic operation of using a data store Create, Read, Update and Destroy. Other data centric functionality also goes in the Model. Searching for data, manipulating data, validating input, and possibly editing the data for display (although sometimes this might logically be part of the view as well).

View The View renders the Model in an interactive displayable format. It takes the data in the Model and paints it up on the screen for you to see and interact with.

Controller The Controller responds to events communicating with the Model and the View. This is like the Main loop in a state machine waiting for events like user actions or Model data to show up and reacting as the program dictates to those events. Let's look at the Model View and Controller created by the Rails scaffolding, and start updating each of them with our own code.

We'll make changes to the Model and to the view and then get into the controller and migrations in the next installment.

Model in Rails

Scaffolding creates the three components in the app folder. If you look at the folder app/model/ you will find a file called movie.rb. The Movie class is the model and it inherits from ActiveRecord::Base which is one of the gems that is installed.

The :: symbol is the Ruby scope operator. In ActiveRecord::Base it means that you are referring to the Base of ActiveRecord and not some other base.

We can modify this class to add funtionalities that must be present in the model. One of the primary functions in the model is to validate the data going into the database. Add the following line to the class

validates_presence_of :title, :description, :one_sheet_url

This line validates that there is at least some value in the fields given in the form. Its server side validation. There are lot more validations that can be checked and upon submit, if any of these data items are not present, the error is presented in a very readable format, highlighting mistakes and also the components that are missing.

There are many other validations that can be used in the model. For example

validates_length_of :title, :maximum => 10, :message => "Title too long - max 10 characters"

some good resources to learn about validations are

  1. ActiveRecord::Validations::ClassMethods API documentation #Module ActiveRecord::Validations
  2. Understanding Validations
  3. Ror Wiki - How to validate

Views in Rails

The htmls that you see on the screen are the views that are generated by scaffolding. They are pretty plain, you can jazz it up by accessing the public/stylesheets/scaffolding.css and then adding custom css rules for tags. This is a simple way of adding CSS to your pages.

Scaffolding creates two folders inside apps/views, layouts and movies. The movies/ folder contains the pages corresponding to the demo that we have seen till now. The extension .html.erb indicates html files with embedded ruby in them. Open any of the files, for example show.html.erb. This file corresponds to the page that shows a single entity. You encounter this page when you create a movie successfully or click on the show in the movies listing page. The code is pretty simple if you know HTML already and the elements in <%= %> are the ruby code just like scriptlets in jsp. The page displays the attributes of the variable @movie. The last two lines are for the edit and the back links. Edit uses the instance @movie as a parameter.

You will notice that the file lacks the basic HTML, HEAD and BODY tags. You will find these in the views/layouts folder. The <%= yield %> is where the chunks of html from the files under the /views directory go when a page is built for display. The controller matches the chunk of html from the views/ directory to the action. Listing all of the movies uses the index.html.erb file, creating a new movie uses the new.html.erb file, editing an existing movie uses the edit.html.erb file, and showing one existing movie uses the show.html.erb file.

Editing display properties If you remember, the file scaffold.css in the public/stylesheets folder was the default style sheet for the movies pages. Now to change to another style rule, add another css file in the same folder and change the stylesheet_link_tag to the new file name minus the .css, something like this

<%= stylesheet_link_tag 'movie_list' %>

You can add images to your pages by putting the images in the public/images directory and using a tag like the one below

<%= image_tag("logo.png") %>

Example of changes to the view

Lets look at a simple example about changes to the view element in rails. In your apps/layouts folder open the movies.html.erb file and make changes to it according the code given


After this create a file called exchange.css in public/stylesheets folder, take the code from below.

Code for both movies.html.erb and exchange.css is available here

Once you put the files in the right server, just start the rails server and see how your application looks now. This is the advantage with MVC, changes to the view doesn't affect the functionalities of application or the logic.

Adding Relational Entities

If you are building a database application, its almost obvious that there will be many tables that will need CRUD operations and most of them will have relationships with one another. Rails 2.0 has made this implementation really simple to use. We have to scaffold another entity and then define relationships in the model class. Here is an example :

Our movie database now needs a commenting mechanism so that users can add comments to each movie that they see. A typical E-R schema for it will reflect a one is to many relationship between movie and comments.

First we make the comments entity in rails by using scaffolding

./script/generate scaffold Comment movie:references body:text person:string

This is exactly the same definition as we did for movies, the only difference being that the movie column has the datatype references, which means it supposed to be the foreign key for this table. You may get an confirmation to overwrite scaffold.css file while running this command. If you have not made changes to the scaffold.css file, then you can type Y to continue. The db/migrate/xxx_create_comments.rb file will be created.Next migrate the database by running

rake db:migrate

If the version number of files haven't changed (and it wouldn't have, since we have not changed it), the Comments table will be created in your database. We have to then define relationships amongst these entities. Following the MVC trend, we will define these relationships at the model level. Open the apps/models/movie.rb file and add a line to its specificaton which indicates that each movie can have many comments. Your code should look like this

class Movie < ActiveRecord::Base
  #validate the data 
  validates_presence_of :title, :description, :one_sheet_url
  has_many :comments
end

Next modify the apps/models/comment.rb file and add the line to inform the model that it belongs to a movie. Also add some validation code. Your code file should look like this.

 class Comment < ActiveRecord::Base
   validates_presence_of :body, :person 
   belongs_to :movie
 end

One other enhancement to rails 2.0 is that by defining things at the model level we are making the URL's RESTful so a new movie will be referred to as http:///localhost:3000/movies/3 and the comments to it will be say http://localhost:3000/movies/3/comments/1.

Next, we have to change the config/routes.rb file to reflect these url's. Lets change that file to reflect the new addition. The code should look like this

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "movies"
  map.resources :posts, :has_many => :comments
end

We have to make the CommentsController prepared to be nested. So that’s what we are going to change: Open the apps/controllers/CommentsController.rb, modify the file to add these lines of code.

class CommentsController < ApplicationController
  before_filter :load_movie
  ...
  def load_movie
    @movie = Movie.find(params[:id])
  end
 end

This makes the @post already set for all the actions within the Comments controller. Now we have to make these changes: Before After

  1. Comment.find @post.comments.find
  2. Comment.new @post.comments.build
  3. redirect_to(@comment) redirect_to([@post, @comment])
  4. redirect_to(comments_url) redirect_to(post_comments_url(@post))

That should make the Comments controller ready. Now let’s change the 4 views at app/views/comments. If you open up either new.html.erb or edit.html.erb you will notice the following new feature:

form_for(@comment) do |f|
 ...
end

We have to change that to reflect the movie attribute. Change it as the following

form_for([@movie, @comment]) do |f|
 ...
 end

Rails will try to be smart enough to understand that this array represents a Nested Route, will check routes.rb and figure out and this is the post_comment_url(@post, @comment) named route.

When we set a Resource Route in the routes.rb. We gain these named routes:

route 	HTTP verb 	Controller Action
comments 	GET 	index
comments 	POST 	create
comment(:id) 	GET 	show
comment(:id) 	PUT 	update
comment(:id) 	DELETE 	destroy
new_comment 	GET 	new
edit_comment(:id) 	GET 	edit

By changing the routes.rb like we did, we are making the routes change to this format.

route  	HTTP verb  	URL
post_comments(@movie) 	GET 	/moviea/:movie_id/comments
post_comments(@movie) 	POST 	/movies/:movie_id/comments
post_comment(@movie, :id) 	GET 	/movies/:movie_id/comments/:id
post_comment(@movie, :id) 	PUT 	/movies/:movie_id/comments/:id
post_comment(@movie, :id) 	DELETE 	/movies/:movie_id/comments/:id
new_post_comment(@movie) 	GET 	/movies/:movie_id/comments/new
edit_post_comment(@movie, :id) 	GET 	/movies/:movie_id/comments/edit

So we can now associate each movie with a comment and access them in a RESTful way.

we have to make the Comments views to behave like they are nested within a Post. So we have to change the named routes within from the default scaffold generated code to the nested form.

References

  1. Ruby On Rails API documentation
  2. Wikipedia - Ruby on Rails
  3. - Wikipedia Ruby
  4. Ruby on Rails official website
  5. The getting started article that I referred to
  6. my delicious links on ror
  7. Rails step by step tutorial
  8. Rolling with rails 2.0 - the first full tutorial

Site Toolbox:

Personal tools
This page was last modified 07:51, 19 September 2008. - This page has been accessed 3,245 times. - Disclaimers - About WikiNayak