views:

284

answers:

5

Is there a way to cache per-request data in Rails? For a given Rails/mongrel request I have the result of a semi-expensive operation that I'd like to access several times later in that request. Is there a hash where I can store and access such data?

It needs to be fairly global and accessible from views, controllers, and libs, like Rails.cache and I18n are.

I'm ok doing some monkey-patching if that's what it takes.

  • Memcached doesn't work because it'll be shared across requests, which I don't want.
  • A global variable similarly doesn't work because different requests would share the same data, which isn't what I want.
  • Instance variables don't work because I want to access the data from inside different classes.
A: 

Global variables are evil. Work out how to cleanly pass the data you want to where you want to use it.

womble
That's not always practical. Like I mentioned, Rails has globalish objects like Rails.cache and I18n.
Wayne Kao
A: 

Have you considered flash? It uses Session but is automatically cleared.

Andrew Peters
A: 

app/models/my_cacher.rb

class MyCacher
  def self.result
    @@result ||= begin
      # do expensive stuff
      # and cache in @@result
    end
  end
end

The ||= syntax basically means "do the following if @@result is nil" (i.e. not set to anything yet). Just make sure the last line in the begin/end block is returning the result.

Then in your views/models/whatever you would just reference the function when you need it:

MyCacher.result

This will cache the expensive action for the duration of a request.

CaffeineFueled
This will be cached for the lifetime of the app instance (many requests) in production mode.
cwninja
+1  A: 

Memoisation?

According to this railscast it's stored per request.

wombleton
Memoisation on the class method will cache for all time. Memoisation on an instance will cache for the lifetime of that instance. Excessive caching on classes clogs up memory. I've taken down production boxes making this mistake.
cwninja
A: 

Try PerRequestCache. I stole the design from the SQL Query Cache.

Configure it up in config/environment.rb with:

config.middleware.use PerRequestCache

then use it with:

PerRequestCache.fetch(:foo_cache){ some_expensive_foo }
cwninja