Depois de programar algum tempo em Ruby, me senti muito incomodado em ter que repetir um determinado código para manter minha estrutura respeitando a Law of Demeter. Pra quem não está familiarizado, segue um simples exemplo em Rails:

[sourcecode language="ruby"]
#models
class Post < ActiveRecord::Base
has_many :comments
end

class Comment < ActiveRecord::Base
belongs_to :post
end

#view - erb|haml
@comment.post.title
@comment.post.name
@comment.post.something_else
[/sourcecode]

O exemplo é um pouco forçado, mas o problema claro do exemplo é que estamos conhecendo demais sobre o objeto post dentro de comment. Se for necessário alguma alteração em algum dos atributos que estamos acessando diretamente, possivelmente isso resultará em modificações em cascata em todo código.

Depois dessa explicação básica para quem ainda não conhecia a Law of Demeter, vamos aplicar algumas soluções:

Segunda tentativa:
[sourcecode language="ruby"]
#models
class Post < ActiveRecord::Base
has_many :comments
end

class Comment < ActiveRecord::Base
belongs_to :post
def post_title
post ? post.title : nil #preciso verificar se é nulo, caso contrário terei problemas
end
def post_name
post ? post.name : nil #preciso verificar se é nulo, caso contrário terei problemas
end
def post_something_else
post ? post.something_else : nil #preciso verificar se é nulo, caso contrário terei problemas
end
end

#view - erb|haml
@comment.post_title
@comment.post_name
@comment.post_something_else
[/sourcecode]

Essa mudança resolve o problema. Acontece que isso acaba sendo um pattern para resolver o problema, portanto, precisamos encontrar uma forma de não ficar repetindo esse código.

Quem já leu o livro The Pragmatic Programmer, tem bem na memória o capítulo que apresenta o conceito DRY - D'ont Repeat Yourself. Quem programa em Ruby e principalmente já usou o framework Rails sabe bem que DRY é um dos chavões que estão imbutidos na propaganda. Vamos então tentar fazer mais algumas modificações pra tentar alcançar esse objetivo:

Terceira tentativa:
[sourcecode language="ruby"]
#models
class Post < ActiveRecord::Base
has_many :comments
end

require 'forwardable'
class Comment < ActiveRecord::Base
extend Forwardable
belongs_to :post
def_delegator :post, :name, :post_name
def_delegator :post, :title, :post_title
def_delegator :post, :something_else, :post_something_else
end

#view - erb|haml
@comment.post_title
@comment.post_name
@comment.post_something_else
[/sourcecode]
O módulo Forwardable já vem com o Ruby. Portanto, a solução mais obvia foi usar esse módulo para melhorar o exemplo anterior. Apesar de escrever menos código, essa alternativa tem o inconveniente de não verificar se o objeto post é nil, causando assim NoMethodError em alguns casos. Sendo assim, a alternativa anterior ainda parece ser mais adequada. Porém, a duplicação de código ainda me incomodava bastante, portanto, resolvi montar uma solução única que deu origem a gem demeter.

A solução definitiva:
[sourcecode language="ruby"]
#no shell
> sudo gem update --system
> sudo gem sources -a http://gemcutter.org
> sudo gem install demeter

#models
class Post < ActiveRecord::Base
has_many :comments
end

class Comment < ActiveRecord::Base
extend Demeter #extends demeter module
demeter :post #demeter post object
belongs_to :post
end

#view
@comment.post_title
@comment.post_name
@comment.post_something_else
[/sourcecode]
Basicamente o problema foi resolvido com 2 linhas de código:
[sourcecode language="ruby"]
extend Demeter
demeter :post
[/sourcecode]
A vantagens são visíveis porque (1) você escreve bem menos, (2) já existe a verificação de objetos nulos e (3) caso você queira sobrescrever o comportamento padrão, basta criar um método que responda a mesma mensagem que a gem demeter responde. Dessa forma, o método criado pelo programador semrpe terá prioridade.

O código fonte do projeto está em http://github.com/emerleite/demeter com todas as instruções para utilização tanto em Ruby quanto em Ruby on Rails. O código fonte tem todos os testes automatizados que cobrem diversos cenários. O resultado desses testes podem ser vistos em http://runcoderun.com/emerleite/demeter. A página da gem fica em http://gemcutter.org/gems/demeter

Aguardo o feedback de vocês :)