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

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

我的第一个方法是创建一个名为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关联表现得很奇怪。 (另外,如果我在development.rb设置config.cache_classes = true ,问题就会消失)

为了解决这个问题,我尝试通过向to_prepare添加to_prepare块来对每个请求重新加载投票扩展:

 config.to_prepare do load 'extend_vote.rb' end 

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

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

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

我相信你可以使用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) 

需要注意的是:这是一个非常古老的gem(最后一次提交是3年),它的外观不适用于rails 3.x。 在Rails 3.x引擎中使这种东西更容易。

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

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

 #in config/initializers/abstract_vote.rb AbstractVote = Vote AbstractVote.abstract_class = true Object.send :remove_const, :Vote #in app/models/vote.rb class Vote < AbstractVote after_create :create_activity_stream_event has_one :activity_stream_event def create_activity_stream_event end end 

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

但是,我再次敦促你找到更新的东西或自己动手(gem只有~250线ruby)

我会尝试在评论中建议的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 

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

Adrien Coquio对ActiveSupport::Concerns有正确的想法,这是扩展模型的Rails方式 。 他的代码会起作用,你应该使用它。

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

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

在生产中,或者如果在配置中将cache_classes设置为true,则不需要执行此操作。

  • 来源
  • RoR指导配置
  • 这表明它已被弃用 ,你应该使用ActionDispatch::Reloader回调吗? 还没有尝试过。

您的问题可能是因为您正在修补课程。 当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模型,请参阅此答案 。

无耻的插件:我的vote_fugem的vote_fu有一些新function和增强function。

你可以尝试这样的事情:

 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”。