Rails -- self vs. # - ruby
I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
self.encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
I posted a previous question about before_save not working, and it turns out that what I had accidentally done is written my encrypt_password as:
def encrypt_password
#encrypted_password = encrypt(password)
end
I understand that if self.encrypted_password sets the encrypted_password attribute, but why does #encrypted_password not do that as well? In the response to the previous post about before_save not working someone said that the instance variable was "forgotten" after the method ended with the way I had originally coded it -- why was this the case? Can someone please explain how self and # work differently in the context of the code above?
NOTE: I already took a look at the posts here and here, but they both say that "self" is calling the attribute = method, and I don't even understand how that method could exist here since I never created it or declared the encrypted_password w/ attr_accessor. So I am still confused, and this is not a re-posting of those questions.
The accessors for encrypted_password have been automatically added by Rails for you because a field by that name exists in the users table.
Any field you add to a table will be automatically made available via self.field_name.
Here is where Michael Hartl's tutorial creates the encrypted_password field in the users table.
Also look at the user_spec.rb (Listing 7.3) in the linked page, where the author is testing for the presence of the encrypted_password field.
UPDATED:
As #mu points out, the # is used for Ruby instance variables (aka "iv"). But encrypted_password is an "attribute" defined by Rails, and is not an instance variable.
If you run User.find(1).instance_variables, you will see that there is an iv called #attributes, which is of type Hash.
Inside that iv is where the encrypted_password is stored. Rails has defined accessor methods for encrypted_password, which gets/sets the data for that
attribute in the #attributes Hash.
Note that you could also get/set the data via #attributes["encrypted_password"] called from within the User class (but the accessor methods are convenient way to do just that).
If you let me, I'd like to rephrase the answer.
I explained in this post, that as soon as you create a (rails-) Model with the same (singular) name as one of the (plural) tablenames of your database, the "magic" of rails will create setters and getters in order to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which defines basic CRUD accessors (Create, Read, Update, Delete).
The key point related to your question, is that you don't know how rails implements the instance variable related to your database table column, And you shouldn't. :) All you have to know is that at that point, you have setters and getters available to CRUD (create, read, update, delete) your database column "encrypted_password".
In your example, maybe rails uses an instance variable called #encrypted_password, maybe rails uses an hash-instance-variable called #attributes["encrypted_password"], or maybe rails uses an instance variable called #you_will_never_guess_encrypted_password.
-
And that's a good point you don't know about the internal rails behavior with instance variables. In 2019 Rails further development may lead the framework to use #complicated-hash-instance-variable to store the encrypted_password value.
In fact the best approach is to let rails manage its "private" "affair" ;) with instance variables, and just use the getter and setter methods it provides to you.
So your application will still work with encrypted_password in the next century (I hope so ^^).
So if you use #encrypted_password it may work with some "imaginary" version of rails and it won't work anymore with other rails versions. Actually with a current version of rails it doesn't work.
-
The second key point is that when you want to use the getter "encrypted_password" Rails created for your encrypted_password database table column, you prefix it with "self" in order to tells Ruby : "ok I want to use the encrypted_password method of my User instance variable."
In Ruby, a method is called by passing its name to a receiver.
You write it like this :
my_receiver.my_method
In your case we pass the method encrypted_password to the User instance variable. But we don't know how this instance variable will be named, so we use the word self to tell Ruby : "I'm talking about any instance variable of the User class that calls the encrypted_password method".
For instance we could have named our instance variable "toto" :
toto = User.new
so toto.encrypted_password would display the encrypted password, and self in this very case in our code would reference toto.
However, thanks to Ruby, if you don't give any receiver when calling a method, Ruby will assume you pass it to self.
Reference : Pragmatic Programmer's guide
So in your example, you even don't need to put "self." as prefix.
You could have it written like this :
class User < ActiveRecord::Base
def encrypt_password
encrypted_password = encrypt(password)
end
end
I hope this helps to clarify this interesting subject.
TL;DR -
Always write self.widget_count = 123 if you intend to save widget_count back to the database.
(But please do read the long answers, as the reason why is valuable to know.)
Related
Missing ActiveRecord methods (find_by) for object
I am trying to do an assignment which requires me to create and save an ActiveRecord within my Model class, and then return it. The rspec is expecting to use the find_by method to verify this. Here's my Model: ----------------------- class User < ActiveRecord::Base attr_accessor :id, :username, :password_digest, :created_at, :updated_at after_initialize :add_user def initialize(attributes={}) #username = attributes[:username] #password_digest = attributes[:password_digest] super end def add_user self[:username] = #username self[:password_digest] = #password_digest self.save self[:id] = self.id end end ---------------- If I do User.new(params), the record is in fact stored properly to the DB. But, the find_by method is missing for the returned object. So, rspec fails. I have looked everywhere but can't seem to find the solution. I am a noob, so sorry if the answer is obvious and I can't see it.
You say If I do User.new(params), the record is in fact stored properly to the DB. But, the find_by method is missing for the returned object This is expected behavior. Hopefully you understand by now the difference between class and instance methods. The main important point is that query methods such as find_by are not made available to model instances. If you do something like user = User.find_by(id: params[:id]), you're calling the find_by class method on the User model. There are a number of methods like where, order, limit, etc. that are defined in ActiveRecord::QueryMethods - these are made available to ActiveRecord::Relation object and your model class. Most of these methods will return ActiveRecord::Relation objects, which is why they're chainable, e.g. User.where(params).order(created_at: :desc).limit(5) However find_by is an exception - it returns a model instance so you can't continue to query on the results. In summary User.new(params) returns an instance of the model which doesn't have find_by available
attr_accessor breaks 2 of my methods in Rails
I have a User model. One of its attributes is a string called :access which can be either nil, "admin", or "active". Now inside the User model I have the following methods: def admin? self.access == "admin" end def active? self.access == "active" end They work fine. But if I add attr_accessor :access to the model something breaks. My admin? and active? methods no longer work. When I go into rails console and get a User out of the database I can see that user = User.find(7) shows access is set to "admin". But if I type user.access it returns nil. user.admin? returns false.
attr_accessor is overwriting the default methods of reading and writing an attribute from db. Why do you need attr_accessor if it is already a column in db and Rails provides you with the read and write methods. Are you confusing it with attr_accessible?
field vs method ruby on rails
I have this class: class User include Mongoid::Document field :revenues, :type => Integer, :default => nil attr_accessible :revenues #now method def revenues return 1 end end Why in console I get 1 instead nil? 1.9.3-p125 :002 > u.revenues => 1 Which has priority, the method or the field? How can I created a method with the same features that a field?
The field macro is defined in Mongoid::Document. It is neither a syntatic feature from Ruby nor from Rails. What's happening with your code is the following: The field function creates for you some methods, one of them is called revenues. When you create another method called revenues, you are in effect overwriting the previously defined method, therefore making it useless. Short answer: I don't understand a zip about Mongoid, but chances are that your field still exists even after you defined oce again a method named revenues. The only drawback is that you cannot access it by calling myUser.revenues anymore. Try to make a test: access your field with the notation some_user[:revenues] and see what happen :) Best regards
Rails Active Record: Calling Build Method Should Not Save To Database Before Calling Save Method
I have a simple user model class User < ActiveRecord::Base has_one :user_profile end And a simple user_profile model class UserProfile < ActiveRecord::Base belongs_to :user end The issue is when I call the following build method, without calling the save method, I end up with a new record in the database (if it passes validation) class UserProfilesController < ApplicationController def create #current_user = login_from_session #user_profile = current_user.build_user_profile(params[:user_profile]) ##user_profile.save (even with this line commented out, it still save a new db record) redirect_to new_user_profile_path end Anyyyyyy one have anyyy idea what's going on. The definition of this method says the following but it's still saving for me build_association(attributes = {}) Returns a new object of the associated type that has been instantiated with attributes and linked to this object through a foreign key, but has not yet been saved. http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one
Ok, I'm sure experienced vets already know this, but as a rookie I had to figure it out the long way...let me see if I can explain this without screwing it up Although I was not directly saving the user_profile object, I noticed in my logs that something was updating the user model's last_activity_time (and the user_profile model) each time I submitted the form (the user model's last_activity date was also updated when the logged in user did various other things too - I later realized this was set in the Sorcery gem configuration). Per http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html AutosaveAssociation is a module that takes care of automatically saving associated records when their parent is saved. In my case, the user mode is the parent and the scenario they provide below mirrors my experience. class Post has_one :author, :autosave => true end post = Post.find(1) post.title # => "The current global position of migrating ducks" post.author.name # => "alloy" post.title = "On the migration of ducks" post.author.name = "Eloy Duran" post.save post.reload post.title # => "On the migration of ducks" post.author.name # => "Eloy Duran" The following resolutions resolved my problem 1. Stopping Sorcery (config setting) from updating the users last_activity_time (for every action) or 2. Passing an ':autosave => false' option when I set the association in the user model as follows class User < ActiveRecord::Base has_one :user_profile, :autosave => false end
Need explanation of some Ruby syntax
I am new to ruby on rails, could anybody explain what does the symbol ':' mean, what would be 'validates' and 'create_table'? So much confused... class Post < ActiveRecord::Base validates :name, :presence => true validates :title, :presence => true, :length => {:minimum => 5} end class CreatePosts < ActiveRecord::Migration def change create_table :posts do |t| t.string :name t.string :title t.text :content t.timestamps end end end
The colon character (:) is the beginning of a syntax literal for a Ruby "Symbol": :abc.class # => Symbol "abc".to_sym # => :abc Symbols are like strings but they are "interned", meaning the Ruby interpreter only has a single copy of it in memory despite multiple possible references (whereas there can be many equivalent strings in memory at once). The 'validates' token in your example above is a class method (of something in the class hierarchy of the "Post class") that is being called with a symbol argument (:name) and a hash argument with a single key/value pair of :presence => true. The 'create_table' token is a method which is being called with a single argument (the symbol ":posts") and is given a block which takes a single argument "t" (do |t| ... end).
:foo is a symbol, i.e. a constant string that is guaranteed to be unique. They are often used in Ruby to reference fields or methods. validates is used in ActiveRecord to set a constraint on field's values. validates :name, :presence => true means that field name must be always set (not null, undefined or empty) for all instances of Post (and corresponding table in DB). validates :title, :presence => true, :length => {:minimum => 5} means that field title must be always set and its length must be greater than 5.
In Ruby, the : means that it is a Symbol. A Symbol is sort of like a lightweight string that's specifically used as an identifier. For example, in a hash, you use symbols as keys that point to their respective values. my_hash = {:key_1 => "A", :key_2 => "B"} In your examples above, you use symbols to specify the properties of your Post model and the columns of your posts table. Here are a few links for further reading on Ruby symbols: http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol http://glu.ttono.us/articles/2005/08/19/understanding-ruby-symbols http://www.ruby-doc.org/core/classes/Symbol.html http://www.robertsosinski.com/2009/01/11/the-difference-between-ruby-symbols-and-strings/
:foo is a "symbol" which is essentially an immutable string. It's major advantage is the fact that it doesn't allocate a new object every time you use it. If you were to use the string "name" every time you needed to use it, you would be making a new String object every time. However, if you use :name instead, you are using the same Symbol object every time (same in terms of pointer equality and object identity). validates and create_table are both methods. In ruby, a method doesn't need parenthesis when called, so validates :foo is the same as validates(:foo). The methods come via inheritance and module mixins. validates is a class method put onto ActiveRecord objects during the inheritance, and create_table is a instance method
Ruby Documentation and Ruby on Rails Documentation: "what does the symbol : mean?" Class:Symbol Symbol objects represent names and some strings inside the Ruby interpreter. They are generated using the :name and :"string" literals syntax, and by the various to_sym methods. The same Symbol object will be created for a given name or string for the duration of a program‘s execution, regardless of the context or meaning of that name. Thus if Fred is a constant in one context, a method in another, and a class in a third, the Symbol :Fred will be the same object in all three contexts. "what would be validates?" ActiveModel::Validations::ClassMethods This method is a shortcut to all default validators and any custom validator classes ending in ‘Validator’. Note that Rails default validators can be overridden inside specific classes by creating custom validator classes in their place such as PresenceValidator. "what would be create_table?" ActiveRecord::ConnectionAdapters::SchemaStatements Creates a new table (The link shows examples, showing SQL statements generated by this method.)
#Grimm As you should know, everything is object on RoR. There are cases where you need to differentiate String from others as the memory handling techniques of String are different from other data structures. Colon : is a form of consideration of such type. They're just symbols like the one that makes hash entry on the memory! Get used to that, it'll be interesting!! :)
as everybody is saying, : is a start of a symbol. Symbol is simple a string or variable name for me. In your example, :name is a variable/symbol that reflect one of the field name in Post table. Rails automatically creates these symbols when you create a Model class. In Ruby, you can call a method/function and specify their parameters with/without the brackets. So, validates :name, :presence => true can be written as validates(:name, :presence => true) So, you are actually passing the :name and the true as the parameters for validates method hope this help you see clearer about the methods calling in Ruby. Same as validates, create_table is also a method.
Apart from the symbols, the point to be noted here is that in Ruby, we dont have to implicitly give {} to specify that an argument is a hash if it is the last argument. I mean by calling validates :name, :presence => true you are calling validates :name, {:presence => true} or validates(:name, {:presence => true}) then it becomes clear that you are calling a method validates with 2 arguments, a symbol and a hash. If we ignore the symbol and substitute strings in place, like this: validates("name", {"presence" => true}) it is pretty much similar to a method call in any other language. So, watchout for this as it is used almost in every helper tags Rails use. For the other methods also you can see this: validates(:title, {:presence => true, :length => {:minimum => 5}}) In the case of create_table, it is a method call with 2 arguments, a symbol and a block.