Caching for layout variables

To make a long question short: I'm fairly new to caching in Rails. What's the best way to cache stuff that is used in the layout without caching the entire action/view?

Lets say I have the following in my Application controller to set some variables that are used in my layout:

  before_filter :get_sidebar_items

  def get_sidebar_items     @menu_items = MenuItem.all     @archives = Post.all_grouped_by_month   end

I can easily cache the display of the menu items in application.html.erb using a fragment:

<% cache(:menu_items) do %>   <% for i in @menu_items %>     <%= i.name %>   <% end %> <% end %>

Problem is, that doesn't cache the database query. Is there a nifty way to cache some global stuff like this? As far as I can tell, the Caching Guide doesn't touch on this much.

Tim Shaffer wrote in post #967005:

To make a long question short: I'm fairly new to caching in Rails. What's the best way to cache stuff that is used in the layout without caching the entire action/view?

Lets say I have the following in my Application controller to set some variables that are used in my layout:

  before_filter :get_sidebar_items

  def get_sidebar_items     @menu_items = MenuItem.all     @archives = Post.all_grouped_by_month   end

I can easily cache the display of the menu items in application.html.erb using a fragment:

<% cache(:menu_items) do %>   <% for i in @menu_items %>     <%= i.name %>   <% end %> <% end %>

Problem is, that doesn't cache the database query. Is there a nifty way to cache some global stuff like this? As far as I can tell, the Caching Guide doesn't touch on this much.

Look up the RDoc for ActiveSupport::Cache::Store. You'll need to use the interface described there (at least in Rails 2).

Best,

Thanks. Turned out to be way simple...

@menu_items = Rails.cache.fetch(:menu_items_all) { MenuItem.all }

And it's just as easy to delete it...

Rails.cache.delete(:menu_items_all)

> Look up the RDoc for ActiveSupport::Cache::Store. You'll need to use > the interface described there (at least in Rails 2).

Thanks. Turned out to be way simple...

@menu_items = Rails.cache.fetch(:menu_items_all) { MenuItem.all }

Although this is sort of wasteful if you're also fragment caching in the view (wasteful in that you put data in the cache that you don't use, and wasteful of one roundtrip to your memcache). There isn't really a great way to say in rails "this code here only exists to support this fragment in the view, so don't bother executing it if you're just going to display the fragment from the cache anyway". In some cases wrapping the database access up in a helper can work, but calling models straight from your helpers isn't particularly nice. Another solution is the interlock plugin

although I haven't used that myself

Fred

Frederick Cheung wrote in post #967208:

> Look up the RDoc for ActiveSupport::Cache::Store. You'll need to use > the interface described there (at least in Rails 2).

Thanks. Turned out to be way simple...

@menu_items = Rails.cache.fetch(:menu_items_all) { MenuItem.all }

Although this is sort of wasteful if you're also fragment caching in the view (wasteful in that you put data in the cache that you don't use, and wasteful of one roundtrip to your memcache).

But Memcached round trips are pretty cheap, since everything is in memory.

There isn't really a great way to say in rails "this code here only exists to support this fragment in the view, so don't bother executing it if you're just going to display the fragment from the cache anyway". In some cases wrapping the database access up in a helper can work, but calling models straight from your helpers isn't particularly nice.

I think DB access in a helper breaks MVC in most cases, because the helper is really part of the view layer.

Concerns like this are contributing to a growing feeling on my part that Rails-style MVC may be a mistake for certain types of complex Web applications. The question is whether something better exists... :slight_smile:

Note also that I am *not* advocating breaking MVC while using Rails.

Another solution is the interlock plugin http://blog.evanweaver.com/files/doc/fauna/interlock/files/README.html although I haven't used that myself

Nor have I. The Cells gem (which I also haven't used) might be useful too.

Fred

Best,

Agreed. I actually took the fragment cache out when I implemented the Rails.cache.fetch bit.

The database roundtrip/query/parse is definitely more intensive than just looping through the results in the view.