Search code examples
ruby-on-railsmodel-view-controller

Passing a variable between controller function in rails


I'm learning rails (and ruby) and I'd like to know if it is possible to pass a variable between controller functions.

Basically, my problem could be resumed like this:

#controller.rb

def pageA
    #assuming @variable is from a form
    @variable = "foo"

    if @variable == "foo"
        redirect_to action: 'pageB'
    else
        ...
    end

end

def pageB
    #I'd like to reuse @variable in pageB.html.erb
end

Is it possible using redirect_to ? Should I take another approach ?

Out of curiosity, is this a problem that can be resolve using a cache ?


Solution

  • You're thinking about this at too low of a level.

    Variables are just a tool that you use to point to data in memory when processing a request and do not persist reliably between one request to another. They are never a valid part of an end goal.

    Instance variables do not persist between requests as the requests are not processed by the same controller instance.

    Rather you need to think of it in terms of passing state (or references to it) back and forth between the server and client. HTTP provides several mechanisms for this such as the URI, request body and headers.

    Data that needs to be persisted between requests on the server needs to be stored somewhere such as a database. In that case we don't need to pass the actual data. We just need to tell the server what we are looking for.

    In the typical Rails REST setup state (in the form of resources) are saved in the database and then you just redirect to that resource.

    class ThingController < ApplicationController
      # Displays a form to create a new resource
      # GET /things/new
      def new
        @thing = Things.new
      end
    
      # Responds to form submissions
      # POST /things
      def create
        @thing = Thing.new(thing_params)
        if @thing.save
          redirect_to @thing # redirect to /things/:id
        else
          render :new        # renders the form again
        end
      end
    
      # Shows a member of the collection
      # GET /things/1
      def show
        @thing = Thing.find(params[:id])
      end
    
      private
    
      def thing_params
        params.require(:thing)
              .permit(:foo, :bar)
      end
    end
    

    Here we just pass the id of the record as part of the URL and can use that in the next request to fetch the newly created record.

    Note that a typical Rails noob trap is to try to use a redirect for "failed" form submissions:

    def create
      @thing = Thing.new(thing_params)
      @my_var= 'Hello World'
    
      if @thing.save
        redirect_to @thing, my_param: @my_var # redirect to /things/:id
      else
        redirect_to action: :new              # OMG How do I pass all the data along?
      end
    end
    

    If the user tries to create/update a resource with invalid data just render the form again and send it as a response instead.

    If you really need to pass additional data from the server along with a redirect you can use query string parameters:

    # Responds to form submissions
    # POST /things
    def create
      @thing = Things.new(thing_params)
      @my_var = 'Hello World'
      if @thing.save
        redirect_to @thing, my_param: @my_var # redirect to /things/:id
      else
        render :new                           # renders the form again
      end
    end
    
    # Shows a member of the collection
    # GET /things/1
    def show
      @my_var = params[:my_param]
      @thing = Thing.find(params[:id])
    end
    

    Any additional options passed to redirect_to (with the exception of named options like status, notice, alert) will be added to the URL as query string parameters. While there certainly are valid use cases ask yourself if you really need it first and take safety into consideration.

    If you need a more transparent mechanism to pass state to a browser and have it send it back cookies can be used. Rails provides the session mechanism that can be used to store data in cookies in a higher level way.

    Out of curiosity, is this a problem that can be resolve using a cache ?

    Not really.

    A cache is a way to improve performance by storing data that is expensive to produce. Any cache that is user specific still needs a mechanism to identify the user.