34 votes

Mixins Ruby et appel de super méthodes

Ok, donc j'ai été refactoring mon code dans mon application Rails, dans un effort pour éliminer les doubles emplois, et, en général, rendre ma vie plus facile (que j'aime une vie facile). Le cadre de ce remaniement, a été de passer le code qui est commune à deux de mes modèles à un module que je peut comprendre où j'en ai besoin.

Pour l'instant, donc bon. On dirait que ça va fonctionner, mais je viens de frapper un problème que je ne suis pas sûr de la façon de se déplacer. Le module (que j'ai appelé pouvant être envoyées), va juste être le code qui gère l'envoi de télécopie, e-mailing, ou l'impression d'un fichier PDF du document. Ainsi, par exemple, j'ai un ordre d'achat, et j'ai Internes, les Ordres de Vente (imagination abrégé de l'ISO).

Le problème que j'ai frappé, c'est que je veux que certaines variables initialisées (initialisé pour les personnes qui n'ont pas l'orthographier correctement :P ) après que l'objet est chargé, donc j'ai été en utilisant le after_initialize crochet. Pas de problème... jusqu'à ce que j'ai commencer à ajouter un peu plus mixin.

Le problème que j'ai, c'est que je peux avoir un after_initialize dans une de mes mixin, j'ai donc besoin d'inclure un super appel au départ pour que l'autre mixin after_initialize des appels appelée. Ce qui est génial, jusqu'à ce que je finis par appeler super et j'obtiens une erreur car il n'est pas super à l'appel.

Voici un petit exemple, dans le cas où je n'ai pas été assez déroutant:

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 de le mixin ont un after_initialize appel, avec un super appel, comment puis-je arrêter cette dernière super appel de collecte de l'erreur? Comment puis-je tester que la super méthode existe avant que je l'appelle?

41voto

valo Points 948

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
 

3voto

newtonapple Points 2199

Avez-vous essayé alias_method_chain ? Vous pouvez essentiellement enchaîner tous vos appels after_initialize . Il agit comme un décorateur: chaque nouvelle méthode ajoute une nouvelle couche de fonctionnalités et passe le contrôle sur la méthode "surchargée" pour faire le reste.

3voto

James A. Rosen Points 25774

L', y compris la classe (le truc qui hérite de ActiveRecord::Base, ce qui, dans ce cas, est - Iso) pourrait définir sa propre after_initialize, de sorte que toute solution autre que alias_method_chain (ou d'autres aliasing qui sauve l'original) les risques d'écrasement de code. @Orion Edwards est la meilleure solution je peut venir avec. Il en existe d'autres, mais ils sont beaucoup plus hackish.

alias_method_chain a aussi l'avantage de créer des versions nommées de la after_initialize méthode, ce qui signifie que vous pouvez personnaliser l'ordre d'appel dans les rares cas que c'est important. Sinon, vous êtes à la merci de n'importe quel ordre l', y compris la classe mixin.

plus tard:

J'ai posté une question sur le ruby-on-rails-core liste de diffusion sur la création de valeur par défaut vide implémentations de tous les rappels. Le processus d'enregistrement des contrôles pour 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 d'extra pile vide images, mais c'est assez bon marché sur chaque mise en œuvre.

0voto

Orion Edwards Points 54939

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 seront de toute façon silencieuses en surpassant cette méthode

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X