简体   繁体   English

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

[英]Passing a block to a dynamically created method

I'm creating a module that extends the functionality of an ActiveRecord model. 我正在创建一个扩展ActiveRecord模型功能的模块。

Here's my initial setup. 这是我的初始设置。

My class: 我的课:

class MyClass < ActiveRecord::Base
  is_my_modiable
end

And Module: 和模块:

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)

What I would like to do now is extend the functionality of the new_method by passing in a block. 我现在要做的是通过传入一个块来扩展new_method的功能。 Something like this: 像这样的东西:

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

This doesn't work though, and it makes sense. 这不起作用,这是有道理的。 In the class_eval, the new_method isn't being executed, just defined, and thus the yield statement wouldn't get executed until the method actually gets called. 在class_eval中,new_method没有被执行,只是被定义,因此在实际调用该方法之前,yield语句不会被执行。

I've tried to assign the block to a class variable within the class_eval, and then call that class variable within the method, but the block was being called on all is_my_modiable models, even if they didn't pass a block into the method. 我试图将块分配给class_eval中的类变量,然后在方法中调用该类变量,但是在所有is_my_modiable模型上调用该块,即使它们没有将块传递给方法。

I might just override the method to get the same effect, but I'm hoping there is a more elegant way. 我可能会覆盖该方法以获得相同的效果,但我希望有一种更优雅的方式。

If I understood you correctly, you can solve this by saving passed block to an instance variable on class object and then evaling that in instance methods. 如果我理解正确,你可以通过将传递的块保存到类对象上的实例变量然后在实例方法中进行评估来解决这个问题。

bl.call won't do here, because it will execute in the original context (that of a class) and you need to execute it in scope of this current instance. 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

You can do this by assigning the block as a method parameter: 您可以通过将块指定为方法参数来执行此操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM