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<
could you try something like this :
class Vote
after_create :create_activity_stream_event
has_one :activity_stream_event
def create_activity_stream_event
# something..
end
end
I think than it will add your function and call functions "after_create" and "has_hone".
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)
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.
Adrien Coquio has the right idea with ActiveSupport::Concerns
, which are the Rails way to extend models. His code will work, and you should use it.
However this will not work all the time in development because when Rails reloads your classes when a file changes, it will not re-evaulate the #send
line. The only solution I could find was attaching to an ActionDispatch
callback in production to ensure the file is re-required after every page load:
if Rails.env.development?
ActionDispatch::Callbacks.to_prepare do
require_dependency "../../lib/vote_fu_extensions"
end
end
In production, or if you set cache_classes
to true in your config, you won't need to do this.
ActionDispatch::Reloader
callbacks instead? Haven't tried yet.[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)
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).