通过类范围向元数据发送消息(元编程)

时间:2014-11-08 16:29:48

标签: ruby metaprogramming

我需要一种从 stuff 方法(通过元编程)发送消息的方法,该方法在对象范围上执行 my_method 。如果没有在Dummy类上插入更多代码,有一种很好的方法吗?

class Dummy
  stuff :final_value do
    value :my_method
  end

  def my_method
    10.5
  end

  def final_value
    0
  end
end

预期回报:

dummy = Dummy.new
dummy.final_value
=> 10.5

我们的想法是采用 value args上的方法,并找到一种方法来映射来自对象范围的值,这个例子是10.5。

在那里发布临时解决方案。

2 个答案:

答案 0 :(得分:1)

这应该这样做。

class Dummy
  def stuff
    self.class.send(:define_method, :final_value) do
      my_method
    end
  end

  def my_method
    10.5
  end

  def final_value
    0
  end
end

dummy = Dummy.new
dummy.stuff
dummy.final_value #=> 10.5

方法stuff的目的是将方法final_value更改为:

def final_value
  my_method
end

因此我们需要动态地执行以下操作:

class Dummy
  def final_value
    my_method
  end
end

此处,创建final_value时,selfDummy。因此,我们需要指示Dummy如上所述定义方法final_value,从而取代其早先的定义。幸运的是,有一种方法就是这样做:Module#define_method

要执行:define_method,我们只需要将其与:final_value以及:final_value的正文一起发送到Dummy一起发送给def stuff Dummy.send(self.class.send(:define_method, :final_value) do my_method end end ,方法Object#send

Smartie

这很好,但是假设我们要将这个类重命名为Dummy?我们必须记住在方法Smartie中将stuff更改为Dummy。将self.class替换为self.会更好。顺便说一下,这是您需要包含class的少数几个实例之一,因为class单独被解释为关键字class Dummy,就像在class Dummy def self.stuff(method, &block) send(:define_method, method, &block) end def my_method 10.5 end def final_value 0 end stuff :final_value do my_method end end Dummy.new.final_value #=> 10.5 中一样。

编辑:我可能误解了这个问题。 (请参阅下面的评论。)以下可能是您想要的:

stuff :final_value do
  my_method
end

请注意,因为Ruby按顺序解析行,然后按原样构建类,

{{1}}

必须出现在其他方法之后。

答案 1 :(得分:0)

处理它的一种临时方法是在define_method作用域上执行代码,这是对象作用域本身。该解决方案尚未经过全面测试,但其理念几乎就是:

def stuff(method_name, &block)
  subject = Stuff.new(block)

  # Defining the final_value method and executing code on object scope.

  define_method method_name do
    # making the sum on object scope (demand is a list of *value* args, 
    # which is a list of methods to execute on object scope)
    #
    # The logic here is to sum them to return as final_value result

    subject.total = subject.demand.map do |method|
      self.send(method)
    end.reduce(:+)

    subject.total
  end

  subject.evaluate!
end

class Stuff
  attr_accessor :demand, :total

  def initialize(stuff_block)
    @stuff_block = stuff_block
    @total = 0
  end

  # Brings all stuff block into Stuff class
  def evaluate!
    @stuff_block.call
  end

  # The main problem was this.
  # The solution was mapping those demanded methods into a demand local variable,
  # then accessing this variable on define_method scope inside *stuff* method, which is the
  # object scope itself. Bahn!
  def value(*args)
    @demand = args
  end
end