Image for post
Image for post

Using ActiveRecord’s delegate method

Let’s suppose that you have the following user class:

class User < ActiveRecord::Base
has_one :address
end

We can diplay the user’s zipcode this way:

user.address.zipcode

This doesn’t seem that bad, however, we have the following issues:

The solution for the first point is simple (but not much elegant):

user.try(:address).try(:zipcode) || 'No zipcode'

This code is still highly coupled, we can to make it better. Fortunately, Rails provides us some help with the delegate method:

class User < ActiveRecord::Base
has_one :address
delegate :zipcode, to: :address
end

Now, we can refactor our code to look like this:

user.zipcode

This is better, because are not calling directly the address to get the zipcode. However, this code will fail if the address is nil, so we change the delegate part of our class:

delegate :zipcode, to: :address, allow_nil: true

Now we can simplify our view from this:

user.try(:address).try(:zipcode) || 'No zipcode'

To this:

user.zipcode || 'No zipcode'

This is better, right?

We can make our code more readable (or if the method name is causing conflicts with other methods) by using a prefix in our zipcode:

user.address_zipcode || 'No zipcode'

This can be accomplish using the prefix option that delegates receives:

delegate :zipcode, to: :address, allow_nil: true, prefix: true

This allows the delegated method to be called with the name of the class that responds finally to the method, in this case, address. You can also use a custom name for this prefix if you want:

delegate :zipcode, to: :address, allow_nil: true, prefix: :home

So the method changes from address_zipcode to home_zipcode:

user.home_zipcode || 'No zipcode'

Finally, you should know that delegate also works with POROs (Plain Old Ruby Objects):

class MyCustomClass
def hello
'Hello world!'
end
end

And the delegate call:

delegate :hello, to: :my_custom_class, prefix: :true

As you can see, delegate is a powerful tool that Rails provides us, because by using it we reduce the coupling of our code, making the code less error prone and allowing make changes more easily.

Written by

Frontend Developer 👨‍💻 Follow me on Twitter 🐦 https://twitter.com/giovannibenussi

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store