・ Rails tutorial is the 4th edition ・ This study is the 3rd lap (2nd lap after Chapter 9) ・ The author is a beginner who has done all of Progate.
・ If you read it, you will not understand it. ・ Search and summarize terms that you do not understand (at the bottom of the article, glossary). ・ Dive into what you do not understand. ・ Work on all exercises. ・ Do not copy chords as much as possible.
Next, we will enter Chapter 6. From here to Chapter 12, it seems that we will start developing a login and authentication system. It's a long time, but let's do it.
Click here for today's song. 17 years old and the wall of Berlin "Prism" I haven't cultivated music so much in the last few years, but a good domestic shoegaze band will come out.
2. Almost all migrations can be undone (at least in this tutorial you can undo all migrations). Undoing is called "rollback" and can be achieved in Rails with the command db: rollback.
$ rails db:rollback
After running the above command, check the contents of db / schema.rb to see if the rollback was successful (Column 3.1 also summarizes other techniques for migration, so please refer to it. Please give me). The above command internally calls the drop_table command to drop the users table from the database. This works because the change method knows that drop_table and create_table correspond to each other. Knowing this correspondence makes it easy to achieve reverse migration for rollback. In the case of irreversible migration such as deleting a certain column, it is necessary to define up and down methods separately instead of the change method. For more information, see Active Record Migration in the Rails Guide. → Execute rails db: migrate. The contents of the schema have disappeared.
schema.rb
ActiveRecord::Schema.define(version: 0) do
end
>> user = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> user.class
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime)
>> user.class.superclass
=> ApplicationRecord(abstract)
>> user.class.superclass.superclass
=> ActiveRecord::Base
>> user.name.class
=> String
>> user.email.class
=> String
2. Which class instances are created_at and updated_at? → Both ActiveSupport :: TimeWithZone class (This article may be helpful)
>> user.created_at.class
=> ActiveSupport::TimeWithZone
>> user.updated_at.class
=> ActiveSupport::TimeWithZone
>> User.find_by(name: "shoji")
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "shoji"], ["LIMIT", 1]]
=> #<User id: 2, name: "shoji", email: "[email protected]", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09">
>> User.find_by_name("shoji")
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "shoji"], ["LIMIT", 1]]
=> #<User id: 2, name: "shoji", email: "[email protected]", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09">
2. For practical purposes, User.all can be treated like an array, but it is not actually an array. Examine the objects generated by User.all and make sure they are in the User :: ActiveRecord_Relation class instead of the Array class. → Below (I'm also checking up to the upper class)
>> users = User.all
User Load (0.2ms) SELECT "users".* FROM "users" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 1, name: "miura", email: "[email protected]", created_at: "2020-09-08 22:53:59", updated_at: "2020-09-08 22:53:59">, #<User id: 2, name: "shoji", email: "[email protected]", created_at: "2020-09-08 22:54:09", updated_at: "2020-09-08 22:54:09">]>
>> users.class
=> User::ActiveRecord_Relation
>> users.class.superclass
=> ActiveRecord::Relation
>> users.class.superclass.superclass
=> Object
>> users.class.superclass.superclass.superclass
=> BasicObject
>> users.class.superclass.superclass.superclass.superclass
=> nil
3. Make sure that when you call the length method on User.all, you will be asked for that length (4.2.3). One of the properties of Ruby is that you can somehow understand how to handle objects without knowing the class in detail. This is called duck typing and is often expressed in the following maxim: "If you look like a duck and sound like a duck, it's already a duck." By the way, in the keynote speech of RubyKaigi 2016, Ruby author Matz explained duck typing. It's a short and easy-to-understand explanation for a few minutes, so please watch it!) → The number of data is displayed.
>> User.all.length
User Load (0.2ms) SELECT "users".* FROM "users"
=> 2
>> user1.name = "yongon"
=> "yongon"
>> user1.save
(0.1ms) SAVEPOINT active_record_1
SQL (0.6ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "yongon"], ["updated_at", "2020-09-08 23:12:52.428275"], ["id", 1]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
2. Now try updating and saving the email attributes using update_attributes. → The following (the id of making various mistakes is incorrect)
>> user1.update_attributes(name: "yongon", email: "[email protected]")
(0.1ms) SAVEPOINT active_record_1
SQL (0.1ms) UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ? [["email", "[email protected]"], ["updated_at", "2020-09-09 03:12:33.687572"], ["id", 4]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
3. In the same way, please check that the magic column created_at can also be updated directly. Tip: It is convenient to use "1.year.ago" when updating. This is one of the Rails-style time specifications, and it calculates the time one year ago from the current time. → Below
>> user1.update_attribute(:created_at, 1.year.ago)
(0.1ms) SAVEPOINT active_record_1
SQL (1.1ms) UPDATE "users" SET "created_at" = ?, "updated_at" = ? WHERE "users"."id" = ? [["created_at", "2019-09-09 03:18:24.829284"], ["updated_at", "2020-09-09 03:18:24.830017"], ["id", 4]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
If you use the setup method, the process written in the method will be executed just before the test. If you define an instance variable in this, you can use it in all tests.
>> user = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> user.valid?
=> true
assert_not @ user.valid? Is RED → It's easy to understand if you think that "@user is valid, isn't it?", But there is a tsukkomi saying "Valid!". As it appears in validates, {} can be omitted when passing a hash as the last argument of the method.
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> u.valid?
=> false
>> u.errors.full_messages
=> ["Name can't be blank", "Email can't be blank"]
2. Execute u.errors.messages and check that the errors can be obtained in hash format. How can I get only the error information about email? → I wrote on this page. . messages It's the same with or without it.
>> u.errors.messages
=> {:name=>["can't be blank"], :email=>["can't be blank"]}
>> u.errors[:email]
=> ["can't be blank"]
>> u.errors.messages[:email]
=> ["can't be blank"]
>> user = User.new(name: "a"*55, email: "e"*244 + "@example.com")
=> #<User id: nil, name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", email: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee...", created_at: nil, updated_at: nil>
>> user.valid?=> false
>> user.errors.full_messages=> ["Name is too long (maximum is 50 characters)", "Email is too long (maximum is 255 characters)"]
Do I need to remember regular expressions? It's complicated, so it's better to check and implement it each time.
2. As mentioned earlier, the regular expression that checks the email address in Listing 6.21 allows invalid email addresses with consecutive dots, such as [email protected]. First, add this email address to the list of invalid email addresses in Listing 6.19 and make sure this causes the test to fail. Then use the slightly more complex regular expression shown in Listing 6.23 to verify that this test passes. → Just do this too. 3. Add [email protected] to the list of Rubular email addresses and try using the regular expression in Listing 6.23 in Rubular. Let's make sure that only valid email addresses match and not all invalid email addresses. → Confirmed.
The content here is a little confusing, but the point is that the email address is set to be case insensitive. Does the test make sure that uppercase users are not valid (the same address for uppercase)? And a callback method appeared to make everything lowercase before the email address was stored in the database. After a quick look, the use of callbacks seems to be careful. It seems that you should avoid conditional branching in the callback.
Let's add a test to lowercase email addresses to Listing 6.26, as shown in Listing 6.33. By the way, in the test code to be added, the reload method that updates according to the value in the database and the assert_equal method that checks whether the values match are used. To see if the tests in Listing 6.33 are working, comment out the before_save line to make it red, and uncomment it to make it green. → Execute as instructed. Since it is converted to lowercase before saving with before_save, it will be RED when commented out and GREEN when canceled.
Let's rewrite the before_save callback to email.downcase! While checking the execution result of the test suite. Tip: You can change the email attribute directly by adding a! To the end of the method (Listing 6.34). → The "!" That appears here represents a destructive process. This means that your email address will remain converted to lowercase. Even if it is rewritten, the test is GREEN.
>> user = User.new(name: "kote", email: "[email protected]")
=> #<User id: nil, name: "kote", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: nil>
>> user.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "[email protected]"], ["LIMIT", 1]]
=> false
>> user.errors.messages
=> {:password=>["can't be blank"]}
>> user = User.new(name: "kote", email: "[email protected]", password: "kotte")
=> #<User id: nil, name: "kote", email: "[email protected]", created_at: nil, updated_at: nil, password_digest: "$2a$10$7Svz/KnRoF7zab0PnhKFL.n/OsSltRvvREHECcmuq.D...">
>> user.valid? User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ? [["email", "[email protected]"], ["LIMIT", 1]]
=> false
>> user.errors.messages=> {:password=>["is too short (minimum is 6 characters)"]}
>> user = User.find(1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "[email protected]", created_at: "2020-09-10 02:37:56", updated_at: "2020-09-10 02:37:56", password_digest: "$2a$10$A5n.HFBigQfwnWVJZw2N0e4M9sxPaR8ndLZwqtZWYS7...">
2. If you can find the object, replace the name with a new character string and update it with the save method. It doesn't work ... why didn't it work? → Below. Since the save method tries to update all attributes, it seems that an error is thrown when updating the password.
>> user.name = "meshino"
=> "meshino"
>> user.save
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "[email protected]"], ["id", 1], ["LIMIT", 1]]
(0.0ms) rollback transaction
=> false
>> user.errors.messages
=> {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
3. Now try updating the user's name using the technique introduced in 6.1.5. → So, update using update_attribute. (However, since the user who threw the error earlier is being reused, it is not valid even after updating the name. It seems that the password needs to be reset.)
>> user.update_attribute(:name, "nakamura")
(0.1ms) begin transaction
SQL (4.2ms) UPDATE "users" SET "name" = ?, "updated_at" = ? WHERE "users"."id" = ? [["name", "nakamura"], ["updated_at", "2020-09-10 03:07:11.190666"], ["id", 1]]
(6.4ms) commit transaction
=> true
>> user.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ? [["email", "[email protected]"], ["id", 1], ["LIMIT", 1]]
=> false
>> user.errors.messages=> {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
-Create a migration and migrate each time you update the database. -Active Record is convenient. Rails is going down, but Active Record was written somewhere with its advantages. -Set invalid input contents in validation and play. ・ I think it's enough to recognize that regular expressions are probably such things. -Improved search efficiency by adding an index to the database. Uniqueness is also guaranteed. -Since has_secure_password is used by gem, I wonder if it is possible to implement secure processing with other convenient gems. When I searched for it, it came out after all. Let's learn from now on.
It takes time because I am investigating various concerns by detouring. However, I don't think that the content of the tutorial alone will work, so I will be interested in various other things and absorb them. Well next! Chapter 7 is the implementation of user registration! sign up!
⇨ Go to Chapter 7! ⇦ Click here for Chapter 5 Click here for premise and author status for learning
・ Assert_not Since it is denied by not, it means the opposite. If the target is true, it fails, if it is false, it succeeds.
· Active Record callback You can set a method to call before / after some processing. For more information, go to the Rails Guide. ](Https://railsguides.jp/active_record_callbacks.html)
·stub A substitute to prepare at the time of testing. The substitute called from the process under test is the stub, and the substitute that calls the process under test is the driver.
Recommended Posts