#89
Jan 20, 2008

Page Caching

Page caching is an efficient way to cache stateless content. In this episode I will show you how to cache the dynamic javascript we created last week.
Download (21.9 MB, 6:31)
alternative download for iPod & Apple TV (11.7 MB, 6:31)

Resources

# javascripts_controller.rb
caches_page :dynamic_states

# config/environments/development.rb
config.action_controller.perform_caching = true

# config/environment.rb
config.load_paths << "#{RAILS_ROOT}/app/sweepers"

# app/sweepers/state_sweeper.rb
class StateSweeper < ActionController::Caching::Sweeper
  observe State
  
  def after_save(state)
    expire_cache(state)
  end
  
  def after_destroy(state)
    expire_cache(state)
  end
  
  def expire_cache(state)
    expire_page :controller => 'javascripts', :action => 'dynamic_states', :format => 'js'
  end
end

# states_controller.rb
cache_sweeper :state_sweeper, :only => [:create, :update, :destroy]

20 comments

pnowak Jan 20, 2008 at 23:50

pretty nifty piece of code, thanks.


AdulteratedJedi Jan 21, 2008 at 01:19

maybe you should release a TM Bundle with all your nifty snippets in :-)


AJ Jan 21, 2008 at 01:42

This episode is not showing in the "Full" podcast, its at episode 88. However, the iPod feed is current.


Zigzo Links Jan 21, 2008 at 10:53

Ryan, this is something i have been very interested in lately. thanks.


Mike Cleaner Jan 22, 2008 at 12:25

Ryan, your 'casts always get me revved up for a long day of refactoring. You don't talk down to your audience and you don't have a huge rub-ego, and that keeps me coming back for more. I even bought a couple Peepcodes! Well done sir, and keep them coming.


Peter D Jan 23, 2008 at 05:20

This may be a really dumb question but since the sweeper observes the State model, why is it necessary to also declare the cache_sweeper in the controller?


Garrett Heaver Jan 24, 2008 at 23:50

Peter D, I was wondering the same thing? Are we missing something?


Bounga Jan 25, 2008 at 01:09

Peter D and Garrett Heaver:

I don't necesseraly wants to do the sweeping job in every controller. It's a matter of features and needs.

So if you want to enable the sweeping jog, you have to tell it in the given controller using cache_sweeper.


Ben Jan 26, 2008 at 23:52

Please post the sweeper snippet!


Ryan Bates Jan 27, 2008 at 22:30

@Peter D, that's a really good question. I'm not entirely sure why a sweeper behaves differently than an observer. It even has the observe line. My guess is it has something to do with the scope in which it is called. It needs access to the request object which is available to the controller.

@Ben, you can find the snippet here: http://pastie.caboo.se/144239

Maybe someday I'll release a TM bundle with all my snippets, but right now they aren't organized well enough.


Philip (flip) Kromer Jan 29, 2008 at 16:18

This is, as usual, quite awesome.

I don't like having the cached files appear in public/ -- it makes life hard for doing svn (a whole bunch of spurious cached files appear there). I also don't like the idea of setting public/ world writeable (or even having world-writeable files in the cache) on the production end.

After some effort, I got all this working under nginx and rails 2.0; see instructions here if you're interested:
http://pastie.caboo.se/145116


Marcus Derencius Feb 11, 2008 at 13:37

This screencast and the before are great.

I just want to share a tip for a small caching challenge that I had.

usually I set the cache directory to "public/cache", but this "break" the caching of dynamic javascripts.

A solution that I found is to redefine @page_cache_directory on javascripts_controller

Check it here: http://pastie.caboo.se/150570

Hope it may be useful.

Marcus


francisoud Mar 03, 2008 at 08:22

I have a problem when trying to use this tutorial :(

I get this error message :
uninitialized constant StateSweeper::State

RAILS_ROOT: C:/developement/ruby/minotaur
c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:478:in `const_missing'
app/sweepers/state_sweeper.rb:3
-e:2:in `load'
-e:2

I double checked and I don't see what I forgot :(

Sweeping doc is not really helpful either:
http://rails.rubyonrails.com/classes/ActionController/Caching/Sweeping.html

any idea ?


francisoud Mar 03, 2008 at 08:33

Ok never mind I got it.

We need to have an object "State" in models/state.rb and I don't have such an object in my project.

The pastie for textmate helped me to understand http://pastie.caboo.se/144239 ;)


partydrone Apr 11, 2008 at 08:39

How would I do this for a RESTful resource? Specifically, would I just put the path in the expire_page command?

def expire_cache(article)
expire_page article_path(article)
end


partydrone Apr 11, 2008 at 08:44

Another question:

You don't specifically add the "caches_page" command to the states controller, just the "cache_sweeper" command. Is that in place of the "caches_page" command?


kino May 04, 2008 at 03:29

Ryan, this is something i have been very interested in lately. thanks.


eddie May 06, 2008 at 07:18

Is it possible to observe more than one model in a sweeper and have different events carried out depending on which model changes?

For example:

class StateSweeper < ActionController::Caching::Sweeper
observe State, Foo

def after_save(state)
expire_cache(state)
end

def before_save(foo)
if foo.someparameter > 1
expire_cache(state)
end
end

def expire_cache(state)
expire_page :controller => 'javascripts', :action => 'dynamic_states', :format => 'js'
end
end

I've tried doing something like the above but i doesn't work - I can't figure out how to differentiate between which model changes, a State model or Foo model - the before_save and after_save functions are triggered whether the model being changed is a State or a Foo ...
I could probable use instanceof? inside each action, but this seems a little messy.


Webdesigner May 16, 2008 at 02:11

Good job! Thanks for the screencast. Has definitely saved me a few hours of work...


Matt Jul 30, 2008 at 10:13

@eddie, I would say that it is possible, the parameter passed to the active record observer methods (after_save and after_destroy) is the record which can be tested to check what it is, check out the api documentation, it has an example using two models: http://api.rubyonrails.com/classes/ActionController/Caching/Sweeping.html

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(required)

subscribe:
sponsored by:
if you want to help:
required:
Get Quicktime Player