・ 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.
Well, Chapter 13. There are only two chapters left at the earliest. This time, we will implement a user message posting function called Micro Post. In short, it's Twitter. Let's be brave! !! Click here for today's BGM. ARIA vocal collection I wonder if the time will come when you can work remotely while drinking a latte in Neo Venice ...
Execute Micropost.new on the Rails console and assign the instance to the variable micropost. Then try substituting the id of the first user for user_id and "Lorem ipsum" for content. What's in the magic columns (created_at and updated_at) of the micropost object at this point?
The named route in Table 12.1 states that _url should be used instead of _path. Why? Think about it. Tip: For the same reason as the account activation exercise (11.1.1.1).
Let's save the micropost object created earlier in the database. Magic column again at this point → Collectively don
>> micropost = Micropost.new(user_id: 1, content: "Lorem ipsum")
=> #<Micropost id: nil, content: "Lorem ipsum", user_id: 1, created_at: nil, updated_at: nil>
>> micropost.created_at
=> nil
>> micropost.updated_at
=> nil
>> micropost.user
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "[email protected]", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil>
>> micropost.user.name
=> "Example User"
>> micropost.save
(0.1ms) SAVEPOINT active_record_1
SQL (1.3ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2020-09-21 02:33:40.343372"], ["updated_at", "2020-09-21 02:33:40.343372"]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> true
>> micropost.created_at
=> Mon, 21 Sep 2020 02:33:40 UTC +00:00
>> micropost.updated_at
=> Mon, 21 Sep 2020 02:33:40 UTC +00:00
>> micropost = Micropost.new(user_id: " ", content: " ")
=> #<Micropost id: nil, content: " ", user_id: nil, created_at: nil, updated_at: nil>
>> micropost.valid?
=> false
>> micropost.errors.messages
=> {:user=>["must exist"], :user_id=>["can't be blank"], :content=>["can't be blank"]}
2. Open the console and try creating a micropost object with an empty user_id and a content of 141 characters or more. Let's make sure that running valid? On this object fails. Also, what is written in the generated error message? → Below
>> micropost = Micropost.new(user_id: " ", content: "a" * 141)
=> #<Micropost id: nil, content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...", user_id: nil, created_at: nil, updated_at: nil>
>> micropost.valid?=> false
>> micropost.errors.messages=> {:user=>["must exist"], :user_id=>["can't be blank"], :content=>["is too long (maximum is 140 characters)"]}
belongs_to: to belongs to the following. The side that sets this can be used as a method. has_many: Set on the side that becomes the parent with the tie.
>> user = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "[email protected]", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil>
>> micropost = user.microposts.create(content: "Lorem ipsum")
(0.1ms) SAVEPOINT active_record_1
SQL (1.8ms) INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2020-09-21 03:35:41.329488"], ["updated_at", "2020-09-21 03:35:41.329488"]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2020-09-21 03:35:41", updated_at: "2020-09-21 03:35:41">
2. A new micropost should have been added to the database in the previous exercise. Run user.microposts.find (micropost.id) to see if it was really added. Also, what will happen if you change the micropost.id part you just executed to micropost? → Below
>> user.microposts.find(micropost.id)
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? AND "microposts"."id" = ? LIMIT ? [["user_id", 1], ["id", 1], ["LIMIT", 1]]
=> #<Micropost id: 1, content: "Lorem ipsum", user_id: 1, created_at: "2020-09-21 03:35:41", updated_at: "2020-09-21 03:35:41">
>> user.microposts.find(micropost)
Traceback (most recent call last):
1: from (irb):6
ArgumentError (You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.)
3. What will happen to the result of executing user == micropost.user? Also, what will happen to the result of executing user.microposts.first == micropost? Please check each. → Below
>> user == micropost.user
=> true
>> user.microposts.first == micropost
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ? [["user_id", 1], ["LIMIT", 1]]
=> true
default_scope: Used when you want to apply a scope to all queries in the model. dependent:: destroy: set to has_many. When the parent is deleted, all associated child databases are deleted. In addition, various things have appeared, so I have summarized them in a glossary.
>> Micropost.first.created_at
Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]]
=> Mon, 21 Sep 2020 04:56:04 UTC +00:00
>> Micropost.last.created_at
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]]
=> Mon, 21 Sep 2020 04:55:35 UTC +00:00
2. What happens to the SQL statement issued when Micropost.first is executed? Similarly, what happens to Micropost.last? Hint: When each is executed on the console The character string displayed in is the SQL statement. → Below
>> Micropost.first
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ? [["LIMIT", 1]]
=> #<Micropost id: 2, content: "jaoivhiua", user_id: 2, created_at: "2020-09-21 04:56:04", updated_at: "2020-09-21 04:56:04">
>> Micropost.last
Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ? [["LIMIT", 1]]
=> #<Micropost id: 1, content: "krutinb", user_id: 1, created_at: "2020-09-21 04:55:35", updated_at: "2020-09-21 04:55:35">
3. Assign the first user on the database to the variable user. What is the id of the micropost that the user object first posted? Next, try deleting the user object using the destroy method. If you delete it, let's check in Micropost.find that the micropost associated with that user is also deleted. → Below
>> user = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "[email protected]", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil>
>> user.microposts.first.id
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["user_id", 1], ["LIMIT", 1]]
=> 1
>> user.destroy
(0.1ms) SAVEPOINT active_record_1
Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]]
SQL (0.1ms) DELETE FROM "microposts" WHERE "microposts"."id" = ? [["id", 1]]
SQL (0.7ms) DELETE FROM "users" WHERE "users"."id" = ? [["id", 1]]
(0.1ms) RELEASE SAVEPOINT active_record_1
=> #<User id: 1, name: "Example User", email: "[email protected]", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil>
>> Micropost.find(1)
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["id", 1], ["LIMIT", 1]]
Traceback (most recent call last):
1: from (irb):13
ActiveRecord::RecordNotFound (Couldn't find Micropost with 'id'=1)
As briefly explained in 7.3.3, the time_ago_in_words method used as the helper method this time can be called from the helper object of the Rails console. Let's execute 3.weeks.ago and 6.months.ago using the time_ago_in_words method of this helper object.
What kind of result will be returned when you execute helper.time_ago_in_words (1.year.ago)? → Collectively below
>> helper.time_ago_in_words(3.weeks.ago)
=> "21 days"
>> helper.time_ago_in_words(6.months.ago)
=> "6 months"
>> helper.time_ago_in_words(1.year.ago)
=> "about 1 year"
3. What is the class of the microposts object? Hint: As you can see in the code in Listing 13.23, first get the object with the paginate method (argument is page: nil) and then call the class method. → Below
s' for #<Class:0x0000000005968db8>)
>> user = User.first
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "[email protected]", created_at: "2020-09-17 08:34:09", updated_at: "2020-09-17 08:34:09", password_digest: "$2a$10$.EZN.AXBx91cG82BFOaKp.qpuwRpmG5N1JASh6KIBnv...", remember_digest: nil, admin: true, activation_digest: "$2a$10$mQpfXtRYM2s5JyNF243gYOln7RRrGaHHlilpOHouLfk...", activated: true, activated_at: "2020-09-17 08:34:09", reset_digest: nil, reset_sent_at: nil>
>> microposts = user.microposts.paginate(page: nil)
Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ? [["user_id", 1], ["LIMIT", 11], ["OFFSET", 0]]
=> #<ActiveRecord::AssociationRelation []>
>> microposts.class
=> Micropost::ActiveRecord_AssociationRelation
>> (1..10).to_a.take(6)
=> [1, 2, 3, 4, 5, 6]
2. Do you really need the to_a method part from the previous exercise? Please check. → You can do it without it.
>> (1..10).take(6)
=> [1, 2, 3, 4, 5, 6]
3. Faker supports a wide variety of cases other than lorem ipsum. Learn how to print to the screen while looking at the Faker documentation, and actually print the university name, phone number, Hipster Ipsum and Chuck Norris facts to the screen. (Translation: Of course, it also supports Japanese, for example, there is faker-okinawa that outputs Okinawan terms. Please play with it.) → Below (If you budget faker-okinawa as it is, an error will occur, so comment out the normal faker gem before bundling.)
>> Faker::University.name
=> "East Abshire University"
>> Faker::PhoneNumber.cell_phone
=> "1-315-982-9239"
>> Faker::Hipster.sentence
=> "Mumblecore pug tilde marfa drinking 8-bit."
>> Faker::ChuckNorris.fact
=> "Chuck Norris can binary search unsorted data."
>> Faker::Okinawa::Awamori.name
=> "Mizuho"
Appeared again response.body. Contains full HTML. Find the relevant element in the HTML with assert_match. A more abstract method than assert_select. 'h1> img.gravatar' ⇨ A nest called img tag with gravatar class inside the h1 tag.
ruby:show.thml.erb
<h1>
<%#= gravatar_for @user %>
<%#= @user.name %>
</h1>
2. Let's change the test in Listing 13.28 to test that will_paginate is displayed only once. Tip: See Table 5.2. → Just add count; 1.
users_profile_test.rb
test "profile display" do
get user_path(@user)
assert_template 'users/show'
assert_select 'title', full_title(@user.name)
assert_select 'h1', text: @user.name
assert_select 'h1>img.gravatar'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination', count: 1
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
ruby:home.thml.erb
<% if logged_in? %>
<%= render 'logged_in' %>
<% else %>
<%= render 'not_logged_in' %>
<% end %>
ruby:_logged_in.html.erb
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
</div>
ruby:_not_logged_in.html.erb
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>
<%= link_to image_tag("rails.png ", alt: "Rails logo"),
'http://rubyonrails.org/' %>
2. Open the console and assign the first user on the database to the user variable. Then try running Micropost.where ("user_id =?", User.id), user.microposts, and user.feed, respectively, to make sure they all have the same results. Tip: You can easily tell if the results are the same by comparing with ==. → It's all the same.
>> Micropost.where("user_id = ?", user.id) == user.microposts
Micropost Load (0.9ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC [["user_id", 1]]
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC
=> true
>> Micropost.where("user_id = ?", user.id) == user.feed
=> true
>> user.microposts == user.feed
Micropost Load (0.3ms) SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC
=> true
request.referrer method: Returns the previous URL.
2. redirect_to request.referrer || root_redirect url line_back(fallback_location: root_url)Let's use a browser to confirm that it works well even if it is replaced with(This method is new in Rails 5)。 → It works.
2. Let's test the total number of microposts posted in the sidebar. At this time, also test whether the singular (micropost) and plural (microposts) are displayed correctly. Tip: Use Listing 13.57 as a guide. → Is this all right?
microposts_interface_test.rb
test "micropost sidebar count" do
log_in_as(@user)
get root_path
assert_match "#{@user.microposts.count} microposts", response.body
other_user = users(:malory)
log_in_as(other_user)
get root_path
assert_match "0 microposts", response.body
other_user.microposts.create!(content: "A micropost")
get root_path
assert_match "1 micropost", response.body
end
2. Test the image uploader implemented in 13.4 by referring to the template shown in Listing 13.63. To prepare for the test, first add the sample images to the fixture directory (command example: cp app / assets / images / rails.png test / fixtures /). The test added in Listing 13.63 checks the file upload on the Home page and whether the image is displayed when the post is successful. Note that the method called fixture_file_upload in the test is a special method that uploads the file defined by fixture18. Tip: To check if the picture attribute is valid, use the assigns method introduced in 11.3.3. This method will allow you to access the micropost in the create action after a successful post. → Below. The test does not become GREEN here. I can't find a part that seems to be wrong, so I checked it and solved it. Refer to this article After restarting spring, it became GREEN.
microposts_interface_test.rb
test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
assert_select 'input[type="file"]'
#Invalid transmission
post microposts_path, params: { micropost: { content: "" } }
assert_select 'div#error_explanation'
#Valid transmission
content = "This micropost really ties the room together"
picture = fixture_file_upload('test/fixtures/rails.png', 'image/png')
assert_difference 'Micropost.count', 1 do
post microposts_path, params: { micropost: {
content: content,
picture: picture } }
end
assert assigns(:micropost).picture?
follow_redirect!
assert_match content, response.body
#Delete a post
assert_select 'a', text: 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
#Access different user profiles(Make sure there is no delete link)
get user_path(users(:archer))
assert_select 'a', text: 'delete', count: 0
end
What happens if I try to send an image file of 5MB or more?
What happens if I try to send a file with an invalid extension? → In both cases, there is no corresponding file ... You will probably get an error.
2. If you have already added the tests in Listing 13.63, you may get a confusing error message when running the test suite at this point. Let's get rid of this error. Tip: Modify the config file in Listing 13.68 to prevent CarrierWave from resizing the image during testing. → I didn't throw an error, but I did it.
・ Microposts are also resources. -Link data tables with belongs_to and has_many. The child side can be used as a method. -Changed the display order in the default scope. When I looked up an article that seemed to be careful about how to use it, it came out. -Use the dependent:: destroy option to delete the associated object and itself at the same time. -By using ActiveRecord, you rarely use raw SQL. -Implemented image upload with CarrierWave. (Is the standard Active Storage now?)
End of micro post mounting ~ ~. There was quite a lot here. Especially the test and doing itself are often reviewed, but there was a lot. Now sample_app is like that too. Now it's time to enter Chapter 14. From user follow-up to status feed implementation, it's the last spurt!
⇨ Go to Chapter 14! ⇦ Click here for Chapter 12 Click here for premise and author status for learning
・ Proc An object of a block ({} or do ~ end). Blocks are not objects, so you need to make them objects with proc. See here
・ Scope (method) A collection of specific queries. Avoid writing lengthy code over and over again by putting together things that you use many times.
・ Lambda type Anonymous function. As the name implies, it's an unnamed function.
・ SQL injection An attack method that illegally manipulates a database system by intentionally using an application security deficiency and executing an SQL statement that the application does not expect. Also, a vulnerability that enables the attack.
・ MIME (Multipurpose Internet Mail Extension) A standard that allows Internet e-mail, which can only use US-ASCII text, to handle various formats.
Recommended Posts