2010-12-04 11 views
4

Nous savons tous que si la classe cible est composée de modules, vous pouvez simplement appeler super dans un nouveau module. Mais que faire si c'est une méthode ordinaire dans une classe?Comment décorer une méthode dans Ruby sans alias_method_chain

class Logger 
    def message(msg) 
    puts msg 
    end 
end 

Dites, Logger est une classe que je ne peux pas changer (par exemple, c'est dans une gemme). Et je veux que Logger mette une ligne "================" avant chaque message. Comment est-ce que je fais cela dans un beauté façon? Héritage? Agrégation? Comment?

+1

vous pouvez ouvrir la classe ou utiliser modèle décorateur l'ol pour faire . – Gishu

Répondre

2

Je soit effectuer subclassing (par @ réponse Iain) ou une inclusion de module personnalisé dans vos propres instances:

module LoggerLiner 
    def message(*args) 
    puts "="*15 
    super 
    end 
end 

log = Logger.new(STDOUT) 
log.extend(LoggerLiner) 
+1

Probablement, le meilleur moyen si vous utilisez l'injection de dépendance dans votre code. Je vous remercie. – Benedict

2

Cela peut ne pas être ce que vous entendez par « beauté », mais sans doute la façon la plus simple est d'avoir, dans votre code quelque part:

class Logger 
    alias message old_message 
    def message(msg) 
    puts "================" 
    old_message(msg) 
    end 
end 
+2

'alias old_message message', PAS' alias message old_message' – Ernest

+1

La modification de librairies communes comme celle-ci est bien pour votre propre code, mais 'considérée comme nuisible' si votre code doit être partagé. Veuillez lire ["The Manhattan Infanticide Logger Manuever"] (http://groups.google.com/group/comp.lang.ruby/tree/browse_frm/thread/aaec68ab9088e3ef/a8df09975bca1c2f?rnum=1&q=chainsaw&_done=%2Fgroup%2Fcomp .lang.ruby% 2Fbrowse_frm% 2Fthread% 2Faaec68ab9088e3ef% 3Ftvc% 3D1% 26q% 3Dchainsaw% 26 # doc_233efa9b2261c825) [sic]. – Phrogz

+0

Ce * est * 'alias_method_chain'. Eh bien, presque. Votre méthode pollue l'espace de noms avec une méthode ugly * une * complètement inutile, alors que 'alias_method_chain' pollue l'espace de noms avec * deux * méthodes complètement inutiles. –

2

Vous pourriez hériter de l'enregistreur et utiliser l'espace de nommage pour l'utiliser:

class LinedLogger < Logger 
    def message(*) 
    puts "=" * 15 
    super 
    end 
end 

Et quand vous voulez l'utiliser:

class Post 
    Logger = LinedLogger 
end 

Seulement dans l'espace de noms Post, vous obtiendrez LinedLogger au lieu de Logger. C'est un bon moyen de limiter vos correctifs. Cela ne fonctionnera pas globalement cependant.

+0

Nice. Mais ne pensez-vous pas que c'est une façon que nous pouvons appeler 'alias_constant'? Assez la même chose que alias_method_chain – Benedict

3

Vous pouvez enregistrer la méthode originale comme un objet de la méthode non liée au lieu de l'enregistrer dans un alias .

Ensuite, vous pouvez utiliser define_method avec un bloc. Le bloc capturera l'objet_méthode non liée dans une fermeture, vous permettant de l'utiliser dans votre nouvelle méthode, sans polluer votre module/classe.

Le seul inconvénient est, vous pouvez probablement pas définir une méthode qui donne ou prend un bloc de cette façon:

module Mod 
    unbound_method = instance_method(:original_method) 
    define_method :original_method do |*args| 
    #do something before 
    #call the original method 
    unbound_method.bind(self).call(*args) 
    #do something after 
    end 
end 
+0

salut, il semble que la méthode .bind a été supprimée dans ruby ​​2.4.1, connaissez-vous l'alternative actuelle? – Ramadoka