简体   繁体   English

Rails 3缓存:如何使用带有Action和Fragment缓存的清除程序使缓存过期?

[英]Rails 3 caching: How do I use a sweeper with Action and Fragment caching to expire the cache?

I'm working on a page that displays a restaurant menu. 我正在处理显示餐厅菜单的页面。 I have 2 models: FoodMenu has_many :products and Product belongs_to :food_menu. 我有2个模型:FoodMenu has_many:products和Product属于to:food_menu。 I don't have controllers for either model. 我没有任何一个模型的控制器。 Instead, I am using a "pages_controller.rb" to display each FoodMenu and its Products with a "menus" action: 相反,我使用“ pages_controller.rb”来显示每个带有“菜单”操作的FoodMenu及其产品:

def menus
 @food_menus = FoodMenu.includes(:products).all
end

I want to use Action Caching for the menus page (localhost:3000/menus), which is working, but I can't get the cache to expire when I update, create, or destroy a product. 我想对菜单页面(localhost:3000 / menus)使用“动作缓存”,该菜单页面正在运行,但是当我更新,创建或销毁产品时,缓存无法过期。

At the top of "pages_controller.rb", I have: 在“ pages_controller.rb”的顶部,我有:

caches_action :menus
cache_sweeper :pages_sweeper

I tried creating separate sweepers for the Product and FoodMenu models in app/sweepers using the example code here: http://guides.rubyonrails.org/caching_with_rails.html#sweepers , but that didn't work. 我尝试使用以下示例代码在app / sweepers中为Product和FoodMenu模型创建单独的清扫程序: http ://guides.rubyonrails.org/caching_with_rails.html#sweepers,但这没有用。 Then, I read in a SO entry that the sweeper is supposed to observe all the models that the controller uses, so I assumed that meant I have to create a "pages_sweeper.rb" that observes both the Product and FoodMenu models and expires the "menus" action. 然后,我读了一个SO条目,认为清扫程序应观察控制器使用的所有模型,因此我认为这意味着我必须创建一个“ pages_sweeper.rb”,以观察Product和FoodMenu模型,并使“菜单”操作。 That didn't work either. 那也不起作用。 What am I doing wrong? 我究竟做错了什么? Here is what I have right now in "pages_sweeper.rb": 这是我现在在“ pages_sweeper.rb”中拥有的内容:

class PagesSweeper < ActionController::Caching::Sweeper
 observe Product, FoodMenu 

 # If our sweeper detects that a Product was created call this
 def after_create(product)
  expire_cache_for(product)
 end

 # If our sweeper detects that a Product was updated call this
 def after_update(product)
  expire_cache_for(product)
 end

 # If our sweeper detects that a Product was deleted call this
 def after_destroy(product)
   expire_cache_for(product)
 end

 def after_create(food_menu)
  expire_cache_for(food_menu)
 end

 # If our sweeper detects that a FoodMenu was updated call this
 def after_update(food_menu)
   expire_cache_for(food_menu)
 end

 # If our sweeper detects that a FoodMenu was deleted call this
 def after_destroy(food_menu)
   expire_cache_for(food_menu)
 end


 private
 def expire_cache_for(product)
 # Expire the menus action now that we added a new product
 expire_action(:controller => 'pages', :action => 'menus')

 # Expire a fragment
 expire_fragment('all_available_products')
 end

 def expire_cache_for(food_menu)
 # Expire the menus page now that we added a new FoodMenu
 expire_action(:controller => 'pages', :action => 'menus')

 # Expire a fragment
 expire_fragment('all_available_food_menus')
 end
end     

I finally figured it out! 我终于想通了! I was able to get both fragment and action caching to work. 我能够同时使用片段缓存和动作缓存。 From the server logs, it seems that Action Caching is a lot faster, so that's what I'm deploying with. 从服务器日志来看,动作缓存似乎要快得多,所以这就是我要部署的内容。

For Fragment Caching, I created a "food_menu_sweeper.rb" that observes both FoodMenu and Product and expires the fragment that I created in the partial, like so: 对于片段缓存,我创建了一个“ food_menu_sweeper.rb”,它同时观察了FoodMenu和Product并使我在局部中创建的片段失效,如下所示:

class FoodMenuSweeper < ActionController::Caching::Sweeper
 observe FoodMenu, Product

 def after_save(food_menu)
   expire_cache(food_menu)
 end

 def after_destroy(food_menu)
   expire_cache(food_menu)
 end

 def after_save(product)
   expire_cache(product)
 end

 def after_destroy(product)
   expire_cache(product)
 end

 private

 def expire_cache(food_menu)
  expire_fragment("menu items")
 end

 def expire_cache(product)
  expire_fragment("menu items")
 end

end

Here is the fragment in the partial: 以下是部分片段:

<% cache("menu items") do %>
  <% for food_menu in FoodMenu.full_list %> 
    ...
<% end %>
<% end %>

FoodMenu.full_list is called within the fragment to cache the database query as well, which is defined in the food_menu.rb model: 在片段内调用FoodMenu.full_list来缓存数据库查询,该查询在food_menu.rb模型中定义:

def self.full_list
  FoodMenu.includes(:products).all
end

Then, the key here is to place the cache_sweeper in "application_controller.rb"! 然后,这里的关键是将cache_sweeper放在“ application_controller.rb”中! This crucial hint came from reading this SO entry: Rails - fragment cache not expiring 这个关键提示来自阅读以下SO条目: Rails-片段缓存未到期

 cache_sweeper :food_menu_sweeper

This all works like a charm. 这一切都像一个魅力。 When I refresh the page, the fragment is read from cache and no database calls are made. 当我刷新页面时,将从高速缓存中读取该片段,并且不会进行数据库调用。 Once I update a product or a food_menu, the cache is cleared and the new data appears and is then cached. 更新产品或food_menu后,将清除缓存并显示新数据,然后将其缓存。

For Action Caching, I added: 对于动作缓存,我添加了:

caches_action :menus

in my "pages_controller.rb", then removed the fragment cache from the partial, and replaced 在我的“ pages_controller.rb”中,然后从部分缓存中删除了片段缓存,并替换了

expire_fragment("menu items")

in food_menu_sweeper.rb, with: 在food_menu_sweeper.rb中,带有:

expire_action(:controller => '/pages', :action => 'menus')

The key here was the leading slash before "pages", which I found via this SO entry: rails caching: expire_action in another namespace 这里的关键是“页面”之前的斜杠,我通过此SO条目找到了它: 轨道缓存:另一个名称空间中的expire_action

Without the leading slash, I was getting a "Can't convert symbol into integer" error when updating items via the ActiveAdmin interface. 没有前导斜线,通过ActiveAdmin界面更新项目时出现“无法将符号转换为整数”错误。

Yay for determination, Google, and Stack Overflow! 是的,决心,谷歌和堆栈溢出!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM