CilaCila A blogging framework for hackers.

Follow the Law of Demeter

| Comments

Follow the law of Demeter

class Address < ActiveRecord::Base 
    belongs_to :customer    end
class Customer < ActiveRecord::Base         has_one :address        has_many :invoices  end
class Invoice < ActiveRecord::Base 
    belongs_to :customer    end
# Views

<%= @invoice.customer.name %>   <%= @invoice.customer.address.street %> 
<%= @invoice.customer.address.city %>
<%= @invoice.customer.address.state %>
<%= @invoice.customer.address.zip_code %>

Law of Demeter: lays out the concept that an object can call methods on a related object but that it should not reach through that object to call a method on a third. In Rails, this could be summed up as “use only one dot.”

So we can write a method :

class Customer < ActiveRecord::Base 
    has_one :address        has_many :invoices      def street          address.street      end     def city 
        address.city        end     def state           address.state       end
end

<%= @invoice.customer_name %>
<%= @invoice.customer_street %>

In addition, your public interface on Invoice has been polluted by methods that arguably have nothing to do with the rest of your interface for invoices.

Solution

Fortunately, Ruby on Rails includes a function that addresses the first concern. This method is the class-level delegate method. This method provides a shortcut for indicating that one or more methods that will be created on your object are actually provided by a related object. Using this delegate method, you can rewrite your exam- ple like this:

class Customer < ActiveRecord::Base 
    has_one :address        has_many :invoices              delegate :street, :city, :state, :zip_code, :to => :address
end
class Invoice < ActiveRecord::Base      belongs_to :customer        delegate :name,              :street,                :to => :customer,               :prefix => true
end

Comments