This is a continuation of the last time about creating a reply function in Chapter 14 of Rails Tutorial.
I will review the work to be done and wash it out. ・ Create a branch with git ・ Make test model、integration
Find in the text how to add a column for model. You can see that we were adding columns with migrate in 9.1.1.
Think about how to make the display by reply in the model. Let's look at the specifications again. @reply should only appear in the recipient's feed and the sender's feed. Think about it in three parts depending on the person who displays it. 1 sender, 2 recipient, 3. Third party (neither sender nor recipient)
Since it is a micropost that I posted, it will be displayed even with the current function.
There are two possibilities: the recipient is following the sender and the recipient is not. If the recipient is following the sender, it will still be visible in the current feature. If the recipient does not follow the sender, it will not be displayed with the current feature. You can see that we need to add functionality here.
The function is to display in_reply_to if you are equal. I thought I'd make a change to what the method user.micrposts returns, but it doesn't work. I didn't post it. It does not select the sender and return the person's micropost. Regardless of who posted It is a function to display all microposts that are equal to in_reply_to.
The idea is that the more microposts you have, the higher the risk of performance issues. It seems necessary to add an index as a countermeasure. I understand that it is a risk to search by alternate key instead of searching by primary key.
Going back to how to make it, this implementation method will display it regardless of whether you are following the sender, so I found that it is not necessary to separate the cases.
Next, think about which method to add the functionality to. Looking at Listing 14.46, the feed method in the User model looks good. It seems that you can specify the condition with where.
When a third party displays the recipient's feed screen, the reply does not appear. If you create a function with the above specifications, it will be displayed. Therefore, it is necessary to add a function that is not displayed. If you are not equal to in_reply_to, you can hide it. At first I thought I should make some changes to what the user.micrposts method returns. As I mentioned above, I knew that the feed method would be good at the recipient, so I intuitively felt that the same method was good.
Read 14.3.2 again.
From the microposts table, select all microposts that have an id that corresponds to the user that a user (that is, yourself) is following.
From the microposts table, select all the replies that you are the recipient of.
SELECT * FROM microposts
WHERE user_id IN (<list of ids>)
OR user_id = <user id>
OR in_reply_to = <user id>
I think it should be done.
Listing 14.44
def feed
Micropost.where("user_id IN (?) OR user_id = ? OR in_reply_to = ?", following_ids, id,id)
end
Looks good.
I judge that the specifications have been suppressed with this, and decide to make it.
Create a branch as usual.
ubuntu:~/environment/sample_app (master) $ git checkout -b reply-micropost
Find in the text how to add columns to the model. 6.3.1 I was adding a column with "Hashed Password". Make it in the same way.
ubuntu:~/environment/sample_app (reply-micropost) $ rails generate migration add_reply_to_microposts in_reply_to:integer
db/migrate/20201003003147_add_reply_to_microposts.rb
class AddReplyToMicroposts < ActiveRecord::Migration[5.1]
def change
add_column :microposts, :in_reply_to, :integer
end
end
Next, create a model test. 13.1.2 Proceed in the same way as Micropost validation. As for which model to test, I added a column to micropost, so I think it is micropost.
Create a reply micropost based on the sender and recipient of the reply.
When you reply, the test displayed on the feed screen will be the ingegration test. Before that, think about what to test with the model test.
As a test of the function of the method added by reply, it seems that update and delete are not in CRUD, so you need to create and read reply. I will try it on the console.
>> user2.microposts.create!(content: "test2", in_reply_to: 1 )
>> user2.microposts.create!(content: "test3", in_reply_to: 1 )
>> Micropost.where(in_reply_to: 1)
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."in_reply_to" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? [["in_reply_to", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Micropost id: 304, content: "test3", user_id: 30, created_at: "2020-10-03 02:37:38", updated_at: "2020-10-03 02:37:38", picture: nil, in_reply_to: 1>, #<Micropost id: 303, content: "test2", user_id: 30, created_at: "2020-10-03 02:18:35", updated_at: "2020-10-03 02:18:35", picture: nil, in_reply_to: 1>]>
If the existing method is enough, it seems that testing is not necessary because model does not add functions, but I will write it for the time being.
test/models/micropost_test.rb
test "reply should be returned" do
@reply_post = @reply_sender.microposts.create!(content: "reply test1", in_reply_to: @user.id)
assert Micropost.where(in_reply_to: @user.id).include?(@reply_post)
end
Now that we have tested the model, let's create an integration test. The content of the test is to create one reply and display it on the recipient's feed screen.
Before that, think about where to make the changes. I thought I needed to make a test of the result as I would make changes to the controller.
Let's start with the integration test. Looking for a test that seems to be helpful, following_test.rb looks good. Also, the place to post is copied based on microposts_interface_test.rb.
test/integration/reply_test.rb RED
test "reply to user " do
log_in_as(@user)
content = "@reply #{@other.name} Cum aspermatur"
post microposts_path, params: { micropost: {content: content }}
log_in_as(@other)
get root_path
assert_not @other.following?(@user)
#get root_path
assert_match content, response.body
#assert_match content, response.body
end
Make changes to the feed to display a reply.
First, create test data. Add data to the fixture.
test/fixtures/microposts.yml
tonton:
content: "@reply malory tonton is the name of the panda."
created_at: <%= Time.zone.now %>
user: michael
Where to test the feed, but I think it's a model test because it changes to the user model.
test/models/user_test.rb RED
test "feed should have the reply posts" do
michael = users(:michael)
malory = users(:malory)
reply_post = microposts(:tonton)
assert michael.feed.include?(reply_post)
assert malory.feed.include?(reply_post)
puts reply_post.content
end
end
Change the feed.
app/models/user.rb
def feed
following_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
Micropost.where("user_id IN (#{following_ids})
OR user_id = :user_id
OR in_reply_to = reply_id",
user_id: id,
reply_id: id )
end
tonton:
content: "@reply malory tonton is the name of the panda."
created_at: <%= Time.zone.now %>
user: michael
in_reply_to: <%= User.find_by(name: "Malory Archer").id %>
Changing the feed caused other tests to fail. See the error message.
ubuntu:~/environment/sample_app (reply-micropost) $ rails test test/models/micropost_test.rb
Running via Spring preloader in process 2886
Started with run options --seed 405
FAIL["test_order_should_be_most_recent_first", MicropostTest, 0.5243559590000189]
test_order_should_be_most_recent_first#MicropostTest (0.52s)
--- expected
+++ actual
@@ -1 +1 @@
-#<Micropost id: 941832919, content: "Writing a short test", user_id: 762146111, created_at: "2020-10-10 01:35:16", updated_at: "2020-10-10 01:35:17", picture: nil, in_reply_to: nil>
+#<Micropost id: 981300582, content: "@reply malory tonton is the name of the panda.", user_id: 762146111, created_at: "2020-10-10 01:35:17", updated_at: "2020-10-10 01:35:17", picture: nil, in_reply_to: 659682706>
test/models/micropost_test.rb:33:in `block in <class:MicropostTest>'
6/6: [===================================================================================================] 100% Time: 00:00:00, Time: 00:00:00
I am getting an error message that there is a problem with the test data. I think that the cause is that the test data with the latest posting time was created as most_recent in fixture, but tonton had a newer posting time. It is not necessary to update the posting time, so correct the posting time of tonton.
test/fixtures/microposts.yml before change
tonton:
content: "@reply malory tonton is the name of the panda."
created_at: <%= Time.zone.now %>
user: michael
test/fixtures/microposts.after changing yml
tonton:
content: "@reply malory tonton is the name of the panda."
created_at: <%= 2.minutes.ago %>
user: michael
in_reply_to: <%= User.find_by(name: "Malory Archer").id %>
It was repaired safely.
Make sure that the micropost with @reply is displayed on the page. Create sample data by referring to Listing 13.25: "Adding Microposts to Sample Data".
db/seeds.rb
# reply
sender = users.first
reciever = users.second
reply_content = "@reply #{receiver.name} reply test"
sender.microposts.create!(content: reply_content,
in_reply_to: receiver.id )
When I raised the rails server and displayed the screen, it was confirmed that the micropost of reply was displayed.
7.0 hours from 10/2 to 10/10.
Recommended Posts