重新打开由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_fu
gem的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”。