Sobrescrevendo method_missing em Ruby on Rails

Tutoriais - 25/Jun/2020 - por André Kanamura

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.

Referências

Foto de perfil do autor
André Kanamura

Dev na Campus Code