views:

314

answers:

5

Say I want to store some variable in my controller. I want to initialize it in one action, increment it in another, and read it in yet another. Just declaring this variable with @foo doesn't work because @foo dies after the action that created it is rendered.
I do not want this variable to be stored in a model.

Is there a way to preserve this variable besides storing it in a session?
It seems like I've run into this simple problem a few times, and I want to know the best way to go about solving it.

+6  A: 

Not really. Each call to a controller action is stateless. Nothing is available after the controller action finishes. A new controller instance is created for each request, and then discarded at the end of the request.

If you don't want to store it in the session, or database model, you don't have many options if you're wanting that variable to be specific to a particular session.

If it is global across all sessions, you could put it in a @@class_variable rather than an @instance_variable, but that can get messy once you start having multiple Rails processes (each which will have their own copy of it), or if you're running in threadsafe mode, you can end up with nasty concurrency bugs.

I guess you could look at something like memcached, but you'd still need to key that to some user_id or other session marker (unless it's global)

madlep
A: 

You could make use of the built in Rails.cache mechanism to store the value but as mentioned in the first answer you'd have to key it off something like the user_id. This is a nice way to go since you can back it with different storage mechanisms.

Rails.cache.write(:foo)
# in later action
Rails.cache.read(:foo)

One other thing you could look at is the flash hash, which provides a keep method to make the flash value last more than one subsequent request.

So in action 1 you could create the value:

flash[:foo] = some_value
flash.keep(:foo)

In action 2 you can access it, and call keep again if you want it to stay alive for more subsequent actions.

flash[:foo] #use it for something
flash.keep(:foo) # keep it for another request

It's a bit of a tricky thing to do cleanly within the context of http requests.

paulthenerd
Cache is a good idea, but I don't think the flash method will work if you have multiple web servers.
Scott
It depends on your session storage because the flash hash is actually stored in the session - for instance if sessions are disabled only flash.now will work
paulthenerd
Cache is is an alright idea. But if it is using the MemoryStore, it's per process memory, so unless you have a single process that handles all requests, you subsequent requests may not hit the process that has it in memory. If the memory store is backed by memcached, it would be much better.
BigCanOfTuna
A: 

If it's a simple count or string, I think the best solution is to store it in the session. That way it will be there if you are using multiple web servers.

Why are you against using a session for this?

Scott
I'm pretty new to Rails, and I just assumed that the session was for *Important Things* like a user ID, not something as trivial as a counter. Thanks.
Sam P
A: 

I too am wondering why you are against using session? If you don't like working with session directly in your actions, you could emulate a surviving @foo instance variable with filters. Something like this maybe?

class FooController < ApplicationController
  before_filter :load_foo
  after_filter :save_foo

  private
    def load_foo
      @foo = session[:foo] || 0
    end

    def save_foo
      session[:foo] = @foo
    end

end

Your actions will the be able to manipulate the value through the @count instance variable and this will be automatically persisted to session.

Daniel Kristensen
A: 

Don't worry, sessions won't bite.

Also, the session is probably the best way to do this.

epogue