Question

Ok, j'ai donc refactorisé mon code dans ma petite application Rails dans le but de supprimer les duplications et, en général, de me faciliter la vie (car j'aime une vie facile).Une partie de cette refactorisation a consisté à déplacer le code commun à deux de mes modèles vers un module que je peux inclure là où j'en ai besoin.

Jusqu'ici, tout va bien.On dirait que ça va marcher, mais je viens de rencontrer un problème que je ne sais pas comment contourner.Le module (que j'ai appelé sendable) sera simplement le code qui gère la télécopie, l'envoi par courrier électronique ou l'impression d'un PDF du document.Ainsi, par exemple, j'ai un bon de commande et des commandes de vente internes (abrégées de manière imaginative en ISO).

Le problème que j'ai rencontré, c'est que je veux que certaines variables soient initialisées (initialisées pour les personnes qui n'épelent pas correctement :P ) après le chargement de l'objet, j'utilise donc le après_initialiser crochet.Aucun problème...jusqu'à ce que je commence à ajouter quelques mixins supplémentaires.

Le problème que j'ai, c'est que je peux avoir un after_initialize dans l'un de mes mixins, je dois donc inclure un super appelle au début pour être sûr que l'autre mixin after_initialize les appels sont appelés.Ce qui est génial, jusqu'à ce que je finisse par appeler super et que j'obtienne une erreur car il n'y a pas de super à appeler.

Voici un petit exemple, au cas où je n'aurais pas été assez confus :

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

Donc, si chacun des mixins a un appel after_initialize, avec un super appelle, comment puis-je arrêter ça en dernier super appeler pour signaler l'erreur ?Comment puis-je tester que la super méthode existe avant de l’appeler ?

Était-ce utile?

La solution

Vous pouvez utiliser ceci :

super if defined?(super)

Voici un exemple:

class A
end

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

B.new.t

Autres conseils

As-tu essayé alias_method_chain?En gros, vous pouvez enchaîner tous vos after_initialize appels.Il agit comme un décorateur :chaque nouvelle méthode ajoute une nouvelle couche de fonctionnalités et transmet le contrôle à la méthode « remplacée » pour faire le reste.

La classe inclusive (la chose qui hérite de ActiveRecord::Base, ce qui, dans ce cas, est Iso) pourrait définir le sien after_initialize, donc toute solution autre que alias_method_chain (ou tout autre alias qui enregistre l'original) risque d'écraser le code.La solution de @Orion Edwards est la meilleure que je puisse proposer.Il y en a d'autres, mais ils le sont loin plus hackish.

alias_method_chain présente également l'avantage de créer des versions nommées de la méthode after_initialize, ce qui signifie que vous pouvez personnaliser l'ordre des appels dans les rares cas où cela est important.Sinon, vous êtes à la merci de l'ordre dans lequel la classe incluse inclut les mixins.

plus tard:

J'ai posté une question sur la liste de diffusion ruby-on-rails-core concernant la création d'implémentations vides par défaut de tous les rappels.Le processus de sauvegarde les vérifie tous de toute façon, donc je ne vois pas pourquoi ils ne devraient pas être là.Le seul inconvénient est la création de cadres de pile vides supplémentaires, mais c'est assez bon marché pour toutes les implémentations connues.

Vous pouvez simplement y ajouter un conditionnel rapide :

super if respond_to?('super')

et tout devrait bien se passer - pas d'ajout de méthodes inutiles ;Agréable et propre.

Plutôt que de vérifier si la super méthode existe, vous pouvez simplement la définir

class ActiveRecord::Base
    def after_initialize
    end
end

Cela fonctionne dans mes tests et ne devrait casser aucun de votre code existant, car toutes vos autres classes qui le définissent remplaceront de toute façon silencieusement cette méthode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top