Are ActiveRecord validations worth anything?

ActiveRecord, which is core component of Rails framework (at least before Rails 3.0 become reality) provides a lot features which developers do love.

Validations are one of those features. They are methods which provide easy way to check if model is valid and protect consistency our data in database. Sounds good, but this is bullshit.

What AR really is ;) (c) CC <a href='http://www.flickr.com/photos/sekimura/'>sekimura</a>” title=”bullshit” width=”500″ height=”375″ class=”size-full wp-image-462″ /><p id=What AR really is ;) (c) CC sekimura

Active Record validations are prone to race conditions. Period. It does not make any sense to rely on them if You really have to have consistent data (I’m referring to unique constraint and validates_uniqueness_of). The only way to go is to have constraints put on database level. Or write a lot workaround code in Rails. Error prone as well.

What is race condition? Race condition (or race hazard) is when outcome of some operation depends on timing between other operations.

Let’s take for example creation of two records where one attribute should be unique. How does work validates_uniqueness_of?

First it checks in DB (via SELECT) if there already is record with such value as unique attribute. If there is no such record then it run INSERT command to create new record.

Now imagine two processes are trying to create such record in the same time. Since SELECT and INSERT are separate operation it is quite possible (remember we have two processes trying to do the same thing at once):

  1. Model.save in PROCESS 1
  2. Model.save in PROCESS 2
  3. SELECT FROM PROCESS 1 (result – no record in DB)
  4. SELECT FROM PROCESS 2 (result – no record in DB)
  5. INSERT FROM PROCESS 1
  6. INSERT FROM PROCESS 1

Now guess how many records will be created? :))

What is takeaway from this rant?

ActiveRecord brings to table a lot improvements which each developer loves, but there is no silver bullet. Such race condition can happen (unless You run one Rails process in non-threading mode, but this is not very useful setup :D) even on low traffic application.

If there is really some business need which requires You to have unique data You have to implement some constraints on database level.

Use AR, since it is wonderful tool, but when used properly. Or maybe – know shortcomings of tools You do use.

Paperclip, passenger and “not recognized by the ‘identify’ command”

If You do use awesome :) Paperclip library, and on application served by Passenger You get errors like:

Avatar /tmp/stream.1170.0 is not recognized by the 'identify' command.

most probably Passenger does not have setup environment and is missing a path. On FreeBSD identify is placed in /usr/local/bin and AFAIR this path is not included by default in PATH. As result Passenger can not find this utility.

You can try to setup environment for Passenger (Apache?) or just add in appropriate environment (in my case it was production.rb):

Paperclip.options[:command_path] = "/usr/local/bin"

Design patterns or SOLID design

I’m using Rails and Ruby over 3 years now, but with each month I know there is a lot new things to learn. Recently I’m trying to learn much more about design patterns, and how to use them.

If You are new to design patterns and want to know why it is worth to learn this stuff I recommend You watching this session from this year’s GORUCO (GOtham RUby COnference):

Associations and to_json on collections of objects

ActiveRecord’s Serialization module does provide to_json method. It allows include associations when dumping to JSON, but it works only for single objects. If You want to run it against collection, returned by find :all You will get:

>> Model.all.to_json :include =>:association
TypeError: wrong argument type Hash (expected Data)
	from (irb):13:in `to_json'
	from (irb):13

If You want to get in JSON collection with some associations You have to do it manually:

    @companies = Company.all, :include => :address
    render :text => "[ %s ]" % @companies.collect{ |c|
      c.to_json(:include => :address)
    }.join(","),
      :content_type => 'application/json'

Test benchmark – useful gem for Rails tests

Doing TDD You need to know which tests are making whole suite slower. If time used by rake test becomes too long, You may be tempted to skip running tests… Waiting for them to complete becomes suddenly burden on Your way to next task.

Waiting... Waiting...

Waiting... Waiting... (c) Tony the Misfit

So, when You need to optimize Your test suite, test_benchmark is a way to go. Just install Rails plugin and after rake test You will get 10 slowest tests and timing of all test in test.log. Now You know where to look for a good place to start and not to wait too long…