The famous tutorial uses self to assign user to an instance variable.
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.hash(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
end
Why wouldn't the following be valid? The book says this type of assignment would create a local variable.
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.hash(remember_token))
#current_user = user
end
###def current_user=(user)
# #current_user = user
# end
# end
If you have #current_user = user in sign_in method then it should definitely work which is as good as calling the commented method current_user=. #current_user is not a local variable, it is an instance variable.
The book says this type of assignment would create a local variable.
Book is not talking about #current_user = user BUT about current_user = user (Notice without prefix #) which will set a local variable current_user and not call the instance method current_user=.
When you call self.current_user = user, then self is the explicit receiver so Ruby knows that current_user= is a method and not a local variable.
Related
Note: before I started the tutorial at railstutorial.org, I completed codecademy's Ruby tutorial.
I'm following Michael Hartl's Ruby on Rails tutorial. I'm on chapter 8, and I'm on the sessions_helper.rb file. There's a variable called "current_user". I learned about scope when following the Ruby tutorial, but now I'm having trouble applying what I learned.
In the tutorial, there is the following code:
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.digest(remember_token))
self.current_user = user
end
def signed_in?
!current_user.nil?
end
def current_user=(user)
#current_user = user
end
def current_user
remember_token = User.digest(cookies[:remember_token])
#current_user ||= User.find_by(remember_token: remember_token)
end
end
In particular, what I don't understand is the difference between
a) self.current_user
b) #current_user
c) current_user
I have an understanding of global, instance, and local variables in concept, but not in application. If I were to code this by myself without the training wheels of the tutorial, I wouldn't know when to use which. Could somebody help me out here?
current_user if you do not explicitly declare a local variable, it will look for a method in the current scope, in this case your def current_user method.
#current_user is an instance variable. One of Rails magics is to copy all the instance variables from the controller into views. In this case, it probably comes from your controller or ApplicationController, which it probably inherits from.
self.current_user is the same as current_user, except that self.current_user= in your code means you're using the method declared right below it instead of assigning a local variable.
i.e.
current_user = "pedro"
current_user # "pedro"
self.current_user # method called
In case of not declaring a local variable, this is what happens with current_user:
current_user # method called
self.current_user # same method called
I am learning Rails from Michael Hartl's tutorial, but I am really confused about the SessionsHelper module. Not enough information is provided about the duplication of the current_user method. Could someone explain why there are two and what are their individual purposes?
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
end
I understand the sign_in method would trigger the call to current_user=(user) but why the current_user method again? I understand the second method gets the user based on remember_token from the database, but I can't connect the dots regarding these.
current_user is the reader method and current_user= is the writer method for your attribute current_user. These two are separate methods, one would be used to read the value and other to write value of current_user.
EDIT
In your case, current_user= method means that set the value of instance variable #current_user equal to the passed value(user).
def current_user=(user)
#current_user = user
end
current_user method means,
def current_user
## if #current_user = nil then
## set #current_user = User.find_by_remember_token(cookies[:remember_token])
## else return #current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
sign_in method,
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
sign_in method would be called as sign_in user from a controller.
Like Aaron mentioned in the comment, without self, Ruby would simply create a local variable called current_user which would be lost once sign_in method finishes execution.
But by saying, self.current_user = user the value of current_user would be preserved in the current instance(instance of controller in which you have included the module) of class.
For a better understanding of self refer to this Stackoverflow question on self
I'm a little confused here. In chapter 8 of the fabulous Ruby on Rails Tutorial by Michael Hartl, I can't figure out why Michael uses a setter to set the instance variable #current_user to user.
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
end
Why did he not just skip the setter method and do it this way instead?
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
#current_user = user
end
end
Is it just because it's best practice to implement a setter or I don't get something?
This is just an OO question. Because the current_user= is part of the session's contract with the world. It is promising that, no matter where the user is ultimately stored (such as in User.current_user), you can always set it thru that interface.
I haven't read it, but he could be
Making a point
Following best practice
Intending to use the setter elsewhere, earlier or later in the tutorial
I am pretty sure he reuses that method later when the user loads another page. Something like:
# in application_controller
before_filter :authenticate
private
def authenticate
if cookies[:remember_token]
self.current_user = User.authenticate(cookies[:remember_token])
end
end
# in user.rb
def self.authenticate(token)
find_by_remember_token(User.encrypt(token))
end
In Michael Hartls rails tutorial , I came across the following code :
module SessionsHelper
def current_user=(user)
#current_user = user
end
def current_user #get logged in user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user) #sign the user in by setting cookies
cookies.permanent[:remember_token]= user.remember_token
current_user = user
end
def signed_in?(user) #check whether user signed in
!current_user.nil?
end
end
My SessionsController create action looks like this :
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user # sign the user in by setting cookies
else
flash.now[:error]= "Invalid email/password"
render 'new'
end
end
Why do we need a writer method def current_user=(user) in the SessionsHelper module ?
Can't we just assign the value of user to the #current_user variable directly in the sign_in method ?
Can it be done like this:
module SessionsHelper
# Notice that there is no def current_user=(user) method.
def current_user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user)
cookies.permanent[:remember_token]= user.remember_token
#current_user = user #set the current user directly withoout a writer method
end
def signed_in?(user)
!current_user.nil?
end
end
Sure you can do like that. But idea is to incapsulate instance variable and never assign it directly outside getter and setter methods and that's how ruby works. You actually cannot do SomeObject.#instanceVar=. you need setter for that.
So if in future you will need to set current_user eather you have to use sign_in method or create new method which will be like exactly setter. So why not to use it?
I am trying to understand this piece of code
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
end
Using the line
self.current_user = user
will it create a variable named current_user for the class SessionsController (the above module belongs to SessionsController class)?
If I use
#current_user = user
instead of the above line, the code seems to work the exact same way. How?!
Thanks a lot for your time.
self.current_user = user just call the method current_user=(user), and the method only does #current_user = user, so it just work the same if you do #current_user = user.
self.current_user = user, this expression is to indicate current_user is the method in class Sessions (mixin by module SessionsHelper) rather than a local variable. The self there represent the class Sessions, so this line is just a method invoking, the same effect with #current_user = user or current_user=(user).