Why use 'reload' method after saving objec

2019-02-05 14:48发布

问题:

I'm working on the exercises for chapter 6 of Hartl's Rails 4 Tutorial. The first exercise tests to make sure that user email addresses are down-cased correctly:

require 'spec_helper'

describe User do
  .
  .
  .
  describe "email address with mixed case" do
    let(:mixed_case_email) { "Foo@ExAMPle.CoM" }

    it "should be saved as all lower-case" do
      @user.email = mixed_case_email
      @user.save
      expect(@user.reload.email).to eq mixed_case_email.downcase
    end
  end
  .
  .
  .
end

What I don't understand is why the 'reload' method is necessary here. Once @user.email is set to the contents of mixed_case_email and saved, aren't @user.reload.email and @user.email the same thing? I took the reload method out just to try it and it didn't seem to change anything with the test.

What am I missing here?

回答1:

Yes in this case @user.reload.email and @user.email is the same thing. But it's good practice to use @user.reload.email instead of @user.email to check what is exactly saved in the database i mean you don't know if you or someone add some code in after_save which changes it's value then it will not have effect on your tests.

EDIT: And also what you are checking is what's saved in the database so @user.reload.email exactly reflects what's saved in database then @user.email



回答2:

In-Memory vs. Database

It's important to understand the difference from in-memory and the database. Any ruby code you write is in-memory. For instance, whenever a query is executed, it creates a new in-memory object with the corresponding data from the database.

# @student is a in-memory object representing the first row in the Students table.
@student = Student.first

Your example

Here is your example with comments for explanation

it "should be saved as all lower-case" do
    # @user is an in-memory ruby object. You set it's email to "Foo@ExAMPle.CoM"
    @user.email = mixed_case_email

    # You persist that objects attributes to the database.
    # The database stores the email as downcase probably due to a database constraint or active record callback.
    @user.save

    # While the database has the downcased email, your in-memory object has not been refreshed with the corresponding values in the database. 
    # In other words, the in-memory object @user still has the email "Foo@ExAMPle.CoM".
    # use reload to refresh @user with the values from the database.
    expect(@user.reload.email).to eq mixed_case_email.downcase
end

To see a more thorough explanation see this post.



回答3:

reload 

Reloads the attributes of object(here @user) from the database. It always ensures object has latest data that is currently stored in database.

With this we can also avoid

ActiveRecord::StaleObjectError

This normally comes when we try to change old version of the object.



回答4:

It should be the same thing. The whole point is that the reload method reloads the object from the database. Now you can check if your newly created test object is actually saved with the correct/expected attributes.



回答5:

What the example wants to check is whether the before_save callback in app/models/user.rb does its job. The before_save callback should set every user's email to downcase before it is saved in the database, thus chapter 6 exercise 1 wants to test if its value in the database, that can be retrieved by using method reload, is effectively saved as downcase.