6

我正在尝试扩展Votegem ( https://github.com/peteonrails/vote_fu ) 为我的应用程序提供的 ActiveRecord 模型 ( )。(即,没有vote.rbin 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关联行为异常。config.cache_classes = true(此外,如果我设置,问题就会消失development.rb

为了解决这个问题,我尝试通过to_prepare在 my 中添加一个块来重新加载每个请求的投票扩展development.rb

config.to_prepare do
  load 'extend_vote.rb'
end

这解决了(can't dup NilClass)问题,但现在每当我创建一个新投票时,create_activity_stream_event回调都会被调用一次。即,第一次投票调用它一次,第二次调用它两次,依此类推。所以看起来该to_prepare块正在积极地重新加载扩展并添加重复的回调。

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

4

6 回答 6

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)
于 2012-06-07T22:50:10.463 回答
4

提醒一句:这是一个非常古老的 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

这样做是允许您拥有自己的 Vote 类,该类继承自 gem 中的类。

但是,我再次敦促您找到更新的东西或自己动手制作(宝石只有约 250 行红宝石)

于 2012-06-01T14:50:50.560 回答
2

我会尝试 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 中的分叉版本(这是我的偏好)。

于 2012-05-26T02:58:14.737 回答
1

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,则无需执行此操作。

于 2013-05-13T11:08:40.387 回答
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”。

于 2012-06-01T14:01:01.753 回答
0

Your issue might be due to the fact that you are monkey patching the class. When rails tries to reload the constants it is not considering your file.

Try to use module technique as given below.

Add a file called 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)

Add an initializer called config/initializers/vote_fu.rb

require "vote_fu_extension"

Note

If you want to add class methods to the Vote model refer to this answer.

Shameless plug: My fork of the vote_fu gem has some new features and enhancements.

于 2012-06-07T23:59:52.223 回答