Studying the RoR 3 Tutorial book by Michael Hartl and on page 345 there's the code inside the SessionsHelper:
Filippos wrote in post #1013927:
Studying the RoR 3 Tutorial book by Michael Hartl and on page 345 there's the code inside the SessionsHelper:
_________________________________________________________ module SessionsHelper
def sign_in(user) cookies.permanent.signed[:remember_token] = [user.id, user.sault] self.current_user = user end
end __________________________________________________________
What is the purpose of "self" inside the session helper? I know that inside a model it refers to the class object. For example
Class User < ActiveRecord::Base self.salt
it refers to User.salt
But when it is inside a helper, does it refer to the helper itself?
Remember that no code inside a method actually executes until the method is called. When the signin method is actually called by some object, for instance:
some_object.sign_in(userA)
...then inside the sign_in() method, self is equal to the object that called the sign_in method, which in this case is some_object.
However, the book never talks about what object is calling the various methods. Instead, you have a controller like this:
class SessionsController < ApplicationController
def new end
def create end
def destroy end
end
and the book talks about some url getting mapped to the new action, which then causes rails to execute the file new.html.erb (and then rails sends the resulting new.html file back to the browser). But if you know any ruby, then you can infer that rails has to create a SessionsController object, like this:
sess_controller = SessionsController.new
in order to be able to call the new, create, and destroy methods, e.g.
sess_controller.new
That part of the book confused me, too, because instead of this:
def sign_in ... self.current_user = user end
you could also write:
def sign_in
@current_user = user end
but you definitely cannot write:
def sign_in .. current_user = user end
That creates a 'local variable' called current_user which will be destroyed when the sign_in method finishes executing. '@' variables persist as long as the sess_controller object still exists.
The way this code works:
def sign_in ... self.current_user = user end
is the self is the object calling the sign_in method. What object is calling the sign_in method? That is a bit convoluted. The sign_in method is in a module called SessionsHelpers, and you have this code in a file:
class ApplicationController < ActionController::Base protect_from_forgery include SessionsHelper end
That means all the methods in SessionsHelper become methods in the ApplicationController class. But you also have this file:
class SessionsController < ApplicationController def new end
def create end
def destroy end
end
..so through inheritance all the ApplicationController methods become methods of SessionsController--including the SessionsHelper methods. In the end, that means a SessionsController object, e.g.
sess_controller = SessionsController.new
is the object that is going to be calling the sign_in method, and so inside the sign_in method self is going to be equal to sess_controller. Of course, rails calls sign_up behind the scenes, so what object is calling sign_up isn't obvious, and therefore determining what self is inside the sign_up method isn't obvious.
Now the question is why use self in the sign_in method here:
def sign_in ... self.current_user = user end
when you can avoid all that confusing stuff and just write:
def sign_in
@current_user = user end
The short answer: it's good practice to always use an accessor method to access an instance variable--rather than assign directly to an instance variable. The reason is that if, for instance, you want to apply some kind of transformation to a value before assigning it to an instance variable, you can do that in the current_user=() method rather than hunting through your code and looking for every @current_user = ... line and making the change to each of those lines.
The purpose of this line is to create an assignment to current user and the existence of the instance variable @current_user is to "permanently" stored, as long as it is needed (not as a local variable) since there's no model?
@ variables can be seen in all the class's methods (that is from ruby), and in rails you have classes with names like SessionsController, and the methods defined in the class are called actions. Rails arranges for @ variables to be seen in any views as well.
In my previous post, I said:
'@' variables persist as long as the sess_controller object still exists.
However, the web is a very ephemeral place to do business, i.e, things exist for only a short period of time before being destroyed. For instance, a browser sends a request to your rails app, which then returns a response in the form of an html page, which usually happens in few seconds--and then everything gets destroyed. Afterwards, the browser doesn't remember communicating with your rails app, and your rails app doesn't remember ever communicating with that browser.
As far as I know, because I am a rails beginner too (and I happen to be at the same spot as you in the book), the sess_controller object, which rails uses to call an action in response to a particular url, is destroyed as soon as the application sends any html page back to the browser.
The @current_user variable appears in this method:
def current_user=(user) @current_user = user end
And @ variables attach themselves to whatever is currently self, which as discussed in my previous post will be this object:
sess_controller = SessionsController.new
So knowledge of the current user is destroyed when the sess_controller object is destroyed, and that happens only a few seconds after the browser initially sends its request--hardly anything permanent.
The trick is to store a permanent cookie on the browser. When a browser sends any request to your rails app, it adds all cookies that were stored by your app. The code you are currently studying in the book stores a cookie on the browser. Presumably, later in the book, the app will always check any browser request to see if the user is 'signed in' or 'signed out', which means: did the browser send a 'signed in' cookie in the request, or was such a cookie absent.
The part that really confused me on p. 345 is where the author says:
7stud -- wrote in post #1013962:
The part that really confused me on p. 345 is where the author says:
== self.current_user = user
The purpose of this line is to create current_user, accessible in both controllers and views which will allow constructions such as:
<%= current_user.name %>
and
redirect_to current_user
The author says "current_user" not 'the current_user function".
Actually, in the line:
self.current_user = user
the function being called is named 'current_user='. ruby syntax just permits you to write it with a space before the equals sign.
It's hard to tell if its just a typo when the author says 'current_user', and the author really means '@current_user'. Then there is this final statement on the page:
I'm going to use these methods in my app instead of the current_user method in the book:
#getter method: def current_user @current_user end
#setter method: def current_user=(user) @current_user = user end
def update_current_user user = current_user #calls getter method return user if user
cookie_arr = cookies.signed[:remember_token]
if cookie_arr self.current_user = User.authenticate_with_salt(*cookie_arr) #calls setter method
return current_user #calls getter method else return nil end
end
Any time the book calls current_user, I plan on calling update_current_user instead.
It follows that the line:
<%= current_user.name %>
actually does this:
<%= current_user().name %>
Punctuation is usually optional in ruby when this doesn't introduce ambiguities.
What I found to be a new concept in this section of the book is that we can call methods in the view that are inherited by the controller:
You can't. The reason why the methods in SessionsHelper can be used in the view is that (by default) all the helpers in app/helpers are included in views. When you add the method directly to the controller then this no longer holds and so the view can't find the method
Fred
Frederick Cheung wrote in post #1013993:
It follows that the line:
<%= current_user.name %>
actually does this:
<%= current_user().name %>
Punctuation is usually optional in ruby when this doesn't introduce ambiguities.
What I found to be a new concept in this section of the book is that we can call methods in the view that are inherited by the controller:
You can't. The reason why the methods in SessionsHelper can be used in the view is that (by default) all the helpers in app/helpers are included in views. When you add the method directly to the controller then this no longer holds and so the view can't find the method
Ahh. Okay.
What do you think about this:
What do you think about this:
> def current_user > @current_user ||= user_from_remember_token > end
> I see two problems with that function. First off, that is a getter > function, yet it can set the @current_user variable. Getter functions > should not set instance variables. Secondly, the function directly sets > the @current_user variable instead of calling the current_user= setter > function.
Memoization is an extremely common practice. I find the existence of current_user= a little odd.
Fred
Hmmm...but all I've really done is removed the code in the getter that sets the instance variable into a function that the getter calls. That still leaves me with a getter that sets the instance variable.
Frederick Cheung wrote in post #1014004:
> the @current_user variable instead of calling the current_user= setter > function.
Memoization is an extremely common practice. I find the existence of current_user= a little odd.
Why's that? In the SessionsController there is a method that is defined like this:
def signin(user) cookies.permanent.signed[:remember_token] = [user.id, user.salt] self.current_user = user end
By the way, the book we are discussing is online and we are discussing section 9.3:
http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec:signin_success
Frederick Cheung wrote in post #1014004:
>> > the @current_user variable instead of calling the current_user= setter >> > function.
> Memoization is an extremely common practice. I find the existence of > current_user= a little odd.
Why's that? In the SessionsController there is a method that is defined like this:
because you've got some weird halfway house where sometimes current_user can work out what the current user is but in other cases it's assigned. Sometimes @current_user is just a cache, sometimes it's a most traditional ivar.
Fred
Ok, I'm not as advanced as 7stud. Im not familiar with ruby and I started studying Rails. Your replies where very informative but also a bit confusing at the same time.
1. So when we want to go to localhost/myapp/users/1 rails first maps the URL to Users#show, then creates the object user_controller = UsersController.new and then executes the user_controller.show?
2. When we assign an instance variable inside the controller , doesn't it refer to the controller object that rails create? For example @user -> user_controller = UsersController.new What would the difference be between the instance variable and the self.user inside t he controller method.
3. I didn't understand your justification of getter and setter.Since instance variables work fine why would you need to define a setter and getter. You can just assign to the instance variable and you're done since it will be accessible in the controller and view for as long as the sess_controller lasts.
Instead of self.current_user = user we could write @current_user = user
Why call the current_user getter and not just call the @current_user directly.
I'm a bit confused.
The easy way would be to memorize or copy/paste but understanding the code is better way to create rails apps.
Ok, I'm not as advanced as 7stud. Im not familiar with ruby and I started studying Rails. Your replies where very informative but also a bit confusing at the same time.
1. So when we want to go to localhost/myapp/users/1 rails first maps the URL to Users#show, then creates the object user_controller = UsersController.new and then executes the user_controller.show?
basically
2. When we assign an instance variable inside the controller , doesn't it refer to the controller object that rails create? For example @user -> user_controller = UsersController.new What would the difference be between the instance variable and the self.user inside t he controller method.
I think you may be imagining magic that doesn't exist.
3. I didn't understand your justification of getter and setter.Since instance variables work fine why would you need to define a setter and getter. You can just assign to the instance variable and you're done since it will be accessible in the controller and view for as long as the sess_controller lasts.
Instead of self.current_user = user we could write @current_user = user
Why call the current_user getter and not just call the @current_user directly.
For me the advantage of a current_user method over using @current_user directly is that I don't have to worry about whether I've setup @current_user already or not, I can just call current_user and things will not. Equally if I never need to check current_user then I don't do the work to set that up unnecessarily. That the current_user method happens to cache its result in the instance variable of the same name is just an implementation detail.
Fred
Filippos wrote in post #1014028:
1. So when we want to go to localhost/myapp/users/1 rails first maps the URL to Users#show, then creates the object user_controller = UsersController.new and then executes the user_controller.show?
Yes.
2. When we assign an instance variable inside the controller , doesn't it refer to the controller object that rails create?
Yes. Instance variables attach themselves to an object--that object is the object that calls the method in which the instance variable is first set. Here is an example:
class Dog def bark @color = "black" puts "woof" end
def color #getter method @color end end
d = Dog.new d.bark puts d.color
--output:-- woof black
For example @user -> user_controller = UsersController.new What would the difference be between the instance variable and the self.user inside the controller method.
Lets see:
class Dog def bark @color = "black" puts "woof" end
def color translate_to_german(@color) end
def translate_to_german(word) if word == "black" 'schwartz' else 'not known' end end
def show puts @color puts self.color end end
d = Dog.new d.bark d.show
--output:-- woof black #@color schwartz #self.color
3. I didn't understand your justification of getter and setter.Since instance variables work fine why would you need to define a setter and getter.
Because it's good programming practice.
You can just assign to the instance variable and you're done since it will be accessible in the controller and view for as long as the sess_controller lasts.
Yes, but what if later you decide that you want to alter the value that is assigned to the instance variable before doing the assignment? Then you would have to look through your code and find every line where you have written @var_name = ...., and change it. What if your program was 10 million lines long? Would you want to do that? How long would it take you? Two years? Twenty years?
If you always access instance variables using getters and setters, you have the flexibility to change the value that is set or the value that is retrieved. For instance suppose you were doing calculations in feet and you decided you needed to do the calculations in meters. Without changing the user interface, you could make those changes in the getter and setter methods. The user could still enter the value in feet, and then your setter could convert to meters and save that value instead. Your getter could then convert the value back to feet and return it.
If you are interested in more examples, you can search google for something like 'why getters and setters'
3. I didn't understand your justification of getter and setter.Since instance variables work fine why would you need to define a setter and getter.
Because it's good programming practice.
Ok, I took my own advice an read some articles about 'why getter and setters' and there are enough people that think it's actually bad programming practice. But as far as I can tell the reasons I stated are why the book is is using getters and setters--however, the book does not always use the getters and setters, so it is being consistent.
wow thanx!
> For example @user > -> user_controller = UsersController.new > What would the difference be between the instance variable and the > self.user inside the controller method.
Lets see:
class Dog def bark @color = "black" puts "woof" end
def color translate_to_german(@color) end
def translate_to_german(word) if word == "black" 'schwartz' else 'not known' end end
def show puts @color puts self.color * end end
* so in this context self.color calls the color method of the Dog class for the d object (d.color) . What if we omitted "self" , would rails execute the color method with the same result?
d = Dog.new d.bark d.show
--output:-- woof black #@color schwartz #self.color
> You can just assign to the instance variable and you're done > since it will be accessible in the controller and view for as long as > the sess_controller lasts.
Yes, but what if later you decide that you want to alter the value that is assigned to the instance variable before doing the assignment? Then you would have to look through your code and find every line where you have written @var_name = ...., and change it. What if your program was 10 million lines long? Would you want to do that? How long would it take you? Two years? Twenty years?
If you always access instance variables using getters and setters, you have the flexibility to change the value that is set or the value that is retrieved. For instance suppose you were doing calculations in feet and you decided you needed to do the calculations in meters. Without changing the user interface, you could make those changes in the getter and setter methods. The user could still enter the value in feet, and then your setter could convert to meters and save that value instead. Your getter could then convert the value back to feet and return it.
If you are interested in more examples, you can search google for something like 'why getters and setters'
Ok, i think i kind of understand what you mean here, but need some examples. I will google it. Getters and setters are the same with the attr_accessor but we can't use it in a controller because its an ActiveRecord attribute, right?
So inside the getter and setter we can define what is going to be stored to or retrieved from an instant variable.
def online_user= (user) @current_user = user #or any other code we want to write end
def online_user @current_user # or any other code we want to write end
Must the name of the "methods" (online_user) be the same with the instant varialbe (@current_user) inside the "method" ?
Filippos wrote in post #1014053:
wow thanx!
puts "woof" 'not known' end end
def show puts @color puts self.color * end end
* so in this context self.color calls the color method of the Dog class for the d object (d.color) . What if we omitted "self" , would rails execute the color method with the same result?
Yes, when a method is called without "a receiver", i.e without an object in front of it, then ruby calls the method with self. In fact, all methods have to have a receiver, i.e an object that calls the method, so if you don't provide one, ruby uses self. One time when you do have to explicitly write self is when you are on the left side of an equals sign:
color = "black"
v.
self.color = "black"
In the first case, you create a local variable and assign 'black' to it. In the second case, you call the color=() method.
d = Dog.new d.bark d.show
--output:-- woof black #@color schwartz #self.color
If you are interested in more examples, you can search google for something like 'why getters and setters'
Ok, i think i kind of understand what you mean here, but need some examples. I will google it. Getters and setters are the same with the attr_accessor but we can't use it in a controller because its an ActiveRecord attribute, right?
attr_accessor :color
is shorthand in ruby for this code:
def color=(val) @color = val end
def color @color end
You can call attr_accessor (yes it is a method) in any class.
So inside the getter and setter we can define what is going to be stored to or retrieved from an instant variable.
def online_user= (user) @current_user = user #or any other code we want to write end
def online_user @current_user # or any other code we want to write end
Must the name of the "methods" (online_user) be the same with the instant varialbe (@current_user) inside the "method" ?
The getter and setter method names will be the same as the instance variable name if you use attr_accessor. If you write the methods by hand, you could use different names, but that is not done. For instance, you wouldn't do this:
class Dog def color=(val) @age = val end
def color @age end end
d = Dog.new d.color = 10 puts d.color
--output:-- 10
I decided to try programming the app using the principles of the anti getter/setter crowd. They say that instead of asking for a value with the getter, and then doing some calculation with the value, instead define a method in the class that does the calculation, and just call that method. Following those principles, here is what my SessionsHelper module looks like:
module SessionsHelper
def sign_in(user) cookies.permanent.signed[:remember_token] = [user.id, user.salt] #self.current_user = user @current_user = user end
def signed_in? get_user_from_cookie ? true : false end
def sign_out cookies.delete(:remember_token) @current_user = nil end
def get_user_from_cookie @current_user || begin cookie_array = cookies.signed[:remember_token]
if cookie_array @current_user = User.authenticate_with_salt(*cookie_arr) return @current_user else return nil end
end # || block end
end
Then instead of calling current_user in the various actions, tests, and helpers, I just call get_user_from_cookie.
Uggh. There must be a hole in my tests:
def get_user_from_cookie @current_user || begin cookie_array = cookies.signed[:remember_token]
if cookie_array @current_user = User.authenticate_with_salt(*cookie_arr) return @current_user else return nil end
end # || block end
*cookie_arr should be *cookie_array. But my tests didn't throw an error.
your implementation makes more sense to me than the example of the book.
In your code you write:
def get_user_from_cookie @current_user || begin <------- what is the begin ? or is that line code a typo?
Pffff... it took me back to basics! According to the book with attr_accessor we create virtual attributes (when we dont want to save something in the database). But it uses them inside a model class (user class) so it's been called as self.password (when attr_accessor :password) I didn't see it outside a model class so im having problems understanding and getting used to getter and setter outside a model class since its the same thing. Not to mention the "self" outside a model class... very strange.
So the logic behind Helpers and the module is that when we're not using a model (px for Sessions) and since the code needed for sign- in / sign-out doesn't involve an action-view relationship like with Controllers -it's just methods and programming- we use the SessionsHelper with modules and not the ApplicationController.
About the methods.
def method1 ... end
is one type of method which doesn't require an argument whereas
def method2(string1, string2, string3...) ... end
is the other type which requires input.
So you mean that since we dont write "self" Rails infers:
self.method1 and self.method2(string1, string2, string3...) when we call them?
with self being the session_controller = SessionsController.new or session_helper = SessionsHelper.new? or is it any other object?