重新打开由gem提供的ActiveRecord模型

时间:2012-05-25 20:37:20

标签: ruby-on-rails model gem metaprogramming

我正在尝试将gem(https://github.com/peteonrails/vote_fu)提供给我的应用程序的ActiveRecord模型(Vote)扩展。 (即vote.rb

中没有app/models

我的第一个方法是创建一个名为lib/extend_vote.rb的文件,其中包含代码:

Vote.class_eval do
  after_create :create_activity_stream_event
  has_one :activity_stream_event

  def create_activity_stream_event
    # something..
  end
end

这在创建第一个投票时有效,但当我尝试创建每个后续投票时,我收到错误TypeError (can't dup NilClass)

我认为这个错误是由于Vote类在每次请求后自动重新加载这一事实引起的,但lib/extend_vote.rb中的代码只在服务器启动时加载一次,这会导致has_one :activity_stream_event 1}}关联表现得很奇怪。 (另外,如果我在config.cache_classes = true

中设置development.rb,问题就会消失

要解决此问题,我尝试通过向to_prepare添加development.rb块来重新加载每个请求的投票扩展名:

config.to_prepare do
  load 'extend_vote.rb'
end

这解决了(can't dup NilClass)问题,但现在每当我创建新投票时,create_activity_stream_event回调都会被调用一次。即,第一次投票调用一次,第二次投票两次,等等,所以看起来to_prepare块正在积极地重新加载扩展TOO并添加重复的回调。

向此Vote模型添加方法和回调的最佳方法是什么?

6 个答案:

答案 0 :(得分:6)

[更新:应该是防止模块在同一类中多次包含的正确解决方案]

我相信您可以使用ActiveSupport::Concern来阻止模块被多次包含,这是由多次调用产生的。 请参阅以下示例:

module VotePatch
  extend ActiveSupport::Concern

  included do
    after_create :create_activity_stream_event
    has_one :activity_stream_event
  end

  module InstanceMethods
    def create_activity_stream_event
      #your code here
    end  
  end

end

Vote.send(:include, VotePatch)

答案 1 :(得分:4)

请注意:这是一个非常古老的宝石(最后一次提交是3年),并且看起来它不适用于rails 3.x。在Rails 3.x引擎中,这种方式更容易。

据我所知,第一种情况下的问题不是重新加载投票模型(它不应该重新加载),而是重新加载activity_stream_event模型。由于未重新加载投票模型,因此关联将在重新加载之前挂在{{​​1}}类的版本上。由于rails在重新加载之前会释放类,因此会导致问题。

有了这个,试试这个黑客:

activity_stream_event

这样做可以让你拥有自己的投票类,继承自gem中的投票类。

但是,我再次强烈建议您找一些更新的东西或自己动手(宝石只有~250线红宝石)

答案 2 :(得分:1)

我会尝试在评论中建议的agmcleod,而不是将其放入lib中,将其放入 config / initializers / vote.rb

 class Vote
   after_create :create_activity_stream_event
   has_one :activity_stream_event

   def create_activity_stream_event
   # something..
   end
 end

当然,您可以在Gemfile中分叉gem,进行修改并链接到分叉版本(这是我的偏好)。

答案 3 :(得分:1)

Adrien Coquio对ActiveSupport::Concerns有正确的想法,the Rails way可以扩展模型。他的代码会起作用,你应该使用它。

但是这在开发过程中不会一直有效,因为当Rails在文件更改时重新加载类时,它不会重新调整#send行。我能找到的唯一解决方案是附加到生产中的ActionDispatch回调,以确保在每次加载页面后重新需要该文件:

if Rails.env.development?
  ActionDispatch::Callbacks.to_prepare do
    require_dependency "../../lib/vote_fu_extensions"
  end
end

在制作中,或者如果您在配置中将cache_classes设置为true,则无需执行此操作。

答案 4 :(得分:0)

你可以尝试这样的事情:

class Vote
    after_create :create_activity_stream_event
    has_one :activity_stream_event

    def create_activity_stream_event
        # something..
    end
end

我认为它会添加你的功能和调用函数" after_create"和" has_hone"。

答案 5 :(得分:0)

您的问题可能是因为您正在修补课程。当rails尝试重新加载常量时 它没有考虑你的档案。

尝试使用下面给出的模块技术。

添加名为lib/vote_fu_extension.rb

的文件
module VoteFuExtension
  def self.included(base)
    base.has_one :activity_stream_event
    base.after_create :create_activity_stream_event
  end
  def create_activity_stream_event
    # something..
  end  
end
Vote.send(:include, VoteFuExtension)

添加名为config/initializers/vote_fu.rb

的初始值设定项
require "vote_fu_extension"

注意

如果您想将类方法添加到Vote模型,请参阅此answer

无耻插件:我vote_fu宝石的fork有一些新功能和增强功能。