Matthew Stopa
Matthew Stopa

Reputation: 3875

How can I pass objects from one controller to another in rails?

I have been trying to get my head around render_to but I haven't had much success.

Essentially I have controller methods:

def first
  #I want to get the value of VAR1 here
end

def second
  VAR1 = ["Hello", "Goodbye"]
  render_to ??
end

What I can't figure out is how to accomplish that. Originally I just wanted to render the first.html.erb file but that didn't seem to work either.

Thanks

Edit: I appreciate the answers I have received, however all of them tend to avoid using the render method or redirect_to. Is it basically the case then that a you cannot pass variables from controller to controller? I have to think that there is some way but I can't seem to find it.

Upvotes: 8

Views: 18400

Answers (4)

praaveen V R
praaveen V R

Reputation: 1261

  1. way 1
    Global variable (fail during concurrent requests)
  2. way 2
    class variable (fail during concurrent requests)
  3. way 3

    • Stash the object on the server between requests. The typical way is to save it in the session, since it automatically serializes/deserializes the object for you.
    • Serialize the object and include it in the form somewhere, and deserialize it from the parameters in the next request. so you can store attributes in the session.

      def first
      @item = Item.new(params[:item])
      session[:item_attributes] = @item.attributes
      end
      
      def second
      @item = Item.new(session[:item_attributes])
      @item.attributes = params[:item]
      end
      
  4. way 4
    The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed to the very next action and then cleared out.
     def new
       @test_suite_run = TestSuiteRun.new
       @tests = Test.find(:all, :conditions => { :test_suite_id => params[:number] })
       flash[:someval] = params[:number]
     end 
     def create
    @test_suite_run = TestSuiteRun.new(params[:test_suite_run]) @tests = Test.find(:all, :conditions => { :test_suite_id => flash[:someval] })
    end
  5. way 5
    you can use rails cache.

      Rails.cache.write("list",[1,2,3])
      Rails.cache.read("list")
    
    But what happens when different sessions have different values? Unless you ensure the uniqueness of the list name across the session this solution will fail during concurrent requests

  6. way 6
    In one action store the value in db table based on the session id and other action can retrieve it from db based on session id.

  7. way 7

    class BarsController < UsersController
    before_filter :init_foo_list

    def method1 render :method2 end

    def method2 @foo_list.each do | item| # do something end end

    def init_foo_list @foo_list ||= ['Money', 'Animals', 'Ummagumma'] end end

  8. way 8
    From action sent to view and again from view sent to other actions in controller.

Upvotes: 2

Judson
Judson

Reputation: 764

Have you considered using the flash hash? A lot of people use it solely for error messages and the like, it's explicitly for the sort of transient data passing you might be interested in.

Basically, the flash method returns a hash. Any value you assign to a key in the hash will be available to the next action, but then it's gone. So:

def first
  flash[:var] = ["hello", "goodbye"]
  redirect_to :action => :second
end

def second
  @hello = flash[:var].first
end

Upvotes: 2

ryanb
ryanb

Reputation: 16287

It is not a good idea to assign the object to a constant. True this is in a global space, but it is global for everyone so any other user going to this request will get this object. There are a few solutions to this.

I am assuming you have a multi-step form you are going through. In that case you can pass the set attributes as hidden fields.

<%= f.hidden_field :name %>

If there are a lot of fields this can be tedious so you may want to loop through the params[...] hash or column_names method to determine which attributes to pass.

Alternatively you can store attributes in the session.

def first
  @item = Item.new(params[:item])
  session[:item_attributes] = @item.attributes
end

def second
  @item = Item.new(session[:item_attributes])
  @item.attributes = params[:item]
end

Thirdly, as Paul Keeble mentioned you can save the model to the database but mark it as incomplete. You may want to use a state machine for this.

Finally, you may want to take a look at the Acts As Wizard plugin.

Upvotes: 23

Andy Gaskell
Andy Gaskell

Reputation: 31761

I usually don't have my controllers calling each other's actions. If you have an identifier that starts with a capital letter, in Ruby that is a constant. If you want to an instance level variable, have it start with @.

@var1 = ["Hello", "Goodbye"]

Can you explain what your goal is?

Upvotes: 2

Related Questions