How to Store an Instance Variable Across Multiple Actions in a Controller

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

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)

Same instance variable for all actions of a controller

Unless you're rendering show.html.erb from the index action, you'll need to set @some_instance_variable in the show action as well. When a controller action is invoked, it calls the matching method -- so the contents of your index method will not be called when using the show action.

If you need @some_instance_variable set to the same thing in both the index and show actions, the correct way would be to define another method, called by both index and show, that sets the instance variable.

def index
set_up_instance_variable
end

def show
set_up_instance_variable
end

private

def set_up_instance_variable
@some_instance_variable = foo
end

Making the set_up_instance_variable method private prevents it from being called as a controller action if you have wildcard routes (i.e., match ':controller(/:action(/:id(.:format)))')

Rails: Set a common or global instance variable across several controller actions

Firstly, you don't want to try to insert code "between" a controller action and a template rendering. Why? Because you want the controller action to have the freedom to choose what sort of response to give. It could return XML, JSON, headers only, a redirection, nothing, etc. That's why after filters are executed after the response has been rendered.

Secondly, you don't want to monkey patch Fixnum. I mean, maybe you do, but I don't. Not often at least, and not unless I get some totally wicked semantic benefits from it, like being able to say 3.blind_mice. Monkey patching it for a random use case like this seems like a maintenance headache down the road.

You mention refactoring out all the controllers' case specific code into a before filter and running them sequentially. Which brings up to my mind... @foo is the same in every case? If that's the case, then one before filter would work just fine:

before_filter :do_common_stuff
def do_common_stuff
@foo = common_foo
@bar = do_something_with @foo
end

That's a totally legit approach. But if @foo changes from controller to controller... well, you have a few more options.

You can separate your before filters into two halves, and customize one per controller.

# application_controller:
before_filter :get_foo, :do_something_common
def do_something_common
@bar = do_something_with @foo
end

# baz_controller:
def get_foo
@foo = pull_from_mouth
end

#baf_controller:
def get_foo
@foo = pull_from_ear
end

But you know, if it's a simple case that doesn't need database access or network access or anything like that... which your case doesn't... don't kill yourself. And don't sweat it. Throw it in a helper. That's what they're there for, to help. You're basically just rearranging some view data into a form slightly easier to use anyway. A helper is my vote. And you can just name it next_url. :)

Pass instance variable between controllers using a helper

I found a way of making the contact_id accesible to the EngagementsController without having to pass variables from ContactsController to EngagementsController via a helper. The nested resources:

  resources :contacts do
resources :engagements
end

generates the path:

POST   /contacts/:contact_id/engagements(.:format)  engagements#create

Thus the contact_id of the engagement is made accessible to the EngagementsController:

def create
@contact = Contact.find(params[:contact_id])
@engagement = @contact.engagements.build(engagement_params)
end

make variables accessible to multiple actions in controller

Just define them as constant:

class MyController < AplicationController

SETUP_STAGES = [:data_entry_completed, :data_validation_completed]

is it possible to use a variable value across multiple actions

is there any way i can store the @variable value when either index or show is called and when next time other action is called just use the same @variable value and not hit database again?

No, there is no way. When the other action is called, it's a completely different instance of your controller class. And that instance variable from last time, it's long gone.

That aside, you should do what Sebastian suggests. It's a logical continuation of what you started (so that, at least, you don't hit the DB twice in the same action).


We can't achieve that using class instance variables too?

Using class-level data, this is more achievable, but only if you have one web process (so that all requests are guaranteed to use the same class). As soon as you add another web worker, this stops working.

This question made me look at your code again, and I realized that this all is pointless/misguided. :)

 @variable = SomeModel.includes(:some_other_model)

This line does not hit database. So it doesn't make sense to try and memoize it. Saves nothing. THIS is what hits the database

  @variable = SomeModel.includes(:some_other_model).find_by!(id: params[:id])
^^^^^^^^^^^^^^^^^^^^^^^^^

rails pass params or variable to multiple controller actions

Use a session variable, that's what they're there for...

session[:prid] = params[:prid]

You can even set up a method in your ApplicationController to give you the principal when you need it...

class ApplicationController < ActionController::Base
def current_principal
principal.find_by(id: session[:prid])
end
end

Now just use current_principal where you need it. (It'll be nil if you haven't selected one yet)

Passing instance variable from one form to the a different controller's action in Rails

You'll have two methods on your controller. One for each form (rendered by the associated template). The first form should post to the second action. The second action can then transfer the request parameters into instance variables, to be available within the second template.

class FooController
def bar
# setup instance variables and render first form
end

def baz
@bar_values = params[:bar]
# setup other instance variables and render second form
end
end

UPDATE0 Do it across two controllers using session.

class FooController
def new_baz
# setup instance variables and render the first form
end

def create_baz
# respond to posting of form data
session[:current_baz_values] = params
redirect_to :action => "baq", :controller => "bar"
end
end

class BarController
def baq
@baz_values = session[:current_baz_values]
# setup other instance variables and render the second form
end
end


Related Topics



Leave a reply



Submit