重新打开一个由gem提供的ActiveRecord模型'。[英] Re-open an ActiveRecord model that's provided by a gem

本文是小编为大家收集整理的关于重新打开一个由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中的分叉版本(这是我的喜好).

本文地址:https://www.itbaoku.cn/post/786500.html

问题描述

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).