将块传递给动态创建的方法

时间:2013-11-01 08:30:21

标签: ruby-on-rails ruby metaprogramming

我正在创建一个扩展ActiveRecord模型功能的模块。

这是我的初始设置。

我的课程:

class MyClass < ActiveRecord::Base
  is_my_modiable
end

和模块:

module MyMod
  def self.is_my_modiable
    class_eval do 
      def new_method
        self.mod = true
        self.save!
      end
   end
  end
end
ActiveRecord::Base(extend,MyMod)

我现在要做的是通过传入一个块来扩展new_method的功能。像这样:

class MyClass < ActiveRecord::Base
  is_my_modiable do
    self.something_special
  end
end

module MyMod
  def self.is_my_modiable
    class_eval do 
      def new_method
        yield if block_given?
        self.mod = true
        self.save!
      end
   end
  end
end

虽然这不起作用,但这是有道理的。在class_eval中,new_method没有被执行,只是被定义,因此在实际调用该方法之前,yield语句不会被执行。

我试图将块分配给class_eval中的类变量,然后在方法中调用该类变量,但是在所有is_my_modiable模型上调用该块,即使它们没有将块传递给方法。

我可能会覆盖该方法以获得相同的效果,但我希望有一种更优雅的方式。

2 个答案:

答案 0 :(得分:2)

如果我理解正确,你可以通过将传递的块保存到类对象上的实例变量然后在实例方法中对其进行评估来解决这个问题。

bl.call不会在这里执行,因为它将在原始上下文(类的)中执行,并且您需要在当前实例的范围内执行它。

module MyMod
  def is_my_modiable(&block)
    class_eval do
      @stored_block = block # back up block
      def new_method
        bl = self.class.instance_variable_get(:@stored_block) # get from class and execute
        instance_eval(&bl) if bl
        self.mod = true
        self.save!
      end
    end
  end
end

class MyClass
  extend MyMod

  is_my_modiable do
    puts "in my modiable block"
    self.something_special
  end

  def something_special
    puts "in something special"
  end

  attr_accessor :mod
  def save!; end
end

MyClass.new.new_method
# >> in my modiable block
# >> in something special

答案 1 :(得分:1)

您可以通过将块指定为方法参数来执行此操作:

module MyMod
  def self.is_my_modiable
    class_eval do 
      def new_method(&block)
        block.call if block
        self.mod = true
        self.save!
      end
   end
  end
end