Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-open an ActiveRecord model that's provided by a gem

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?

like image 389
Tom Lehman Avatar asked May 25 '12 20:05

Tom Lehman


1 Answers

[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)
like image 178
Adrien Coquio Avatar answered Dec 01 '22 08:12

Adrien Coquio