No artigo Presenters e Decorators em Ruby mostramos como podemos utilizar o SimpleDelegator para delegar métodos na implementação de Presenters e Decorators. Aqui, vamos mostrar como é possível obter resultados semelhantes sobrescrevendo o método method_missing do Ruby. Recomendamos a leitura do artigo anterior caso queira entender melhor como funcionam os design patterns Presenter e Decorator. Caso você já tenha compreensão destes conceitos, pode continuar lendo!
Para relembrar o contexto do exemplo descrito, temos duas classes, Rental, que representa uma locação de um carro, e RentalPresenter, que vai preparar informações de Rental para visualização nas views:
class Rental
attr_accessor :status, :customer, :price
def initialize(status, customer, price)
@status = status
@customer = customer
@price = price
end
end
class RentalPresenter
attr_reader :rental
def initialize(rental)
@rental = rental
end
def status
"A locação está com status: #{@rental.status}"
end
end
Dadas as classes poderíamos rodar:
rental = Rental.new("Ativa", "Luiz", 45)
rental_presenter = RentalPresenter.new(rental)
puts rental_presenter.status
# => A locação está com status: Ativa
Para completar o design pattern, ainda precisamos fazer com que RentalPresenter responda a todos os métodos aos quais Rental pode responder. Para isso podemos utilizar diferentes estratégias, como Herança ou o SimpleDelegator (com foi descrito no artigo anterior). Vamos mostrar aqui como resolver esse problema sobrescrevendo method_missing na classe RentalPresenter:
class RentalPresenter
attr_reader :rental
def initialize(rental)
@rental = rental
end
def status
"A locação está com status: #{@rental.status}"
end
def method_missing(method_name, *args, &block)
@rental.public_send(method_name, *args, &block)
end
def respond_to_missing?(method_name, include_private = false)
@rental.respond_to?(method_name, include_private)
end
end
O method_missing é um método em Ruby que intercepta chamadas de métodos que não existem no contexto da classe e nos permite implementar o código para lidar com essa chamada. Dessa forma podemos controlar como a classe vai se comportar quando forem chamados métodos que não estão implementados nela. Neste caso, queremos que as chamadas sejam delegadas para @rental e fazemos isso usando o método public_send. Esse tipo de implementação é suscetível a erros, por isso é importante também implementar o método respond_to_missing?, como explicado neste artigo da thoughtbot.
Vale lembrar que sobrescrever o method_missing altera somente o comportamento do método na classe RentalPresenter, assim o método original continua funcionando normalmente e métodos que também não forem encontrados em Rental ainda disparam a mensagem de erro NoMethodError.