我几乎尝试了一切,但似乎无法使用 来自模特的expire_fragment?我知道你不应该这样做 非MVC,但肯定有一些方法可以做到这一点。
我在lib / cache_helper.rb中创建了一个包含所有过期助手的模块, 每个都只是一堆expire_fragment调用。我有我的全部 缓存清理器设置在/ app / sweepers下并有一个“包括 CacheHelper“在我的应用程序控制器中,因此缓存中的缓存 app通过控制器调用时工作正常。
然后事情是我有一些外部守护进程,特别是一些 重复的cron任务,它调用一个调用某个特定的rake任务 方法。此方法执行一些处理并将条目输入到 模型,之后我需要使缓存过期。
这是最好的方法,因为我无法在模型中指定缓存清理程序。 直观的观察者似乎是最好的解决方案,但接下来 抱怨expire_fragment未定义等等,我甚至 尝试将ActionController缓存类包含在观察者中 但那没用。我喜欢有关如何创建解决方案的一些想法 为了这。谢谢。
答案 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
让我知道这是否有用!