How to pass multiple attributes to find_or_create_by in Rails 3 : the Ruby way

March 23, 2012 · Posted in Development 

Ruby and Rails have many magic features like blocks/closures, duck typing/mix-ins/modules,  method_missing, poetry mode.

One of the common tasks in programming web applications on Rails is finding a row in a table by several columns and creating a new row in case of row is missing.

Find by one attribute

This will find a row by ‘name’ column in table ‘Products’ and will create a new row if not found.

Product.find_or_create_by_name('name here')

 

 

If you want a new row to be created with many other attributes then you can pass them as a hash:

Product.find_or_create_by_name('namehere', :category_id=>1,  :enabled=>true, :in_stock=>0, :price=>100)

 

Find by multiple attributes

If you want to find by multiple attributes you can use model’s methods like find_or_create_by_field1_and_field2(field1_value, field2_value).

Product.find_or_create_by_name_and_category_id('namehere', 10)

 

If you want to pass multiple attributes to a new row to be created then you can use the following syntax:

Product.find_or_create_by_name_and_category_id('namehere', 10) do |r|
r.price= 100
r.available = true
r.enabled=1
r.in_stock=0
end

Notice that you cannot pass extra parameters to Product.find_or_create_by_name_and_category_id method because it expects only two parameters. Thanks Rails this method can be passed a block with your code and this code will be invoked for the new row to be initialized.

 

Without all that Ruby’s magic the task could be achieved with the following code:

attr = { :enabled=>true, :in_stock=>0, :price=>100}
row = Product.find_by_name_and_category_id('namehere', 10) || Product.create(attr)

 

You must agree that this code looks not so elegant and concise.
Details are here on Github

Comments