ruby元编程从实例方法获取实例变量的容器类

时间:2014-10-11 23:07:49

标签: ruby metaprogramming

想象一下,我们有三个班级:

class SomeClass

  def container_class
    # ...
  end

end

class Container
  attr_accessor :property
end

class Container2
  attr_accessor :lalala
end

# now I create instances
p = SomeClass.new

c = Container.new
c.property = p
c.property.container_class # => should return Container 

c2 = Container2.new
c2.lalala = p
c2.lalala.container_class # => should return Container2

问题: 如何使用ruby元编程来编写方法container_class,以便返回调用此方法的实例的容器(组合)类?

您只能使用元编程。不应手动更改容器类。

2 个答案:

答案 0 :(得分:1)

我从here那里得到了这个答案。以下是获取调用者绑定的方法:

require 'continuation'

def caller_binding
  cc = nil     # must be present to work within lambda
  count = 0    # counter of returns

  set_trace_func lambda { |event, file, lineno, id, binding, klass|
    # First return gets to the caller of this method
    # (which already know its own binding).
    # Second return gets to the caller of the caller.
    # That's we want!
    if count == 2
      set_trace_func nil
      # Will return the binding to the callcc below.
      cc.call binding
    elsif event == "return"
      count += 1
    end
  }
  # First time it'll set the cc and return nil to the caller.
  # So it's important to the caller to return again
  # if it gets nil, then we get the second return.
  # Second time it'll return the binding.
  return callcc { |cont| cc = cont }
end

现在,您可以按如下方式定义班级SomeClass

class SomeClass
  def container_class
    return unless bnd = caller_binding
    bnd.eval "self.class"
  end
end

答案 1 :(得分:0)

您可以使用它,我不是使用最漂亮的方式,但您可以编写一个注入该方法的新访问器。

类:

class Container
  def property=(arg)
    arg.called_from = self.class
    @arg = arg
  end

  def property
    @arg
  end
end

class SomeClass
  def container_class
    @klass
  end

  def called_from=(klass)
    @klass = klass
  end
end

规格:

require_relative 'container'

describe Container do
  let(:container) { Container.new }

  it 'passes message' do
    container.property = SomeClass.new

    expect(container.property.container_class).to eq Container
  end
end