Elentok's Blog

About me

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
      t.timestamps
    end
  end
end

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
  end

  def down
    change_column :books, :price, :float
  end
end

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 }
end

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!'
    end
  end
end

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
end

class Order < ActiveRecord::Base
  has_many :lineitem
end

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:

he:
  errors:
    messages:
      blank: אנא הכנס ערך
      equal_to: חייב להיות שווה ל-%{count}
  number:
    separator: .
    unit: $
  human:
    decimal_units:
      units:
        billion: ביליון
        thousand: אלף
    storage_units:
      units:
        byte:
          one: בייט
          other: בתים
        gb: ג'יגה-בייט
  date:
    formats:
      default: ! '%d.%m.%y'
      long: ! '%e ב%B, %Y'
      short: ! '%e %b'
  activerecord:
    models:
      attributes:
        book:
          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
else
  ... 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()
      end

      it "returns false" do
        @result.should be_false
      end

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

When running rspec the output will look like this:

Done
  #save
    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
  ...
end

3.times do
  ...
end

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,

David.

Next:Exporting (hebrew) contacts from a Motorola i876 phone