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

后端 未结 6 1946
旧时难觅i
旧时难觅i 2021-02-10 05:53

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<

相关标签:
6条回答
  • 2021-02-10 06:33

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

    0 讨论(0)
  • 2021-02-10 06:39

    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)

    0 讨论(0)
  • 2021-02-10 06:39

    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.

    0 讨论(0)
  • 2021-02-10 06:44

    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.

    • A source
    • RoR guides about configuration
    • This suggests it has been deprecated and you should use ActionDispatch::Reloader callbacks instead? Haven't tried yet.
    0 讨论(0)
  • 2021-02-10 06:47

    [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)
    
    0 讨论(0)
  • 2021-02-10 06:57

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

    0 讨论(0)
提交回复
热议问题