Why I love Ruby on Rails

About four months ago I started writing a couple of projects in Ruby on Rails, and the more I used it, the more amazed I was at how easy it was and how fast I was advancing.

The purpose of this post is to share some of the things I love about Ruby on Rails.

1. Everything has its place

Whenever I worked on a web project one of the things that always annoyed me was that the directory structure never felt "right", I kept moving files around, trying to make it better, but it was never perfect.

Ruby on Rails apps have the best directory structure I have ever seen (part of the reason it's so good is because of the asset pipeline which I'll talk about later):

  • root/
    • app/
      • assets/ - app-only images, stylesheets and javascript (no 3rd party)
      • models/ - active record models
      • views/ - views (haml, erb, etc...)
      • controllers/
      • helpers/
    • config/ - all configuration files, localizations
    • db/ - db-related stuff (migrations, schema, sqlite databases)
    • doc/
    • lib/
    • log/
    • public/
    • script/ - utility scripts
    • spec/ - all tests
      • controllers/
      • factories/
      • models/
      • requests/
      • routing/
    • vendor/ - 3rd party libraries (the place for jquery plugins, twitter bootstrap, html5 boilerplate, etc...)
      • assets/
      • plugins/

When I need to add a new component I never have to think twice about where to put it, everything has its place.

To create a new rails project (with the entire directory structure) just run "rails new {app-name}".

2. Active Record Migrations

ActiveRecord is Ruby on Rails' default database layer, its best feature in my opinion is migrations.

A migration is basically a database patch, for example, a migration that adds a new table looks like this:

  class CreateBooks < ActiveRecord::Migration
    def change
      create_table :books do |t|
        t.string :title
        t.string :summary
        t.string :author
        t.float :price


The "t.timestamps" automatically adds the "last updated" and "date created" fields.

Another example of a migration is changing the type of a field:

  class ConvertPriceToInteger < ActiveRecord::Migration
    def up
      change_column :books, :price, :integer

    def down
      change_column :books, :price, :float

You can write complex migrations use active record models to convert data (instead of writing lots of SQL code).

You can take any version of the database and run the new migrations on it by just running "rake db:migrate".

As your application grows and changes, your database needs to adapt as well, and this is the best solution I've found for this problem (at least for non-NoSQL databases).

3. Active Record Validations

You can define model validations like this:

  class Book < ActiveRecord::Base
    validates_presence_of :title, :author, :price
    validates :price, numericality: { greater_than: 0 }

And when you try to save the object:

  mybook = Book.new
  mybook.save() # returns false

it will return false and you can access the validation errors via "mybook.errors".

You can also add custom validations like:

  class Book < ActiveRecord::Base
    validate :my_custom_validation

    def my_custom_validation 
      unless /^a/.match(title).nil?
        errors.add :title, \
          'books starting with "a" are not allowed!'

The best thing about this is that it connects directly with the views generated by the "rails g scaffold" command so you just define the validations in one place and the errors will be displayed in the browser.

4. Active Record Relationships

Active Record also allows a simple way to define relationships between models, for example:

  class LineItem < ActiveRecord::Base
    belongs_to :order

  class Order < ActiveRecord::Base
    has_many :lineitem

This allows you do do stuff like "lineItem.order.name" and "order.lineitems.length" (by default this links aren't loaded, they will be loaded when you try to access the "lineitems" or "order" properties or if you use the "include" option when querying the model).

5. Asset Pipeline

It is now becoming a standard to use higher level languages such as SASS and Coffeescript instead of plain CSS and Javascript.

Both languages allow writing much cleaner and readable code, and SASS adds a lot of useful features to CSS like mixins, inheritance, variables and more.

The only problem with using these languages is that they must be compiled into css and javascript for the browser to understand them. Yhat's where the asset pipeline comes in, it will automatically find all assets (stylesheets, javascripts, coffeescripts) in the app/assets and vendor/ directories and serve them to the client.

A very useful feature of this is that in development mode all of the assets are served independently (so it's easier to debug), but when switching to production mode all of the assets are combined into a single compressed file (one .js file and one .css file, but you can customize it to create packages).

The asset pipeline also takes care of caching the resources and cache-busting when uploading a new version.

So when you want to add a new sass/coffeescript file just create in the app/assets/ directory and rails will take care of the rest.

6. Localization

Rails has the best localization implementation I have ever worked with, all you need is a simple config/locales/{lang}.yml file which looks something like this:

        blank: אנא הכנס ערך
        equal_to: חייב להיות שווה ל-%{count}
      separator: .
      unit: $
          billion: ביליון
          thousand: אלף
            one: בייט
            other: בתים
          gb: ג'יגה-בייט
        default: ! '%d.%m.%y'
        long: ! '%e ב%B, %Y'
        short: ! '%e %b'
            title: שם
            summary: תקציר
            author: סופר
            price: מחיר

This will automatically be loaded by Rails when once you change the locale to "he", and will affect date/time formatting, views that use form helpers such as "f.label_for".

If you just want to get the localized version of on of these items you can do "I18n.t('activerecord.models.attributes.book.title')" and it will translate it for you.

It also supports pluralization like this:

  I18n.t('human.storage_units.byte', count: 4)

Which will return "4 בתים".

This is just brilliant, think how many times you wrote code like this:

  if (count == 1)
    ... write the singular version
    ... write the plural version

you don't need that anymore!

7. Test Driven

Ruby is the most test friendly platform I have ever used, everything can be stubbed and mocked.

When you use rails generators to create scaffolds, controllers and all the other stuff it can generate it always generates the base tests for them, so when you want to add tests all you have to do is open the relevant file and start writing.

The testing framework I like the most with ruby is RSpec, the hierarchal format of the tests makes them really easy to both write and read, for example:

  describe Book do
    describe "#save" do
      context "with no values" do
        before :each do
          @book = Book.new()
          @result = @book.save()

        it "returns false" do
          @result.should be_false

        it "has an error for the 'title' field" do
          @result.errors[:title].type.should == 'required'

When running rspec the output will look like this:

      with no values
        returns false
        has an error for the 'title' field

Very readable! very comfortable to write.

There's another testing framework for Rails called "Cucumber", it's designed for acceptance tests mostly (from what I understand) and it looks like this:

  Feature: name of the feature
  As a user
  I want this feature
  So I will have this feature

    Scenario: doing something
    Given a form
    When I click the button
    Then something happens

I use this with capybara and selenium to automate acceptance tests, and what I love about this is this:

  • It creates an executable document that can be shared with the QA team and the product owners.
  • When these tests are written before the implementation (as they should be) they offer great guidance and focus for the development process.

An annoying issue about running tests in Rails is that it takes a few seconds to load the rails framework. Fortunately, there's a tool called "Spork" that fixes this problem by running a server with a preloaded version of the rails server and forks a copy of it every time you run your tests. I've been using it for the past months and it has improved my tests from ~4 seconds to ~50 milliseconds, so I highly recommend it.

Database testing!!

When running your tests Rails connects to a testing database (can be a local sqlite db) so you can run all of sorts of database-dependant tests (unique values, etc...).

8. There's a gem for your every need

As of the time of writing this there are 35,950 gems in rubygems. I have only scraped the surface and these are some of the really cool gems I've found:

  • Paperclip - adds file attachment support to active record model (just add an ":attachment" field to your model and you're done).
  • Devise - adds user authentication support, very easy to use (took me about 5 minutes to get it working the first time I used it), very customizable (it can create its own models or attach to existing ones).
  • Enumify - adds enum supports for activerecord models.
  • Factory Girl - fixtures replacement with a straightforward definition syntax
  • LiveReload - automatically reloads your browser when files in the project directory has changed.

9. Ruby is awesome!

I just love this language! these examples are freakin' amazing:

  if date < 1.year.ago

  3.times do

And I love the fact that you don't need brackets to call a method.

10. Vim is the perfect Rails IDE

With tons of rails-related Vim plugins such as vim-rails, vim-ruby, vim-haml, vim-coffeescript and many more I have been really enjoying working on Ruby on Rails projects with Vim.

I have been using Vim for about 15 years now, and I'm amazed at how I keep learning new things about it and improving my vim-skills.

How I learned Ruby on Rails

  • I read (and practiced) the Agile Web Development with Rails book. It's also highly recommended by the ruby on rails official home page.
  • I think I learned the most about Rails by subscribing to Gary Bernhardt's Destroy All Software screencasts. All of his screencasts are about 10 minutes long, very straightforward and condensed with information and techniques. In addition to improving my RoR skills, these screencasts have given me a much better understanding of TDD.
  • Another screencasts web site for RoR is railscasts.com/, I started watching their free screencasts and I'm considering subscribing to them too.

Hope you enjoyed this post, I know I did :-) Thanks for reading,



Sean Cuevo said…
I loved your post! Tomorrow I'm going to a speak at university for a Java class to promote my startup and speak about the benefits of developing with RoR, so this was definitely very useful. I started working with RoR about half a year ago and it's great to work with, but I'm still working with 3.0. From the looks of the framework you mentioned, you seem to be on a more updated version; what are your thoughts?
David Elentok said…
Hi Sean,

Thanks for the complement :-)

I'm using Rails 3.2 and I think the biggest improvement from 3.0 is the asset pipeline (checkout this screencast).

It makes a lot of really cool stuff like these possible:

- Backbone-on-rails
- jasminrice - setting up a client-side javascript testing environment is usually an annoying task, this makes it as easy as using rspec on the server. it's freakin' awesome! it's one of most helpful gems I've used so far.

And it's not just Rails itself that offers cool stuff, in the week it's been since I wrote this post I've discovered some more awesome gems like "compass-rails", "bourbon", "zurb-foundation".
benslin kard said…
Ruby on Rails is designed to make it simpler for Ruby on Rails developers to develop applications and website

Popular posts from this blog

Restart the Windows File Sharing Service to fix weird problems

WPF, ImageSource and Embedded Resources

SharpDevelop dark color scheme