John Merlino
2010-Dec-16 21:49 UTC
How does ActiveRecord translate parameter hash to values of a class instance''s properties
Hey all,
Let''s say you have this method:
def signup
@user = User.new(params[:user])
if request.post?
if @user.save
session[:user] = User.authenticate(@user.login, @user.password)
flash[:message] = "Signup successful"
redirect_to :action => "welcome"
else
flash[:warning] = "Signup unsuccessful"
end
end
end
Since User class inherits from ActiveRecord class, I presume
ActiveRecord contains a constructor that takes the key/value pairs of a
hash from the parameters of web form and checks if the keys of the hash
match the instance methods available in current class instance which
were generated from field names from a table of the same name (e.g.
Users).
Basically all that happening with this line:
@user = User.new(params[:user])
Hence, you can now do @user.login - should login be a field in the
database. If a value for login was captured in the param hash, then
@user.login will return value passed from params hash.
Does anyone have a general description of what ActiveRecord does behind
the scenes to achieve this?
Now the second question. What about parameter''s that are not part of
the
table field names? ActiveRecord doesn''t create getter and setter
methods
for these parameters by default.
You have to manually do it:
attr_accessor :password #should a password field not exist in users
table
So basically when a new instance is created, and we accept a hash
parameter, which contains a password key that doesn''t translate to
field
in database, ruby immediately checks if such a value has a setter and
getter (attr_accessor :password) and if it does, then it calls the below
method since it needs to resolve the password object:
def password=(pass)
@password=pass
self.salt = User.random_string(10) if !self.salt?
self.hashed_password = User.encrypt(@password, self.salt)
end
So is that all that happens here or does ActiveRecord do something else
behind the scenes? This is a different question from my first question.
ALso, why do you think this method is authenticating the user as soon as
they are created? The fact that they just been created suggests that
they are real.
Thanks for response.
--
Posted via http://www.ruby-forum.com/.
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.
Frederick Cheung
2010-Dec-16 22:11 UTC
Re: How does ActiveRecord translate parameter hash to values of a class instance''s properties
On Dec 16, 9:49 pm, John Merlino <li...-fsXkhYbjdPsEEoCn2XhGlw@public.gmane.org> wrote:> Hence, you can now do @user.login - should login be a field in the > database. If a value for login was captured in the param hash, then > @user.login will return value passed from params hash. > > Does anyone have a general description of what ActiveRecord does behind > the scenes to achieve this?Barring some complication around protected attributes, multi parameter assignments etc... the code behind all this boils down to hash_of_attributes.each do |name, value| send "#{name}=", value end Whether the method called is one backed by a database attribute or not doesn''t matter Fred -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
Robert Walker
2010-Dec-16 22:22 UTC
Re: How does ActiveRecord translate parameter hash to values of a class instance''s properties
John Merlino wrote in post #968948:> Since User class inherits from ActiveRecord class, I presume > ActiveRecord contains a constructor that takes the key/value pairs of a > hash from the parameters of web form and checks if the keys of the hash > match the instance methods available in current class instance which > were generated from field names from a table of the same name (e.g. > Users). > > Basically all that happening with this line: > @user = User.new(params[:user]) > > Hence, you can now do @user.login - should login be a field in the > database. If a value for login was captured in the param hash, then > @user.login will return value passed from params hash. > > Does anyone have a general description of what ActiveRecord does behind > the scenes to achieve this?You pretty much just described it. There''s likely a good bit of meta-programming happing behind the scenes, but as a general overview what you describe is a good way to think about it. If you really want to know then clone the Rails project and read the code. It is open source after all.> Now the second question. What about parameter''s that are not part of the > table field names? ActiveRecord doesn''t create getter and setter methods > for these parameters by default. > > You have to manually do it: > > attr_accessor :password #should a password field not exist in users > tableYou can think of attr_accessor, attr_reader, attr_writer as shortcuts for writing out the individual accessor methods for dynamically generated instance variables. No real magic is going on here other that Ruby being dynamic and creating what it needs when it needs it at runtime. BTW: What you''re describing here is often referred to as "virtual attributes" when related to ActiveRecord. Everywhere else, AFAIK, they are simply called accessors backed by instance variables. -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
John Merlino
2010-Dec-17 19:16 UTC
Re: How does ActiveRecord translate parameter hash to values of a class instance''s properties
Still a little confused in terms of sequence of operation:
1) A request for URL is made (e.g. www.mysite.com/login). The URL is
matched against a route to find the corresponding controller. So I have
this:
map.signup "signup", :controller => "users", :action
=> "signup"
Hence, the signup URL string is mapped against the UsersController. Now
Rails knows that the file containing the controller can be found as
apps/controllers/users_controller.rb. The signup portion of query string
is matched against the signup action of the UsersController, which is
represented by a signup method in the controller.
2) Hence, at this point that method is invoked:
def signup
@user = User.new(params[:user])
if request.post?
if @user.save
session[:user] = User.authenticate(@user.login, @user.password)
flash[:message] = "Signup successful"
redirect_to :action => "root"
else
flash[:warning] = "Signup unsuccessful"
end
end
end
We instantiate new User object, passing it arguments from the parameter
hash passed via query sring. When page first loads, these parameters
will all be equal to nil since no assignment has been made to the
properties of the User object (because no user input occurred yet):
<User id: nil, login: nil, hashed_password: nil, email: nil, salt: nil,
created_at: nil>
Nevertheless these properties are "own members" (instance methods) of
the Object instance, courtesy of the hash iteration described above
where setters and getter methods are dynamically created at run time:
def login=(p)
@p = p #if no parameter passed, nil returned implicitly in Ruby?
end
def login
@p
end
@user.login #nil
User now fills out form and submits it:
<% content_for :signup do %>
<% form_for @user, :url => { :action => "signup" } do |f|
%>
<%= error_messages_for ''user'' %><br/>
<%= f.label(:user_login, "Username")%>
<%= f.text_field(:login) %><br/>
<%= f.label(:user_password, "User Password")%>
<%= f.password_field(:password) %><br/>
<%= f.label(:user_password_confirmation, "Password
Confirmation")%>
<%= f.password_field(:password_confirmation) %><br/>
<%= f.label(:user_email, "User Email")%>
<%= f.text_field(:email)%>
<%= f.submit("Sign Up") %>
<% end %>
<% end %>
Here, the signup method is called again but this time it''s not a GET
request. It''s a POST request. We also now have values for our key hash.
And interestingly there is a method here called password that gets as
parameter of predefined password_field method. There is no password
method in our users table. Ruby must resolve it, so it checks the
instance methods of our user instance and indeed it finds it:
attr_accessor :password, :password_confirmation
def password=(pass)
@password=pass
self.salt = User.random_string(10) if !self.salt?
self.hashed_password = User.encrypt(@password, self.salt)
end
Basically this just populates our salt and hashed_password properties to
ensure protection of our user. Ultimately, we now have values for our
properties of our instance. Since we are dealing with post request, we
save the user calling predefined save method of ApplicationController??
(who knows).
if request.post?
if @user.save
session[:user] = User.authenticate(@user.login, @user.password)
And store the user as part of current session.
Is this a correct analysis in terms of sequence of events?
A real gray area for me is when the instance variable @user stores the
correct value in signup.html.erb with an appropriate reference to
current instance. Does this it become available as soon as the method is
called in controller? Or is there some more magic going on that makes it
available?
Thanks for response.
--
Posted via http://www.ruby-forum.com/.
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.