2017-09-08 1 views
0

Je suis en train de mettre en œuvre une hooker pour les méthodes de classe, quelque chose comme before_action et after_action. Le problème est si je définir une méthode l'intérieur du module, avec define_method ou normalement avec def do_before; puts 'do_before called'; end à l'intérieur method_added obtient non défini. Alors, comment puis-je appeler un module method intérieur method_added?méthode d'auto module appelé Undefined method_added

module Hooker 

    [:before, :after].each do |element| 
    define_method("#{element}_action") do |name| 
     puts "#{element}_action called with parameter #{name}" 
    end 
    define_method("do_#{element}") do 
     puts "do_#{element} called" 
    end 
    end 
    def method_added(name) 
    return if @filtering # Don't add to original_ methods 
    @filtering = true 

    alias_method :"original_#{name}", name 
    define_method name do |*args| 
     do_before # undefined method `do_before' for #<Bar:0x007ff2f183c318> 
     self.send("original_#{name}", *args) 
     do_after # undefined method `do_after' for #<Bar:0x007ff2f183c318> 
    end 

    @filtering = false 
    end 
end 

class Bar 
    extend Hooker 

    before_action 'foo2' 
    after_action 'bar2' 

    def my_func 
    puts 'MyFunc called' 
    end 
end 

Bar.new.my_func 

Répondre

1

C'est parce que vous utilisez - extend

ajoute les méthodes et les constantes du module spécifié métaclasse

de la cible, mais vous devez également - include

il se mélange dans les méthodes de module spécifié comme méthodes d'instance dans la classe cible

# some code goes here 

class Bar 
    extend Hooker 
    include Hooker 

    before_action 'foo2' 
    after_action 'bar2' 

    def my_func 
    puts 'MyFunc called' 
    end 
end 

Bar.new.my_func 
=> before_action called with parameter foo2 
=> after_action called with parameter bar2 
=> do_before called 
=> MyFunc called 
=> do_after called 

Beaucoup plus clairement de séparer ce à différents modules.

+0

fonctionne parfaitement, mais est-il possible/astuce pour ne pas avoir à faire les 2 étapes dans la classe? Peut-être utiliser self.extended ou hérité? –

+0

Je ne sais pas, mais jetez un coup d'oeil sur [Module # précédez] (http://ruby-doc.org/core-2.0.0/Module.html#method-i-prepend), Un grand avantage de ce pouvoir est pour remplacer les méthodes d'une manière plus sûre, plutôt que d'utiliser un alias ou des astuces comme alias_method_chain. codage heureux! –

+0

Ne pas tirer ... Essayé avec ce 'def self.extended (base); base.send: include, self; fin »mais ne fonctionne toujours pas. –

0

est ici une solution de rechange, dans laquelle vous n'avez pas besoin d'appeler explicitement dans la classe à la fois extend et include:

module Hooker 
    def self.included(klass) 
    klass.extend ClassMethods 
    end 

    module ClassMethods 
    [:before, :after].each do |element| 
     define_method("#{element}_action") do |name| 
     puts "#{element}_action called with parameter #{name}" 
     end 
     define_method("do_#{element}") do 
     puts "do_#{element} called" 
     end 
    end 
    end 

    def method_added(name) 
    return if @filtering # Don't add to original_ methods 
    @filtering = true 

    alias_method :"original_#{name}", name 
    define_method name do |*args| 
     do_before 
     self.send("original_#{name}", *args) 
     do_after 
    end 

    @filtering = false 
    end 
end 

class Bar 
    include Hooker 

    before_action 'foo2' 
    after_action 'bar2' 

    def my_func 
    puts 'MyFunc called' 
    end 
end 

Bar.new.my_func 

L'astuce ici est que le module Hooker est maintenant extend ing si le include .

+0

Salut, fonctionne bien comme je le voulais. J'ai déjà accepté l'autre réponse, habituellement si une autre réponse est plus fiable est ok pour changer l'acceptation? EDIT: ne fonctionne pas. –

+0

Oui, il est recommandé de [modifier, ou même de ne pas accepter] (https://meta.stackexchange.com/q/93969/369864) la réponse acceptée. Vous devriez choisir la réponse * que vous avez trouvée la plus utile *. (Ou aucun, si aucune réponse n'était suffisamment utile!) Je ne suis pas sûr pourquoi cela ne fonctionne pas, cependant; il y a peut-être une limite de temps ou quelque chose comme ça. (Je serais intéressé de savoir si vous le comprendre!) –