Tales of a GeekTrotter Binary Logbook

8Jan/110

Ruby on Rails: the best web programming framework?

I’ve been programming for some websites mainly in PHP/MySQL for many years now and decided to have a look to what is new in this field.

And there is a very promising one that should have lights on it: Ruby on Rails (or simply “RoR”).
Let’s have an overview on RoR in this post.

Introduction

First, if you’re new to Ruby, don’t worry!
Ruby has been made to be pleasant and easy to program with.
I advise you to read the very nice Ruby introduction Ruby in 20 minutes (available in many languages).

In Ruby, the main thing to remind is that everything is object. Even an integer!
So you can have methods and attributes for all your variables (through the classes).

In Rails, we have a MVC architecture, which means we separate the Model, the Controller and the View.
When it goes to web programming, the model can for example define a database table, the view defines what is shown to the visitors (html-based) and the controller is the “glue” between the models and the views.
The obvious advantage of doing so it that your code is not messed up. You don’t have SQL statements messed up with HTML rendering and so on.
It is far easier to maintain than some PHP programming!

But after using Rails for a while, the real reason why I don't want to go back to PHP is the simplicity of Rails to deal with databases through its "ActiveRecord" class.
We can create, use and update a database without entering a single SQL statement!
Rails is mapping Ruby Classes to Database Tables.

Power of Rails through an example

Retrieving a table

So let say I have a ruby class "Product" that belongs to ActiveRecord.
This model is mapped to a table called "products" in the database. And the columns of the table are attributes in the model.
So if I have a table "products", I can get its content with a single instruction exempt of any ugly SQL code:

products = Product.all

That's it! This short code is enough to already appreciate the power of RoR.
With a single line (and it is true that there is nothing else, no definition of Article or whatever before, this can be the first line of a script) we connected to our database and got the content of the table "products".
Because Rails is smart and knows that "Product" refers to the table "products" in the database.
Rails is even smart enough to know that the plural of "category" is not "categorys" but "categories"!
So if we had a table "categories" in our database, to get all the categories it would be:

categories = Category.all

Retrieving only one line of the table

Well, getting all the content of a table is good.
But we can also get the last article of the table "products" easily:

lastProduct = Product.last

And that's it? Yes it is! Why should it be harder though?
Thanks to Rails we don't have to "SELECT * FROM products ORDER BY date DESC LIMIT 1" or something similar ;).

Accessing the elements of this line

And to access to the content of the last article, we just need to access the attributes.
Let say the table "products" has columns "name" (string), "description" (text) and "price" (int).
We can access these values simply by calling the attributes of the class:

lastProduct.name
lastProduct.description
lastProduct.price

Validation

Validation is also well implemented in Rails and is a strength of this framework.
You can validate forms entered by users very easily.
For example if you want the rate to be between 1 and 5, you just need to add the following code in the Comment model...

validates_inclusion_of :rate, :in => 1..5,
                              :message => "rate has to be between 1 and 5"

...and you will never record a rate which is not between 1 and 5 in the database!
Besides, the visitor who enters by mistake a rate which is not within this range will be warned by the previously specified message.

References to another table

Cool! And what if a column is a reference to another table.
For example let say we can have comments about the product, and the comments of the product are stored in another table called "comments" that has three fields: "product_id" (integer) which is a reference to the product the comment belongs to, "content" (text) which is the comment about the product and a "rate" (int) between 1 and 5 they give to the product.

So the products can have comments and each comment belongs to a product, right?
Well, this relation is very easy to set up with Rails. We need to add one single line in both models (the products model and the comments model).

In the products model, we said a product can have many comments.
Well, Rails talks pretty well English and understands us:

has_many :comments

In the comments model, we said a comment belongs to a product:

belongs_to :products

And that's it! The relation is done!
Let say we want to get all the comments of the first product...
If we were working with PHP/MySQL it would have required a few minutes to write the code for it.
With Ruby, it's a few seconds and it's pretty:

comments = Product.first.comments

And it's done!
As we defined the relation between the comments and the products (adding two lines to the models!), this single line retrieves the comments of the first product.

More complex relation with other tables

This feature is even more powerful when the relation is more complex.
Let say the comments are written by users that are stored in another table "users".
We want to get the name of the user that wrote the last comment on the first product... (yeah, why not? :p)
We assume there is another column "user" in the table "comments" which is a reference to a user in the table "users", and that this table "users" contains, among others, a column "name" with the user name.

firstProduct = Product.first
lastComment = firstProduct.comments.last
userName = lastComment.user.name
# can be done in 1 call:
userName = Product.first.comments.last.user.name

Not even one SQL statement. No "JOIN comments USING..." or similar.
Only attribute calls.

Search and Find in a table

OK but how can we find the user whose name is "Thomas"?
Easy!

thomas = User.where(:name => "Thomas")

No SQL again (but same as "WHERE name = 'Thomas'").

You will actually have to enter a very small SQL statement when you want to search something.
For example let say we want to retrieve all the products whose description contains "something very interesting".
The code is as follows:

searchTerm = "something very interesting"
results = Product.where("description LIKE '%?%'", searchTerm)

The question mark in the LIKE statement is replaced by "searchTerm".
This is almost the only time we need to enter some SQL code, and it is not a full SQL code anyway, just a portion of code 😉

Additional Information

I think you saw that with Rails, you can build complete, usable and maintainable websites in a few minutes (or hours).
The best way to see it by yourself is to follow the Getting Started with Rails Guide.

Why isn't Rails the web framework #1 then??
Because still a very few web hosts provide good Rails hosting for low cost... and most of the web hosts simply do NOT support Rails...
I even myself cannot use Rails as much as I would like to (except on localhost... which is a bit frustrating sometimes!).

This is sad but I hope it will change soon so Rails will get the success it deserves!

I'll write an article about web2py, another framework inspired by Rails which is similar (but not as good as Rails IMHO) but has the advantage of being easier to deploy and accepted by more web hosts.