How do I store an instance variable across multiple actions in a controller? - ruby-on-rails

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.

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)

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.

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.

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?

Don't worry, sessions won't bite.
Also, the session is probably the best way to do this.

Related

Updating and accessing a session with Ruby on Rails

I'm working with ruby on rails 2.5.
I have an object "payment_plan". This object can change with a toggle behavior that changes, and I need to keep this object alive thorough all the session and at the end it should be save par of it in my mongo db. I need to access the latest status of the object always. The controller should be capable to update the object and the view should be able to access the latest state of the object.
Any insights on how to do something like this would be great :)
I have try to create a helper function in the application controller but had problem accessing it from the view.
Also I prefer not to save the state of the object in the db, because it will be too many db calls later.
To access a controller helper function from view, define it as a helper:
class SomeController < ApplicationController
helper def some_helper
end
end
As for storing some data in session - it's ok, rails has nice session store mechanism for session[:my_object_prop] = 1/session[:my_object_prop] (see in official guide)
But keep in mind, that:
by default session is stored in cookies, which are passed in headers from client browser with every request to your server (even to images, if they are on the same domain), so it's only practical to save small amounts of data there.
user can clear their's cookies and data will be lost (this is usually fine)
the opposite of the latter - a user may come to your app with session data from old version of your code
thank you very much, that was very helpfull!
Another question, can I override a session value after setting it up?
for example session[:plan_id]="plan_id_1" and they further on in the run do something like this: session[:plan_id]="plan_id_2"
thanks!!!

Instance variable in controller with Ruby On Rails

When someone is logging into my application, I use:
def create
#user = User.authenticate(params[:email], params[:password])
[...]
end
Ok, then, when someone is logging out:
def destroy
user = User.find_by_id(session[:user_id])
[...]
end
Knowledge
As far as I know, variable scopes work based on a scope, at least on Ruby (on Rails).
Our friend said:
In case of controllers, it present for that HTTP request alone, the object and the instance variables.
Ok. My variable scope created on create method is useless for destroy method, but I was thinking about the subject and the following question appears: There's a way to preserve #user for that controller at all, regardless of the HTTP request?
I mean, # in this case seems useless to me because its not flexible. I don't know, just sounds strange for me I can't reuse it when I want to.
That's how the web works and why http is a 'stateless protocol'. You must understand that you are not starting to run a program and stop it when your user logs out. But you 'restart' the program for every single request. It's a new instance, a new process that knows nothing of the last one and for sure shares no memory with it. Actually the Rails instance that handles the create and the one that handles the destroy could easily run on two physically different servers!
There is no state (but what you put in the session storage or the URL params). # in this case means that your view can use this data (which in the Ruby context means that Rails already is doing some tricks to get it handed over there, since these are two different classes and the view would otherwise not know anything about the controllers instance variables).

Rails - Expiring Cached Actions from Resque (a Delayed Job alternative)

I have Resque queue that processes a particular model to do some post processing. Once this model is updated I would like to expire the cached view action.
I have a Sweeper setup and working, but it only observes changes made to the models in Controller Actions.
I know it is not very MVC to be expiring cached items from my model, but all my post processing logic is contained in my models.
Ideally I would like to not double up on my cache busting code, so if I could get my existing sweeper to watch model changes that would be ideal.
Alternatively I would settle for expriing the action cache from inside my model OR a model observer.
p.s.: I can expire cached fragments from within a model observer I have setup, but not actions.
I'd really like to know what the best practice is here. I'm sure I am not the only one who has this requirment.
Thanks.
I'm wondering if something like this is the way to go:
http://dev.mensfeld.pl/2011/07/rails-wykorzystywanie-sweeperow-poza-kontrolerami-na-samych-modelach/
Since I wasn't able to add my comment on that site you linked to. I'll put it here:
In Rails 3.1.3, if you instantiate the controller and then try calling expire_fragment, you'll get errors about trying to call host on NilClass.
After some experimenting, I remembered that functional tests can instantiate your controller. So I changed the instantiation code to:
#controller ||= ApplicationController.new
if #controller.request.nil?
#controller.request = ActionDispatch::TestRequest.new
end
This seems to work, even in production, even using rails console.
In the end I came up with the following solution:
Added my existing sweeper to the list of object observers in application.rb:
config.active_record.observers = :app_sweeper
Added code to the sweeper methods to instantiate the #controller object if it was missing in the sweeper (which is what happens when coming via a object instead of a controller).
#controller ||= ActionController::Base.new
I could then use the expire_fragment method to expire the cached action, with the following tidbit.
expire_fragment("#{ActionMailer::Base.default_url_options[:host]}/items/#{item.id}")
That said, mj1531's answer might prove to be a nicer solution if it means that I can use the expire_action method instead of faking it with the expire fragment. I will report back when I have test it out and select the best answer.

How to persist data about what page you are on in Rails app

This will probably be easiest if I explain what I'm trying to do. I have three actions in my Rails app controller, each rendering a different page. The page-render is done with a single partial which uses variables that were set in the controller action code. For example, each page has a list on it, but on one page the list is sortable. Up to now I've been handling this by setting a #sortable flag to true or false in the code for my actions.
This works fine when an action is initially run. The problem is that I have AJAX stuff going on (e.g. adding a new element to the list) and when this happens, I need to know the value of the #sortable variable again. It seems to have gone, even though I'm still technically on the same page. What I want is a variable store that is linked to the page you are on.
What are your recommendations for doing this? (Storing it in the Rails session hash seems like overkill - too much chance that the wrong value will get left in there by some yet-to-be-implemented action.)
Ben
In rails I've only managed to set page scoped variables for initial setup too.
I think the only solution would be to pass the sortable flag from the page on the ajax request. You can store it either with a javascript variable, in a hidden field, custom attribute on your list or anyway you wish and then in the ajax you simply add that to the request so you can treat that on the server side persistently.
Why do you don't want use session? As for me before_filter works fine for such tasks
in ApplicationController
before_filter :init_actions
def init_actions
session[:action] = action_name
session[:controller] = controller_name
end

ruby on rails way to destroy extra session variables in a model?

I have additional variables I add to an authlogic users session like:
session[:current_profile] = extra_id
I currently destroy these on logout in a controller like:
session[:current_profile] = nil
I'd like to clean this up and destroy them in the session model in the after_destroy method like:
def after_destroy
session[:current_profile] = nil
end
This session method doesn't seem to be callable from the models though. Any idea how to destroy a session variable from a model?
Thanks!
You're really not supposed to alter things in the Controller space from the Model space, which is to say, a Model should not be controlling a Controller. Models should be able to run independently of a controller, such as in unit tests where no controller is present.
While you might be able to wrangle this sort of thing with an Observer, I don't know of an easy way to do this. It's probably better to have the controller perform all the required actions directly.
If you put an after_destroy hook like this in you will have some serious side-effects if, for example, a user logged in as an admin destroys another user account and then their session profile suddenly disappears.

Resources