Fork me on GitHub

Mohammad AbuShady Software Developer

Law of demeter and rails delegation

Defining law of demeter

The law of demeter simply states that objects should only talk to them selves or objects directly associated to them selves, because otherwise this is considered a dependency and would reduce maintainability and cause tight coupling between objects. This rule can often be ignored if the objects are of the same type, but more about that later.

Here’s some violations of the law of demeter:

And here’s some methods that don’t violate the law

To simplify the rule we might state it as

Don’t use more than a single dot in methods calls

Violating the law of demeter in rails

First lets take a look at a few places where we could find violations in rails, first and easiest place to find this violation is in rails, let’s take this example snippet from a view, assuming this is an order object in a cart system, such code could also be found in a view, model or controller, it’s very common

Such code works because rails internatlly queries for the associated object and then sends the corresponding method call to the new queried object, this could go wrong very easily, assuming for some reason we moved the invoice number to a different object, or changed its name, we will need to find every single usage of this method and replace it in every view, which is a very bad maintainability issue.

Adding wrappers

To fix someone might think ok we’ll wrap these methods in the model so then they’ll be centralized and can be maintained easier and in a single place, here’s the modifined order model

And here’s the updated view

This sure does a lot better than how we started, if any thing changes in the models, the public API of the object stays the same, and the internal method gets changed, but this intrudces a new kind of problem, the size of the file, if we keep going like this, we’ll end up with one huge model, consisting mostly of tiny wrapper functions to handle these small things, luckily rails gave us a way to improve this one step further.

#delegate

In rails models you can specify certain messages that are delegated to other objects using the delegate keyword, you could say it’s like specifying a handler to handle this message, here’s how we delegate the methods form our past examples:

Just by defining those two lines, our order instance now can respond to those messages, and delegate them to the :item object and :invoice object.

But for them to work we need to implement those messages

Well we separated the concern, but we still have an implementation issue, we just moved the methods from one place to another, to solve this we can check the :prefix option.

The prefix option allows us to specify a prefix that will be used during the method call, but not passed during the delgation, if prefix is set to true then the prefix will be set as the name of the object we’re delegating to,
so now by setting prefix: :true we can say item_name but the item will only recieve name, this should solve our problems

If the associated object is nil, rails will raise an error because there’s no object to recieve the message and handle it, for this we can specify an extra parameter called allow_nil this message will tell rails to return nil if the associated object is nil, instead of raising an error, this way we don’t need to handle special cases when the object is not initialized or not existing.

So here’s the final model version

and here’s the final view