How to call expire_fragment from Rails Observer/Model?

前端 未结 8 810
长发绾君心
长发绾君心 2020-12-23 10:15

I\'ve pretty much tried everything, but it seems impossible to use expire_fragment from models? I know you\'re not supposed to and it\'s non-MVC, but surely there much be

相关标签:
8条回答
  • 2020-12-23 10:51

    Why not have your external rake tasks call the expiry method on the controller. Then you're still being MVC compliant, you aren't building in a dependence on some scoping hack, etc.

    For that matter, why don't you just put all the daemon / external functionality on a controller and have rake / cron just call that. It would be loads easier to maintain.

    -- MarkusQ

    0 讨论(0)
  • 2020-12-23 10:57

    This is quite easy to do. You can implement Orion's suggestion, but you can also implement the broader technique illustrated below, which gives you access to the current controller from any model and for whichever purpose you decided to break MVC separation for (e.g. messing with the fragment cache, accessing current_user, generating paths/URLs, etc.)

    In order to gain access to the current request's controller (if any) from any model, add the following to environment.rb or, much preferably, to a new plugin (e.g. create vendor/plugins/controller_from_model/init.rb containing the code below):

    module ActiveRecord
      class Base
        protected
          def self.thread_safe_current_controller #:nodoc:
            Thread.current[:current_controller]
          end
    
          def self.thread_safe_current_controller=(controller) #:nodoc:
            Thread.current[:current_controller] = controller
          end
    
          # pick up the correct current_controller version
          #  from @@allow_concurrency
          if @@allow_concurrency
            alias_method :current_controller,  :thread_safe_current_controller
            alias_method :current_controller=, :thread_safe_current_controller=
          else
            cattr_accessor :current_controller
          end
      end
    end
    

    Then, in app/controllers/application.rb,

    class ApplicationController < ActionController::Base
      before_filter { |controller|
        # all models in this thread/process refer to this controller
        #  while processing this request
        ActiveRecord::Base.current_controller = controller
      }
    
      ...
    

    Then, from any model,

    if controller = ActiveRecord::Base.current_controller
      # called from within a user request
    else
      # no controller is available, didn't get here from a request - maybe irb?
    fi
    

    Anyhow, in your particular case you might want to inject code into your various ActiveRecord::Base descendants when the relevant controller classes load, so that the actual controller-aware code still resides in app/controllers/*.rb, but it is not mandatory to do so in order to get something functional (though ugly and hard to maintain.)

    Have fun!

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