Ruby mixins e di chiamare super metodi
-
08-06-2019 - |
Domanda
Ok, così ho potuto refactoring del mio codice nel mio piccolo Rails, nel tentativo di rimuovere la duplicazione, e in generale rendere la vita più facile (come mi piace una vita facile).Parte di questo refactoring, è stato quello di spostare il codice che è comune a due dei miei modelli per un modulo che posso comprendere, se ne ho bisogno.
Così lontano, così buono.Guarda come sta andando a lavorare fuori, ma ho appena colpito un problema che non so come risolvere.Il modulo (che ho chiamato sendable), è solo andare a essere il codice che gestisce l'invio di fax, e-mail, o la stampa di un PDF del documento.Così, per esempio, ho un ordine di acquisto, e devo Interno di Ordini di Vendita (con la fantasia abbreviato ISO).
Il problema l'ho colpito, non è che voglio alcune variabili inizializzate (inizializzato per le persone che non magia correttamente :P ) dopo che l'oggetto è stato caricato, ho usato il after_initialize hook.Nessun problema...fino a quando ho iniziare ad aggiungere alcuni più mixins.
Il problema che ho, è che posso avere un after_initialize
in uno dei miei mixins, quindi ho bisogno di includere un super chiamata all'inizio per assicurarsi che le altre mixin after_initialize
chiamate chiamato.Che è grande, fino a quando alla fine l'ho chiamata super e ho un errore perché non c'è nessun super chiamare.
Ecco un piccolo esempio, nel caso in cui non sono stato abbastanza 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
Così, se ogni uno dei mixins hanno un after_initialize chiamata, con un super chiamata, come posso impedire che lo scorso super chiamata da generare l'errore?Come si può verificare che il super esiste un metodo prima di chiamare?
Soluzione
È possibile utilizzare questo:
super if defined?(super)
Ecco un esempio:
class A
end
class B < A
def t
super if defined?(super)
puts "Hi from B"
end
end
B.new.t
Altri suggerimenti
Hai provato alias_method_chain
?Si può sostanzialmente incatenati tutti i tuoi after_initialize
le chiamate.Esso agisce come un decoratore:ogni nuovo metodo aggiunge un nuovo livello di funzionalità e passa il controllo sul "override" metodo per fare il resto.
L'compresa la classe (la cosa che eredita da ActiveRecord::Base
, che, in questo caso è Iso
) potrebbe definire il proprio after_initialize
, in modo che qualsiasi soluzione diversa alias_method_chain
(o altri aliasing che salva originale) rischi di sovrascrivere il codice.@Orion Edwards soluzione è il meglio che posso venire con.Ci sono altri, ma sono lontano più hackish.
alias_method_chain
ha anche il vantaggio di creare versioni con nomi di after_initialize metodo, significa che è possibile personalizzare l'ordine di chiamata in quei rari casi che conta.In caso contrario, sei in balia di qualunque ordine e il classe include i mixins.
più tardi:
Ho postato una domanda per il ruby-on-rails-core mailing list sulla creazione di vuoto di default implementazioni di tutte le richiamate.Il processo di salvataggio dei controlli per tutti, comunque, quindi non vedo perché non dovrebbero essere lì.L'unico aspetto negativo è la creazione di extra vuoto stack frame, ma questo è abbastanza a buon mercato su ogni nota implementazione.
Si può solo gettare un rapido condizionale in là:
super if respond_to?('super')
e non dovrebbero esserci problemi, senza aggiunta di inutili metodi;bella e pulita.
Invece di controllare se il super metodo esiste, si può solo definire
class ActiveRecord::Base
def after_initialize
end
end
Questo funziona nel mio test, e non rompono le tue codice esistente, perché tutte le altre classi che definiscono sarà solo silenzio l'override di questo metodo comunque