2010-09-11 7 views
0

Comment utiliser des méthodes provenant de deux espaces de noms différents?Utilisation de méthodes provenant de deux étendues différentes?

class Bar 
    def self.configure &block 
    new.instance_eval &block 
    end 

    def method2 
    puts "from Bar" 
    end 
end 

class Foo 
    def method1 
    puts "from Foo" 
    end 

    def start 
    Bar.configure do 
     method1 
     method2 
    end 
    end 
end 

Foo.new.start 

Dans l'exemple ci-dessus method1 ne peut pas être appelé parce qu'il est pas du champ d'application de la barre. Comment puis-je rendre les méthodes des deux domaines appelables en même temps?

Répondre

3

L'astuce est de transmettre les appels de méthode manquant à l'instance de Foo:

class Bar 
    def self.configure(&block) 
     o = new 
     context = eval('self', block.binding) 

     class << o; self; end.send(:define_method, :method_missing) do |meth, *args| 
      context.send(meth, *args) 
     end 

     o.instance_eval &block 
    end 

    def method2 
     puts "from Bar" 
    end 
end 

class Foo 
    def method1 
     puts "from Foo" 
    end 

    def start 
     Bar.configure do 
      method1 
      method2 
     end 
    end 
end 

Foo.new.start #=> "from Foo" 
       #=> "from Bar" 
+0

+1 pour l'astuce context.send. Est-ce que cela continuera à fonctionner si Bar a aussi une méthode1? – Sudhanshu

+0

@Sudhanshu, oui, mais 'method1' dans' Bar' aura la priorité – horseyguy

1

Essayez ceci:

class Bar        
    def initialize(foo) 
    puts "init" 
    @f = foo 
    end 

    def self.configure(foo, &block) 
    new(foo).instance_eval &block 
    end 

    def method2 
    puts "from Bar" 
    end 
end 

class Foo 
    def method1 
    puts "from Foo" 
    end 

    def start 
    Bar.configure(self) do 
     @f.method1 
     method2 
    end 
    end 
end 

Cette @f fait une variable d'instance de niveau de classe de Bar, qui est défini lors de l'initialisation d'un objet de Bar à l'aide nouvelle (foo) dans Bar.configure. Le bloc qui passe suppose l'existence de @f, qui contient une référence à l'objet de la classe Foo.

C'est une manière alambiquée de le faire - je ne peux pas penser à quelque chose de mieux cependant. Ce serait intéressant de connaître le cas d'utilisation.

+0

Correction d'un petit bug. – Sudhanshu

1

Peut-être, cela est la meilleure façon. Il n'a pas besoin de modifier Bar.

class Bar 
    def self.configure &block 
    new.instance_eval &block 
    end 

    def method2 
    puts "from Bar" 
    end 
end 

class Foo 
    def method1 
    puts "from Foo" 
    end 

    def start 
    foo = self # add 
    Bar.configure do 
     foo.method1 # modify 
     method2 
    end 
    end 
end 

Foo.new.start 
1

Je simplifierait votre code comme suit:

class Bar 
    def self.configure &block 
    obj = new 
    block.call(obj) 
    obj 
    end 

    def method2 
    puts "from Bar" 
    end 
end 

class Foo 
    def method1 
    puts "from Foo" 
    end 

    def start 
    Bar.configure do |obj| 
     method1 
     obj.method2 
    end 
    end 
end 

Foo.new.start 

logique de bloc est propre et la mise en œuvre ne nécessite pas le changement de contexte. Vous utilisez la fonctionnalité ruby ​​standard de passer des paramètres au bloc.

Questions connexes