Amestecuri de Ruby și metode de apel super

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?

0
fr hi bn

4 răspunsuri

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ă

0
adăugat
@yaauie - sigur, aș fi putut pune un raise "oh no" dacă methods.include? (: after_initialize) înainte de monkeypatch, dar asta ar face mai greu să înțeleagă acest exemplu ... Este atât de ușor pentru a vă prinde în detaliu toate cazurile de margine că lecția actuală aici (doar patch-uri o metodă de bază în) s-ar pierde în zgomot.
adăugat autor Orion Edwards, sursa
Coborâtă pentru că nu este o soluție generală. Ideea este că nu știți dacă există sau nu super-ul, astfel încât activarea activă a ActiveRecord :: Base # after_initialize în existență, creați un punct în care codul dvs. se va rupe dacă/când ActiveRecord adaugă o Base # after_initialize sau arity este schimbat ; este departe de a fi numit condiționat dacă este definită.
adăugat autor yaauie, sursa
monkeypatching nu este o soluție generală și nu ar trebui? în general ? Fii incurajat. Un răspuns care implică o monedă de tip single-off pe care se poate întâmpla să funcționeze duce la un cod inutil de complex și fragil, mai ales dacă nu aveți control până la nivelul lanțului de moștenire.
adăugat autor yaauie, sursa

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ă.

0
adăugat

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.

0
adăugat

Puteți folosi acest lucru:

super if defined?(super)

Iată un exemplu:

class A
end

class B < A
  def t
    super if defined?(super)
    puts "Hi from B"
  end
end

B.new.t
0
adăugat