4

I am going through the great Michael Hartl tutorial to build ruby app here.

I am trying to understand the concept of how to create a session and I am stuck in understanding this line:

self.current_user = user

in this method:

module SessionsHelper

  def sign_in(user)
    cookies.permanent[:remember_token] = user.remember_token
    self.current_user = user
  end
end

I understand the whole concept of creating a cookie with the user_token.

But I don't understand what does self.current_user = user means and why is it even necessary to keep this line of code - I have the cookie with the token - why do I need to know the current user?

Also, where does this "self" is being stored - it is not like a flash[:success] parameter I can see in one of my views. so I don't understand where it is.

there are also these 2 methods in the same module:

  def current_user=(user)
    @current_user = user
  end

  def current_user
    @current_user ||= User.find_by_remember_token(cookies[:remember_token])
  end 

And still I am trying to connect the dots of the purpose for this mysterious current user - is its purpose is to create @current_user global variable to use in the views?

If so - why there are there these 2 duplicated functions def current_user=(user) and def current_user

Alon
  • 7,618
  • 18
  • 61
  • 99
  • 1
    `self` is a Ruby keyword that allows an object to refer to itself. So `self.current_user = user` is calling the module's `current_user=(user)` method, which assigns the argument `user` to the variable `@current_user`. In your view, when you call `current_user` you're calling the `current_user` method which returns the `@current_user` variable. Hope that helps. – Tom L Aug 15 '12 at 13:43
  • Oh, I just noticed that Harti's tutorial says more or less the same things I wrote in my answer... hmm. Did you not understand what he wrote? It's not obvious stuff, takes a while to sink in (had to double-check myself multiple times while writing that answer). – Chris Salzberg Aug 15 '12 at 13:49
  • Like what Tom L said, self allows an object to refer to itself, and everything is an object in Ruby. So 'self' can refer to many things, such as an instance of the class (when inside an instance method, like in your example), the class itself (when defined inside the class but outside methods, like def self.method), etc. – gerky Aug 15 '12 at 13:49
  • 1
    Thank you for all your help - it is still difficult for me to grasp the concept of it - why can't I just replace the sentence "self.current_user=user" with "@current_user=user"? – Alon Aug 15 '12 at 13:59
  • I want to make sure - the only purpose of "self" in this case is to make the variable "current_user" available throughout the module? if I would have written "current_user = user" instead then it would treat "current user" as local and it could not have been accessible in other methods? – Alon Aug 15 '12 at 14:00
  • 1
    It's a bit more than that. Check out this answer: http://stackoverflow.com/questions/1693243/instance-variable-self-vs#1693319 – Chris Salzberg Aug 15 '12 at 21:50

2 Answers2

5

A few things.

First, you're reading the method names wrong (which is not surprising given how cryptic ruby method naming can be). def current_user=(user) is actually read as defining the method current_user= that takes an argument user, whereas def current_user defines a method current_user that takes no arguments. These are referred to respectively as setters and getters.

Here's a reference: Ruby (programming language): What are setters and getters in Ruby?

So that explains the duplication. On to your next question.

I don't understand what does self.current_user = user means

self is a topic unto itself, worthy of its own discussion, so I won't even try to explain it (here's one reference out of many). For the purposes of this question it's just important to remember that in order to set instance variables, you need to prefix your assignment with self, even within the class (where for other purposes it would be implicit). The rest of the line is a call to the current_user= setter method I mentioned above, with the argument user.

why is it even necessary to keep this line of code - I have the cookie with the token - why do I need to know the current user?

The reason it's necessary is that you don't want to be looking up the user from the token every time you need to get the current user. Take a look at the getter method:

def current_user
  @current_user ||= User.find_by_remember_token(cookies[:remember_token])
end

What this says is: if I haven't looked up and set the instance variable @current_user yet, then look it up; if I have already set it, then just return it. That saves a lot of looking up.

I think that answers your questions. There are a lot of deeper issues (self, etc.) which you can find more information about elsewhere. Here's one discussion of why you need to include self in setters on SO: Why do Ruby setters need "self." qualification within the class?

UPDATE: Small clarification, that last link about using self for setters within the class is actually a bit off-topic, since you're calling it within a module and not directly from a class. In the context of a module, the self in self.current_user = user will become the class that the module is included inside of, e.g. User.current_user if it was called within the class User, etc. Again, another topic of discussion unto itself...

Community
  • 1
  • 1
Chris Salzberg
  • 27,099
  • 4
  • 75
  • 82
  • Thank you so much for your reply. I took the time to go over the issues you explained. I understood the whole concept of setters and getters. I still need to grasp better the "self." issue - but this will be on a new thread :) – Alon Aug 17 '12 at 11:42
  • Glad it made sense to you! `self` can be kind of mind-bending, I'm still learning the nuances of it. – Chris Salzberg Aug 17 '12 at 12:13
1

The method def current_user=(user) is basically a setter that the sign_in method uses in order to set the current_user.

def current_user will return the @current_user or if it is not set it will find it in the Users table by the remember_token. This basically allows you get the current_user at any point in time.

self.current_user in the context of the sign_in method will refer to the calling class or module in this case. It will be calling current_user from the Session Helper module.

Leo Correa
  • 19,131
  • 2
  • 53
  • 71