Ok, așa că mi-am refăcut codul în aplicația mea mică Rails, în efortul de a elimina duplicarea și, în general, fă-mi viața mai ușoară (așa cum îmi place o viață ușoară). O parte a acestei refactorizări a fost aceea de a muta codul care este comun pentru două dintre modelele mele într-un modul pe care pot include acolo unde am nevoie.
Până acum, bine. Se pare că va funcționa, dar tocmai am lovit o problemă pe care nu o știu cum să ajung. Modulul (pe care l-am denumit trimis) va fi doar codul care gestionează trimiterea prin fax, e-mail sau imprimarea unui document PDF. Așadar, de exemplu, am o comandă de cumpărare și am ordine de vânzare interne (abreviată imaginativ la ISO).
Problema pe care am lovit-o este că vreau ca unele variabile inițializate (inițializate pentru persoanele care nu scriu corect: P) după încărcarea obiectului, așa că am folosit cârligul after_initialize . Nici o problemă ... până când nu voi adăuga mai multe mixuri.
Problema pe care o am este că pot avea un after_initialize
în oricare dintre mixurile mele, așa că trebuie să includ un apel super la începeți să vă asigurați că sunt apelate celelalte apeluri after_initialize
mixin. Ceea ce este minunat, până când am terminat apelul de super și am o eroare deoarece nu există nici un super de apel.
Iată un mic exemplu, în cazul în care nu am fost destul de confuz:
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
Deci, dacă fiecare dintre cele mixte are un apel after_initialize, cu un apel super , cum pot să opresc ultimul apel super de la ridicarea erorii? Cum pot testa că metoda super există înainte să o numesc?
Mai degrabă decât să verificați dacă metoda super există, puteți să o definiți
class ActiveRecord::Base
def after_initialize
end
end
Acest lucru funcționează în testarea mea și nu ar trebui să încalce niciunul din codul dvs. existent, deoarece toate clasele dvs. de altădată care o definesc vor suprasolicita oricum în mod tăcut această metodă
Ați încercat alias_method_chain
? Puteți să vă conectați toate apelurile after_initialize
. Se comportă ca un decorator: fiecare nouă metodă adaugă un nou nivel de funcționalitate și trece controlul asupra metodei "overridden" pentru a face restul.
Clasa inclus (lucru care moștenește din ActiveRecord :: Base
, care, în acest caz este Iso
) < after_initialize , astfel încât orice soluție, alta decât alias_method_chain
(sau altă alianță care salvează originalul), riscă să suprascrie codul. Soluția @Orion Edwards este cea mai bună soluție cu care pot veni. Există și alții, dar sunt mult mai hackiști.
alias_method_chain
also has the benefit of creating named versions of the after_initialize method, meaning you can customize the call order in those rare cases that it matters. Otherwise, you're at the mercy of whatever order the including class includes the mixins.
later:
Am trimis o întrebare la lista de discuții rubin-pe-șine-core despre crearea de implementări goale implicite ale tuturor apelurilor. Procesul de economisire le verifică oricum, așa că nu văd de ce nu ar trebui să fie acolo. Singurul dezavantaj este crearea de cadre suplimentare de stivă goale, dar acest lucru este destul de ieftin pentru fiecare implementare cunoscută.