【问题标题】:Send message to object via class scope (metaprogramming)通过类范围向对象发送消息(元编程)
【发布时间】:2014-11-08 16:29:47
【问题描述】:

我需要一种方法从 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。

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

【问题讨论】:

  • 这看起来有点困难——这肯定是可能的,可能很容易,但乍一看似乎你在你的对象上调用了一个实例方法,但你想使用一个完全不同的方法在类实例上。要改变self的含义,请查看.clas_eval、.instance_eval和instance_eval(&block) if block_given?
  • 您正在调用一个不存在的方法,即 stuff()。因此,您需要在提出问题之前解决该问题。
  • 我可以想出很多方法来获得想要的结果,但我不知道你想要哪一种,因为你的问题太模糊了。
  • 如果您不尽快进行编辑澄清,您的问题将被关闭。我认为你需要做的就是说你想添加一个方法stuff,它会产生你预期的输出,但你不知道如何编写那个方法。

标签: ruby metaprogramming


【解决方案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被创建时,self就是Dummy。因此,我们需要指示Dummy 像上面一样定义方法final_value,从而替换它之前的定义。幸运的是,有一种方法可以做到这一点:Module#define_method

要执行:define_method,我们只需要将它连同它的参数:final_value和一个将成为:final_value的主体的块一起发送到Dummy,使用方法Object#send

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

这很好,但假设我们要将类重命名为Smartie?我们必须记住在方法stuff 中将Dummy 更改为Smartie。最好将Dummy 替换为self.class。顺便说一句,这是您需要包含self. 的少数情况之一,因为class 单独被解释为关键字class,如class Dummy

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

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

注意,因为 Ruby 会按顺序解析这些行,从而构建类,

stuff :final_value do
  my_method
end

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

【讨论】:

  • 这行不通。 stuff在这里是实例方法,不是类方法。
  • @sethvargo,是的,它是一个实例方法,但是self.class => Dummy,所以我不明白你的意思。
  • 在他的例子中,他想在类级别使用stuff 来定义一个新方法......除非我误解了OP。
  • OP 说stuff 是一种方法。要调用它,需要Dummy.stuffDummy.new.stuff,具体取决于它是类方法还是实例方法。这些陈述都没有显示。也没有initialize 方法可以做到这一点。我以为它是一个实例方法,但将它变成一个类方法是一个微不足道的改变。顺便说一句,你认为它应该是一个类方法是什么?
  • 但是看看 OP 的示例用法 - 它是一个类方法,而不是一个实例方法......
【解决方案2】:

处理它的一种临时方法是在 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-05
    • 2013-04-02
    • 2018-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-09
    • 1970-01-01
    相关资源
    最近更新 更多