I’d like to start with a question: Have you ever seen code like this?

class User < ActiveRecord::Base
end
 
User.new.tap do |user|
  user.name     = "John Doe"
  user.username = "john.doe"
  user.password = "john123"
end

I have. But what few developers know is that many methods in Active Record already accept a block, so you don’t need to invoke tap in the first place. And that’s all because Active Record loves blocks! Let’s go through some examples.

Using blocks with Active Record

When creating an Active Record object, either by using new or create/create!, you can give a block straight to the method call instead of relying on tap:

User.new do |user|
  user.name     = "John Doe"
  user.username = "john.doe"
  user.password = "john123"
end
 
User.create do |user|
  user.name     = "John Doe"
  user.username = "john.doe"
  user.password = "john123"
end

And you can mix and match with hash initialization:

User.new(name: "John Doe") do |user|
  user.username = "john.doe"
  user.password = "john123"
end

All these methods, when receiving a block, yield the current object to the block so that you can do whatever you want with it. It’s basically the same effect as using tap. And it all happens after the attributes hash have been assigned and other internal Active Record code has been run during the object initialization, except by the after_initialize callbacks.

That’s neat. That means we can stop using tap in a few places now. But wait, there’s more.

Active Record associations also love blocks

We talked about using blocks when building an Active Record object using new or create, but associations like belongs_to or has_many also work with that, when calling build or create on them:

class User < ActiveRecord::Base
  has_many :posts
end
 
class Post < ActiveRecord::Base
  belongs_to :user
end
 
# has_many
user = User.first
user.posts.build do |post|
  post.title = "Active Record <3 Blocks"
  post.body  = "I can give tap a break! <3 <3 <3"
end
 
# belongs_to
post = Post.first
post.build_user do |user|
  user.name     = "John Doe <3 blocks"
  user.username = "john.doe"
  user.password = "john123"
end

That’s even better. That means we can stop using tap in a few more places.

Wrapping up: Active Record <3 blocks

It is possible to avoid extra work, sometimes simple stuff such as using tap with methods like new and create, other times more complicated ones, by getting to know what the framework can give us for free.

There are other places inside Active Record that accept blocks, for instance first_or_initialize and friends will execute the given block when the record is not found, to initialize the new one.

In short, next time you need a block when creating records using Active Record, take a minute to see if you can avoid using tap by using an already existing feature. Remember: Active Record <3 blocks. And don’t do that with blocks only, the main idea here is that you can learn more about the framework, and let it do more work for you.

How about you, do you have any small trick in Ruby or Rails that makes your work easier? Take a minute to share it with others in the comments. :)

Tags: , , ,

This entry was posted on Wednesday, July 18th, 2012 at 4:46 pm and is filed under English. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

  • Shemerey

    yet another good example about scoping 
     
    Developer.where(“salary = 9000″).scoping do
          Developer.where(“name = ‘Jamis’”).first
    end

  • Fabiano Vilela

    Awesome!!!

  • http://carlosantoniodasilva.wordpress.com Carlos Antonio

    Nice, thanks for sharing.

  • http://twitter.com/robb1e Robbie Clutton

    Here’s how I tested the above: http://pivotallabs.com/users/rclutton/blog/articles/2203-testing-within-an-activerecord-block

  • http://www.vinibaggio.com Vinicius Baggio Fuentes

    Cool, didn’t know that. Thanks Charles!

  • googya

    thanks for sharing! I used the tap method all the time, now can write less code! 

  • jrochkind

    would one find it mentioned in the docs anywhere that these methods take blocks? How’d you figure it out?  If I remember in the future “oh yeah, some AR methods take blocks but I forget which ones and how”, is there a way to look this up in the docs, or am I reduced to googling for your blog post?

  • http://carlosantoniodasilva.wordpress.com Carlos Antonio

    Some of these methods are documented inside the Active Record API, like here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html, but I’m afraid not all possibilites are covered – which leaves us with some source diving to figure out all of them. Of course, since you know that the basic ones are documented, similar methods should just work the same.

    Anyway, a pull request (or committing to lifo/docrails) improving the docs where necessary would be more than welcome :). Thanks!