Использование методов из двух разных областей? - PullRequest
0 голосов
/ 11 сентября 2010

Как использовать методы из двух разных пространств имен?

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

В приведенном выше примере method1 не может быть вызван, поскольку он не находится в области действия Bar.Как сделать так, чтобы методы из обеих областей вызывались одновременно?

Ответы [ 4 ]

3 голосов
/ 11 сентября 2010

Хитрость заключается в пересылке пропущенных вызовов методов в экземпляр 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"
1 голос
/ 18 сентября 2010

Я бы упростил ваш код следующим образом:

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

Блочная логика чиста, и реализация не требует переключения контекста. Вы используете стандартную функциональность ruby ​​для передачи параметров в блок.

1 голос
/ 12 сентября 2010

Может быть, это самый простой способ. Не нужно изменять 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 голос
/ 11 сентября 2010

Попробуйте это:

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 

Это делает переменную экземпляра уровня класса @fa Bar, которая устанавливается при инициализации объекта Bar с помощью new (foo) в Bar.configure.Передаваемый блок предполагает существование @f, который содержит ссылку на объект класса Foo.

Это сложный способ сделать это, хотя я не могу придумать ничего лучшего.Было бы интересно узнать вариант использования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...