重新打开,用一个宝石提供的ActiveRecord模型(Re-open an ActiveRecor

2019-07-29 13:57发布

我试图延长ActiveRecord模型( Vote ),一个宝石( https://github.com/peteonrails/vote_fu )提供了我的申请。 (即,没有vote.rbapp/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协会的行为古怪。 (另外,问题消失,如果我设置config.cache_classes = truedevelopment.rb

为了解决这个问题,我试图通过添加使投票扩展在每次请求重新加载to_prepare块到我的development.rb

config.to_prepare do
  load 'extend_vote.rb'
end

这解决了(can't dup NilClass)问题,但现在每当我创建一个新的投票中, create_activity_stream_event回调函数被调用的额外时间。 即,第一投称之为一次,第二次调用它两次,等,等,所以它似乎像to_prepare块太积极重装的延伸和添加重复回调。

什么是方法和添加回调的最佳方式Vote模式呢?

Answer 1:

[UPDATE:应防止模块被正确的解决方案包括在同一类几次]

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


Answer 2:

提醒一句:这是一个非常古老的宝石(最后一次提交的是3岁)和它的外观作为是不会与钢轨3.X工作。 在Rails 3.x的引擎使得这类东西比较容易的方式。

据我所知,在第一种情况下的问题不在于投票模型被重新加载(它不应该),但该activity_stream_event模型重新加载。 由于投票模型没有重载协会是左挂到的版本activity_stream_event类从重装前。 由于轨道的胆量了班,他们得到重新加载之前,这会导致问题。

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

#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红宝石线)



Answer 3:

我想尝试什么agmcleod提出的意见,但不是把它在lib中,把它在配置/初始化/ vote.rb:

 class Vote
   after_create :create_activity_stream_event
   has_one :activity_stream_event

   def create_activity_stream_event
   # something..
   end
 end

当然,你可以用叉子叉的宝石,让您的修改,并链接到你的Gemfile的分叉版本(这是我的偏好)。



Answer 4:

阿德里安Coquio有正确的想法ActiveSupport::Concerns ,这是Rails的方式来扩展模型。 他的代码将工作,你应该使用它。

然而,这将无法工作在发展所有的时间,因为当Rails的重新加载你的类时,一个文件的更改,也不会重新evaulate的#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回调呢? 还没试过。


Answer 5:

你的问题可能是由于这样的事实,你是猴子修补类。 当铁轨试图重新加载常数是不考虑你的文件。

尝试使用模块技术为如下。

添加一个名为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宝石有一些新的功能和增强功能。



Answer 6:

你可以尝试这样的事:

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



文章来源: Re-open an ActiveRecord model that's provided by a gem