Delegation is a quite common practice in Ruby projects, if you consider proxies, mixins and composition as the ingredient of the Delegation Pattern.
The Delegation Design Pattern is a technique where an object exposes certain behavior but it actually delegates responsibility for implementing that behavior to an associated object.
When using Rails and the cool associations feature, it can be tempting to chain method calls like this:
product.provider.name
provider.address.city
company.building.city
These lines violate the Law of Demeter. To fix it, we should change them to be :
product.provider_name
provider.address_city #or provider.city
company.city
This way, it’s much cleaner and we don’t break the law anymore. But to do that, we need to add a bunch of methods to our models :
class Product < ActiveRecord::Base
belongs_to :provider
def provider_name
provider.name
end
# More methods to access the provider's attributes
end
class Provider < ActiveRecord::Base
has_many :products
# Has a name attribute
end
You get the idea ! The models will grow out of control if we define methods for each attribute like this. But Rails has a solution : Delegate.
class Product < ActiveRecord::Base
belongs_to :provider
delegate :name, to: :provider, prefix: true
end
class Provider < ActiveRecord::Base
has_many :products
# Has a name attribute
end
Note the use of the
prefix
key. It allows us to use product.provider_name instead of just product.name since a product would probably have its own name.
Delegate is a really powerful feature that let you clean up your models. Let’s see another example.
If we have a Company that belongs to a Building, we could do :
class Company < ActiveRecord::Base
belongs_to :building
delegate :street, :city, :country, to: :building, allow_nil: true
end
class Building < ActiveRecord::Base
has_many :companies
attr_accessible :street, :city, :country
end
Now, we don’t break the law of Demeter anymore since we can access the street directly from the company object. Plus, less BS inside our models Note how we added
allow_nil
to the delegate. With this little addition, if the company does not have any building associated, we will just get nil
when calling street
and no exception!
If you never used the delegate method, it’s time to add it to your toolbox !
You can get more information about delegate in the documentation.