本文是小编为大家收集整理的关于重新打开一个由gem提供的ActiveRecord模型'。的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。
问题描述
我正在尝试扩展一个宝石( https://github). com/peteonrails/pote_fu )向我的申请提供. (即,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,问题就消失了)
要解决这个问题,我试图通过在我的development.rb中添加to_prepare块来对每个请求进行投票扩展.
config.to_prepare do load 'extend_vote.rb' end
这解决了(can't dup NilClass)问题,但是现在每当我创建新的投票时,create_activity_stream_event回调都会额外称为.即,第一票将其调用一次,第二票将其调用两次,等等.
将方法和回调添加到此Vote模型的最佳方法是什么?
推荐答案
[更新:应该是防止模块在同一类中几次包含的模块的正确解决方案]
我相信您可以使用 activeVoll"> ActiveSupport ::关注点包括几次由回调导致的次数几次. 请参阅下面的示例:
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)
其他推荐答案
一个谨慎的词:这是一个非常古老的宝石(最后一个提案已有3年的历史),而且外观不会像IS一样与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
这样做的是让您拥有自己从宝石中继承的投票类.
但是,我再次敦促您找到更多最新的东西或滚动自己的东西(宝石只有〜250行红宝石)
其他推荐答案
我会尝试agmcleod在评论中建议的内容,而不是将其放入lib中,而是将其放入 config/initializers/doct.rb :
中class Vote after_create :create_activity_stream_event has_one :activity_stream_event def create_activity_stream_event # something.. end end
当然,您可以分叉宝石,进行修改并链接到您的Gemfile中的分叉版本(这是我的喜好).
问题描述
I'm trying to extend an ActiveRecord model (Vote) that a gem (https://github.com/peteonrails/vote_fu) provides to my application. (I.e., there is no vote.rb in app/models)
My first approach was to create a file called lib/extend_vote.rb that contains the code:
Vote.class_eval do after_create :create_activity_stream_event has_one :activity_stream_event def create_activity_stream_event # something.. end end
This works when the first vote is created, but when I try to create each subsequent vote I get the error TypeError (can't dup NilClass).
I think this error is caused by the fact that the Vote class is reloaded automatically after every request, but the code in lib/extend_vote.rb is loaded only once when the server starts and this causes the has_one :activity_stream_event association to behave weirdly. (Also, the problem goes away if I set config.cache_classes = true in development.rb)
To solve this problem, I tried to make the vote extensions reload on every request by adding a to_prepare block to my development.rb:
config.to_prepare do load 'extend_vote.rb' end
This solves the (can't dup NilClass) problem, but now whenever I create a new vote, the create_activity_stream_event callback gets called an additional time. I.e., the first vote calls it once, the second calls it twice, etc, etc. So it seems like the to_prepare block is reloading the extension TOO aggressively and adding duplicate callbacks.
What's the best way to add methods and callbacks to this Vote model?
推荐答案
[UPDATE: should be the right solution to prevent the module being include several times in the same class]
I believe you can use ActiveSupport::Concern to prevent the module being include several times which result by callback called several time. See the example below :
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)
其他推荐答案
A word of caution: this is a very old gem (last commit is 3 years old) and by the looks of it won't work with rails 3.x as is. In Rails 3.x engines makes this sort of stuff way easier.
As I understand it the problem in the first case is not that the vote model gets reloaded (it shouldn't) but that the activity_stream_event model is reloaded. Because the vote model isn't reloaded the association is left hanging onto the version of the activity_stream_event class from before the reload. Since rails guts out classes before they get reloaded, this causes problems.
With this in mine, try this hack:
#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
What this does is allow you to have your own Vote class that inherits from the one in the gem.
But again, I urge you to find something more up to date or roll your own (the gem is only ~250 lines of ruby)
其他推荐答案
I'd try what agmcleod suggested in the comments but instead of putting it in lib, put it in 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
Of course, you could fork the gem, make your modifications and link to your forked version in your Gemfile (that's my preference).