Создать Mixin с набором методов, которые могут вызывать методы другого класса. - PullRequest
2 голосов
/ 10 ноября 2011

Я хочу определить набор методов, которые могут быть добавлены в класс (C в примере), используя Mixin. Эти методы могут быть определены любым классом, который наследуется от другого класса (в примере A) и должен иметь возможность вызывать методы в экземпляре получателя (экземпляр C).

У меня есть этот фрагмент кода:

M = Module.new

class A
  class << self
    def init(method)
      if block_given?
        M.send(:define_method, method) do
          instance_exec &Proc.new
        end
      else
        block = self.method(method).to_proc
        M.send(:define_method, method) do
          yield block.call
        end
      end
    end
  end
end

class B < A
  init(:foo) do
    "foo+".concat(c_method)
  end

  def self.bar
    "bar+".concat(c_method)
  end

  init(:bar) 
end

C = Class.new do
  def c_method
    "c_method"
  end
end

c = C.new

c.extend(M)

puts c.foo

puts c.bar

Добавление методов с использованием блоков работает, но последняя строка не работает: (

foo+c_method
test.rb:28:in `bar': undefined local variable or method `c_method' for B:Class (NameError)
from test.rb:15:in `call'
from test.rb:15:in `block in init'
from test.rb:46:in `<main>'

Что я делаю не так? Или это не имеет смысла?

Спасибо

Juan

Ответы [ 2 ]

1 голос
/ 10 ноября 2011

Когда вы готовите instance_exec &Proc.new внутри оператора if, этот оператор выполняется в экземпляре класса C в качестве контекста. Вы можете проверить это, добавив puts self внутри блока для init(:foo). С другой стороны, когда вы вызываете yield block.call, вы приводите выполнение потока в контекст объекта класса B (конечно, не к экземпляру этого класса :)). Это место вашего кода ничего не знает о C :: c_method, и это является причиной ошибки.

0 голосов
/ 10 ноября 2011

Кажется, что я пытаюсь сделать, это отменить привязку метода: бар от B и привязку к C, что запрещено.Вы можете найти больше информации в этом замечательном сообщении

M = Module.new

class A
  class << self
    def init(method)
      if block_given?
        M.send(:define_method, method) do
          instance_exec &Proc.new
        end
      else
        block = self.method(method).unbind
        M.send(:define_method, method) do
          m = block.bind(self)
          puts m
        end
      end
    end
  end
end

class B < A
  init(:foo) do
    "foo+".concat(c_method)
  end

  def self.bar
    "bar+".concat(c_method)
  end

  init(:bar) 
end

C = Class.new do
  def c_method
    "c_method"
  end
end

c = C.new

c.extend(M)

puts c.foo

puts c.bar

foo+c_method
test.rb:16:in `bind': singleton method called for a different object (TypeError)
from test.rb:16:in `block in init'
from test.rb:48:in `<main>'
...