0

I came across 2 rather similar codes while doing Hartl's Tutorial. In Listing 8.25,the show returns an instance variable @user which is obtained by Rails's find method.

class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      log_in @user
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

In Listing 8.30, there is a similar situation in create, which uses find_by and should return an instance variable too, but this time it was defined as user instead of @user. I don't really know when to use @ and I also observe that both are controllers, so I would think the syntax should be consistent. Is there a reason for this discrepancy and in general, when are we allowed or not allowed to use @ to define instance variables?

class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      log_in user
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out
    redirect_to root_url
  end
end

A side question that I have would be, am I right to conclude that any methods defined in controllers are definitely class methods, hence the need for User.new, while in models where we need to define methods, due to the extra flexibility provided, we need to declare explicitly during implementation whether it's a class method def User.new_token or it's an instance method(def remember).

Prashin Jeevaganth
  • 1,223
  • 1
  • 18
  • 42

2 Answers2

0

In this case, defining instance variable in users#new, for example, is necessary for it to be accessible in view. Rails uses this specific mechanism to copy all the instance variables defined in controller context to view.

On the other hand, in sessions#create you only want to redirect the user if sign in is successful and display sign in form otherwise, which - as you see in sessions#new - doesn't require any instance variables defined. So local variable in this case is sufficient.

Marek Lipka
  • 50,622
  • 7
  • 87
  • 91
  • Hi there, thanks for your answer, I have commented on the alternative answer after consolidating both of your answers. Do you mind verifying the correctness of my assumption? – Prashin Jeevaganth Dec 20 '18 at 08:38
0

Mostly we need to use an instance variable in the following cases,

  1. When we need to access the variable from view
  2. If we are calling a method from the action, instead of returning a value, we can directly update the variable from the called method.

Please do not be confused user is a local variable. They only exists within its scope (current block)

@user is an instance variable - and is available to all methods within the class.

  • Hi, consolidating your post and the alternative answer provided, am I right to assume that the decision to put `@` is based on the type consistency in both of the conditional branches? I interpreted the other answer as since `sessions#create` might `render new`, and `new` does not require instance variables, `redirect_to user` should follow suit without `@`. In contrast for `UsersController`, `users#create` might `render 'new'` which definitely returns `@user`, hence we should just return `redirect_to @user` too. – Prashin Jeevaganth Dec 20 '18 at 08:36
  • Not exactly. You use `redirect_to @user` instead of `redirect_to user`, because, simply, `@user` is defined and `user` isn't (in `users#create`). And `@user` is defined because you may need it in form should the `@user.save` fail (i.e. return `false`). – Marek Lipka Dec 20 '18 at 08:40
  • @MarekLipka so is it more like we are using `@user` over `user` as `@user.save` is essential and `user.save` won't work the same way? And that `@user` is more powerful since any changes are updated to the latest, where the local variable `user` might not do for us. – Prashin Jeevaganth Dec 20 '18 at 08:48
  • `@user.save` and `user.save` works the same way if they hold the same value, so it's not the case. The only difference is visibility, `@user` is accessible in views, `user` isn't, end of story. – Marek Lipka Dec 20 '18 at 08:52
  • @MarekLipka So how do you know whether a method should allow its variable to be accessible in view? Generally is it because `User` is created directly for this purpose, while `Session` is made to support `User` and doesn't necessarily need to be seen? – Prashin Jeevaganth Dec 20 '18 at 09:10
  • It depends on what you want in view, really. If you want your view to be dependent on `User` instance, like you do in user form, you set `@user` variable so you can use it in view, like you want. That's all. – Marek Lipka Dec 20 '18 at 09:14