我正在尝试将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
模型添加方法和回调的最佳方法是什么?
答案 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,则无需执行此操作。
ActionDispatch::Reloader
回调吗?还没试过。答案 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有一些新功能和增强功能。