2010-01-25 3 views
4

Je suis en train d'ajouter l'enregistrement à une méthode de l'extérieur (Aspect-style orienté)exploitation forestière Recoupement Ruby

class A 
    def test 
    puts "I'm Doing something..." 
    end 
end 

class A # with logging! 
    alias_method :test_orig, :test 
    def test 
    puts "Log Message!" 
    test_orig 
    end 
end 

a = A.new 
a.test 

Les travaux ci-dessus va bien, sauf que si jamais je devais faire alias la méthode à nouveau, il va dans une boucle infinie. Je veux quelque chose de plus comme super, où je pourrais l'étendre autant de fois que nécessaire, et chaque extension avec alias son parent.

Répondre

9

Une autre alternative est d'utiliser des méthodes non liées:

class A 
    original_test = instance_method(:test) 
    define_method(:test) do 
    puts "Log Message!" 
    original_test.bind(self).call 
    end 
end 

class A 
    original_test = instance_method(:test) 
    counter = 0 
    define_method(:test) do 
    counter += 1 
    puts "Counter = #{counter}" 
    original_test.bind(self).call 
    end 
end 

irb> A.new.test 
Counter = 1 
Log Message! 
#=> #.... 
irb> A.new.test 
Counter = 2 
Log Message! 
#=> #..... 

Ceci a l'avantage qu'il ne pollue pas l'espace de noms avec des noms de méthode supplémentaires, et est assez facilement Abstraite, si vous souhaitez effectuer une méthode de classe add_logging ou ce que vous avez.

class Module 
    def add_logging(*method_names) 
    method_names.each do |method_name| 
     original_method = instance_method(method_name) 
     define_method(method_name) do |*args,&blk| 
     puts "logging #{method_name}" 
     original_method.bind(self).call(*args,&blk) 
     end 
    end 
    end 
end 

class A 
    add_logging :test 
end 

Ou, si vous voulez être en mesure de faire un tas d'aspects w/o beaucoup de la plaque de la chaudière, vous pouvez écrire une méthode qui écrit des méthodes d'aspect en ajoutant!

class Module 
    def self.define_aspect(aspect_name, &definition) 
    define_method(:"add_#{aspect_name}") do |*method_names| 
     method_names.each do |method_name| 
     original_method = instance_method(method_name) 
     define_method(method_name, &(definition[method_name, original_method])) 
     end 
    end 
    end 
    # make an add_logging method 
    define_aspect :logging do |method_name, original_method| 
    lambda do |*args, &blk| 
     puts "Logging #{method_name}" 
     original_method.bind(self).call(*args, &blk) 
    end 
    end 
    # make an add_counting method 
    global_counter = 0 
    define_aspect :counting do |method_name, original_method| 
    local_counter = 0 
    lambda do |*args, &blk| 
     global_counter += 1 
     local_counter += 1 
     puts "Counters: [email protected]#{global_counter}, [email protected]#{local_counter}" 
     original_method.bind(self).call(*args, &blk) 
    end 
    end  
end 

class A 
    def test 
    puts "I'm Doing something..." 
    end 
    def test1 
    puts "I'm Doing something once..." 
    end 
    def test2 
    puts "I'm Doing something twice..." 
    puts "I'm Doing something twice..." 
    end 
    def test3 
    puts "I'm Doing something thrice..." 
    puts "I'm Doing something thrice..." 
    puts "I'm Doing something thrice..." 
    end 
    def other_tests 
    puts "I'm Doing something else..." 
    end 

    add_logging :test, :test2, :test3 
    add_counting :other_tests, :test1, :test3 
end 
+0

"Cela a l'avantage de ne pas polluer l'espace de noms avec des noms de méthodes supplémentaires": Oui! Et étant donné que cette pollution était la source du problème des PO en premier lieu, c'est un avantage plutôt convaincant. –

+0

Parfait, merci –

+0

Attendez ... Donc, ma classe d'origine doit écrire sa méthode comme non liée en premier lieu? C'est un peu ennuyeux –

2

Premier choix: sous-classe au lieu de passer outre:

class AWithLogging < A\ 
    def test 
    puts "Log Message!" 
    super 
    end 
end 

Deuxième choix: nommer vos méthodes orig plus attentivement:

class A # with logging! 
    alias_method :test_without_logging, :test 
    def test 
    puts "Log Message!" 
    test_without_logging 
    end 
end 

Ensuite, un autre aspect utilise un nom différent orig:

class A # with frobnication! 
    alias_method :test_without_frobnication, :test 
    def test 
    Frobnitz.frobnicate(self) 
    test_without_frobnication 
    end 
end 
+0

Le fait est que ces méthodes ne devraient pas avoir besoin d'avoir un nom en premier lieu. Voir la réponse de @ rampion pour une solution * beaucoup * meilleure. –

+0

Oui, je préfère aussi sa solution. Merci à tous les deux. – Grandpa

Questions connexes