Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I store an instance variable across multiple actions in a controller?

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.

like image 323
Sam P Avatar asked Jul 22 '09 20:07

Sam P


4 Answers

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.

like image 25
Daniel Kristensen Avatar answered Nov 03 '22 17:11

Daniel Kristensen


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)

like image 181
madlep Avatar answered Nov 03 '22 17:11

madlep


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.

like image 7
paulthenerd Avatar answered Nov 03 '22 16:11

paulthenerd


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?

like image 1
Scott Avatar answered Nov 03 '22 18:11

Scott