Mixins do Ruby e chamar super métodos
-
08-06-2019 - |
Pergunta
Ok, então eu tenho de refatoração de código meu em meu pequeno Trilhos de app em um esforço para remover a duplicação, e, em geral, facilitar minha vida (como eu gosto de uma vida fácil).Parte desta refatoração, tem sido a de mover o código que é comum a dois dos meus modelos para um módulo que eu possa incluir onde eu preciso.
Tão longe, tão bom.Parece que vai funcionar, mas eu acabei de bater um problema que eu não tenho certeza de como dar a volta.O módulo (que eu tenho chamado de enviável), é apenas vai ser o código que trata de fax, e-mail ou imprimir um PDF do documento.Assim, por exemplo, eu tenho uma ordem de compra, e eu tenho Interno de Pedidos de Vendas (imaginação abreviado para ISO).
O problema conquistei, é que eu precisava de algumas variáveis inicializadas (inicializado para as pessoas que não soletra corretamente :P ) depois que o objeto é carregado, por isso estou usando o after_initialize gancho.Nenhum problema...até eu começar a adicionar alguns mais mixins.
O problema que eu tenho, é que eu possa ter uma after_initialize
em qualquer um dos meus mixins, então eu preciso incluir super chamada para começar, certifique-se de que os outros mixin after_initialize
chamadas de ser chamada.O que é ótimo, até eu acabar chamando de super e eu recebo um erro, porque não há super para chamar.
Aqui está um pequeno exemplo, no caso eu não tenha sido bastante confuso:
class Iso < ActiveRecord::Base
include Shared::TracksSerialNumberExtension
include Shared::OrderLines
extend Shared::Filtered
include Sendable::Model
validates_presence_of :customer
validates_associated :lines
owned_by :customer
order_lines :despatched # Mixin
tracks_serial_numbers :items # Mixin
sendable :customer # Mixin
attr_accessor :address
def initialize( params = nil )
super
self.created_at ||= Time.now.to_date
end
end
Assim, se cada um dos mixins ter um after_initialize convocação, com super chamar, como eu posso parar com isso última super chamada de elevar o erro?Como posso testar o super existe um método antes de eu chamá-lo?
Solução
Você pode usar esse:
super if defined?(super)
Aqui está um exemplo:
class A
end
class B < A
def t
super if defined?(super)
puts "Hi from B"
end
end
B.new.t
Outras dicas
Você já tentou alias_method_chain
?Basicamente você pode acorrentado todos os seus after_initialize
chamadas.Ele age como um decorador:cada novo método adiciona uma nova camada de funcionalidade e passa o controle para o "substituído" método para fazer o resto.
Incluindo a classe (a única coisa que se herda ActiveRecord::Base
, que, neste caso, é Iso
) poderia definir o seu próprio after_initialize
, assim , qualquer solução diferente alias_method_chain
(ou outros aliasing que guarda o original) riscos de substituição de código.@Orion Edwards solução é a melhor que eu pode vir acima com.Existem outros, mas eles são longe mais hackish.
alias_method_chain
também tem a vantagem de criar o nome versões do after_initialize método, o que significa que você pode personalizar a ordem de chamada nos raros casos em que é importante.Caso contrário, você está à mercê de qualquer ordem, incluindo a classe inclui os mixins.
mais tarde:
Eu postei uma pergunta para o ruby-on-rails-core lista de discussão sobre a criação de padrão vazio implementações de todas as chamadas de retorno.O processo de salvamento verifica-los todos de qualquer maneira, então eu não vejo por que não deveria estar lá.A única desvantagem é a criação de extra pilha vazia quadros, mas que é muito baixo em todos os conhecidos de implementação.
Você pode apenas jogar uma rápida condicional lá:
super if respond_to?('super')
e você deve ser fino - sem adição de métodos inúteis;agradável e limpo.
Em vez de verificar se o super existe um método, você pode apenas definir
class ActiveRecord::Base
def after_initialize
end
end
Isso funciona no meu teste, e não deve quebrar qualquer código existente, porque todas as suas outras classes que definem será apenas silenciosamente substituindo este método de qualquer maneira