如何从Rails Observer / Model调用expire_fragment?

时间:2008-12-26 02:12:41

标签: ruby-on-rails ruby caching memcached

我几乎尝试了一切,但似乎无法使用 来自模特的expire_fragment?我知道你不应该这样做 非MVC,但肯定有一些方法可以做到这一点。

我在lib / cache_helper.rb中创建了一个包含所有过期助手的模块, 每个都只是一堆expire_fragment调用。我有我的全部 缓存清理器设置在/ app / sweepers下并有一个“包括 CacheHelper“在我的应用程序控制器中,因此缓存中的缓存 app通过控制器调用时工作正常。

然后事情是我有一些外部守护进程,特别是一些 重复的cron任务,它调用一个调用某个特定的rake任务 方法。此方法执行一些处理并将条目输入到 模型,之后我需要使缓存过期。

这是最好的方法,因为我无法在模型中指定缓存清理程序。 直观的观察者似乎是最好的解决方案,但接下来 抱怨expire_fragment未定义等等,我甚至 尝试将ActionController缓存类包含在观察者中 但那没用。我喜欢有关如何创建解决方案的一些想法 为了这。谢谢。

8 个答案:

答案 0 :(得分:43)

免责声明:我的导轨有点生锈,但这个或类似的东西应该可以使用

ActionController::Base.new.expire_fragment(key, options = nil) 

答案 1 :(得分:13)

Orion提供的解决方案完美无缺。为了方便起见,我将以下代码放入config/initializers/active_record_expire_fragment.rb

class ActiveRecord::Base
  def expire_fragment(*args)
    ActionController::Base.new.expire_fragment(*args)
  end
end

现在,您可以在ActiveRecord :: Base的所有实例上使用expire_fragment,例如User.first.expire_fragment('user-stats')

答案 2 :(得分:7)

这很容易做到。您可以实现Orion的建议,但您也可以实现下面所示的更广泛的技术,这使您可以从任何模型访问当前控制器,无论您决定打破MVC分离的目的(例如,搞乱片段缓存,访问{{ 1}},生成路径/ URL等。)

要从任何模型访问当前请求的控制器(如果有),请将以下内容添加到{{1}或者,最好是新插件(例如,创建包含下面代码的current_user):

environment.rb

然后,在vendor/plugins/controller_from_model/init.rb

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

然后,来自任何模型

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 } ... 后代,以便实际的控制器感知代码仍驻留在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 中,但它为了获得一些功能(虽然丑陋且难以维护),但并非必须这样做。

玩得开心!

答案 3 :(得分:6)

在我的一个脚本中,我使用以下hack:

  require 'action_controller/test_process'

  sweepers = [ApartmentSweeper]

  ActiveRecord::Base.observers = sweepers
  ActiveRecord::Base.instantiate_observers

  controller = ActionController::Base.new
  controller.request = ActionController::TestRequest.new
  controller.instance_eval do
    @url = ActionController::UrlRewriter.new(request, {})
  end

  sweepers.each do |sweeper|
    sweeper.instance.controller = controller
  end

然后,一旦调用了ActiveRecord回调,清扫程序就可以调用expire_fragment。

答案 4 :(得分:2)

我有点像铁路菜鸟,所以这可能不正确,甚至没有帮助,但尝试从模型中调用控制器操作似乎是错误的。

是否无法在控制器中编写执行所需操作的操作,然后从rake任务中调用控制器操作?

答案 5 :(得分:2)

为什么不让外部rake任务调用控制器上的expiry方法。然后你仍然是MVC兼容的,你不依赖于一些范围黑客等等。

就此而言,为什么不将所有守护进程/外部功能放在控制器上并让rake / cron调用它。这将是更容易维护的负载。

- MarkusQ

答案 6 :(得分:2)

将当前控制器作为参数传递给模型方法调用会不会更容易和干净?如下:

def delete_cascade(controller)

  self.categories.each do |c|
    c.delete_cascade(controller)
    controller.expire_fragment(%r{article_manager/list/#{c.id}.*})                
  end
  PtSection.delete(self.id)
  controller.expire_fragment(%r{category_manager/list/#{self.id}.*})        
end

您可以从模型中访问控制器的所有公共方法和属性。 只要你不修改控制器的状态,它应该没问题。

答案 7 :(得分:1)

这可能对您正在进行的操作无效,但您可以在模型上定义自定义回调:

class SomeModel < ActiveRecord::Base
    define_callback :after_exploded

    def explode
        ... do something that invalidates your cache ...
        callback :after_exploded
    end
end

然后你可以像往常一样使用扫地机:

class SomeModelSweeper < ActionController::Caching::Sweeper
  observe SomeModel 

    def after_exploded(model)
      ... expire your cache
    end
end

让我知道这是否有用!