1

我正在创建一个扩展 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 模型上都调用了该块,即使它们没有将块传递给方法。

我可能只是重写该方法以获得相同的效果,但我希望有一种更优雅的方法。

4

2 回答 2

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
于 2013-11-01T09:00:54.163 回答
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
于 2013-11-01T08:58:29.373 回答