Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding Rails Instance Variables

In my application I have a products_controller that makes use of instance variables. My understanding of instance variables in Ruby is that you can use them within different methods of the same class. So why is it that we use the same instance variable in multiple methods of rails apps? Below we have an instance variable @product set twice, is the @product variable in the new action not overwritten when we use it in the create action?

I am just a little confused as to the scope of these variables within methods of the same class.

  def new
    @product = Product.new
  end

  def create
    @product = Product.new(product_params)

    respond_to do |format|
      if @product.save
        format.html { redirect_to @product, notice: 'Product was successfully created.' }
        format.json { render :show, status: :created, location: @product }
      else
        format.html { render :new }
        format.json { render json: @product.errors, status: :unprocessable_entity }
      end
    end
  end
like image 242
adamscott Avatar asked Dec 07 '22 22:12

adamscott


2 Answers

Instance variables are in the instance class scope. In Ruby on Rails, because the way how that API was built, instance variables are also available in the views.

You need to be aware of that new and create methods are commonly used in different ProductsController instances.

First request: GET http://localhost:3000/product/new

When you ask for the new action (I suppose that is a form), Rails API implementation at a given point creates an instance of ProductsController and sends the new message to that instance (calls the new method). Then, the instance variable @product is created and available in any method, or in any view that the action renders. At a given point, Rails replies with a web page and the class instance, and all its instance variables, are destroyed (won't be available anymore).

Second request: POST http://localhost:3000/product/create

When you submit the form for database persistence, again a new controller instance is created, and the create method is called. Because is a new instance, the @product doesn't have any value.

Note, however, that there is a difference between rendering a view (like its happening in the new action) and a redirect (like you do in the create action if @product.save is true). When you render, you remain in the same controller instance, with you redirect, new server request happens, so the previous controller instance is destroyed and a new controller instance is created.

The before action

before_action is called before you actually start executing the action code. In Rails perspective, an action is not a Ruby method. The class method is the definition of that action:

From Rails guides:

A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.

The action acts as an entry point determined by the routes. If you call create inside new, it won't trigger that before_action again.

like image 197
Paulo Abreu Avatar answered Dec 25 '22 02:12

Paulo Abreu


No, it doesn't overwrite it. An instance variable (@variable_name) is accessible within all methods of a single instance object of a class.

Now imagine, there's a client request to the "new product route". Rails creates an instance object of your products_controller and invokes only the new action of that instance. That defines @product = Product.new, renders your new.html.erb template and that's it. After that, the controller instance will be forgotten.

Next, your client clicks the "create product button" of your "new product form". Another request arrives the server. Rails creates another instance of your products_controller and calls the create action. The new action isn't invoked. And so, you have a new product instance (@product = Product.new(product_params)) with the attributes sent by the form.

Of course, you could call the create method from your new action ...

# only an example
def new
  @product = Product.new
  create
end

... or the other way round. This would overwrite the @product variable. But why should you do that?

like image 32
guitarman Avatar answered Dec 25 '22 02:12

guitarman