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
.