How to update the user's name
Starting with a new rails console, assuming you already have a user in the database, we can assign him to a local variable like this:
> user = User.find_by(email: "mhartl@example.com")
Now we have a User object in memory that is a copy of the user in the database. We can confirm this with:
> user
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2018-08-16 05:57:13", updated_at: "2018-08-16 06:15:04", password_digest: "$2a$10$bS2s/24d3ePLscZ8iT/NheDAvOZmODKjbCV5groq4v5...">
Now we change our user object's name:
> user.name = "Ace Ventura"
=> "Ace Venutura"
However, if we try to save this user, it fails:
> user.save
=> false
We can then examine the error message:
> user.errors.messages
=> {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
In exercise 2 of section 6.2.4 in Hartl's book, he asks us why calling .save didn't work. The reason it didn't work is because the validations are failing. In the user model at app/models/user.rb, we specifically state that in order for a user to be added to the database, a validation check should be performed to ensure that the user object has a password string of at least 6 characters in length. If we check our user object for a password string, we get this result:
> user.password
=> nil
All we need to do then is enter in the password we supplied earlier in the chapter, and we will be able to call .save:
> user.password = "foobar"
=> "foobar"
> user.save
=> true
Now we can check to make sure that the database was updated correctly:
> User.find_by(email: "mhartl@example.com").name
=> "Ace Ventura"
What was the "technique" Hartl was talking about?
The method I gave above for updating a database entry is not the "technique" Hartl was referring to in question 3. I provided that section to explain what is going on with the validations. When Hartl mentioned a "technique", he was implying that you should do this trick:
> user.update_attribute(:name, "El Duderino")
=> true
(Note we are using update_attribute here, not the plural version update_attributes.) As Hartl explains in section 6.1.5, when we need to update only a single attribute and skip validations at the same time we should use update_attribute.
So what does the authenticate method do?
In a simple sense, all the .authenticate method does is answer the question: "If I provided this password to the user object, will I be able to save the object to the database?" So when we do:
> user.authenticate("not_the_right_password")
=> false
All authenticate is doing here is telling us that if we set user.password = "not_the_right_password", when we call .save it will fail because we have the wrong password. It doesn't actually change anything on the object itself.
What about save! ?
To cite the docs:
There are many ways to change the state of an object in the database. Some methods will trigger validations, but some will not. This means that it's possible to save an object in the database in an invalid state if you aren't careful.
The following methods trigger validations, and will save the object to the database only if the object is valid:
create
create!
save
save!
update
update!
The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don't: save and update return false, and create just returns the object.
Note that save also has the ability to skip validations if passed validate:
false as an argument. This technique should be used with caution.
save(validate: false)
In summary:
save - If the model is new a record gets created in the database, otherwise the existing record gets updated. Validations run and if any of them fail the action is cancelled and save returns false.
save! - If the model is new a record gets created in the database, otherwise the existing record gets updated. Validations run and if any of them fail ActiveRecord::RecordInvalid gets raised.
save(validate: false) - Same as save but validations are bypassed altogether.