I made a reply function for Rails Tutorial extension (Part 2): Change model

This is a continuation of the last time about creating a reply function in Chapter 14 of Rails Tutorial.

Identify what to do in the coding work

I will review the work to be done and wash it out. ・ Create a branch with git ・ Make test    model、integration

Add column to model

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)

1 sender

Since it is a micropost that I posted, it will be displayed even with the current function.

2 recipients

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.

3 Third party

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.

Conditional SELECT statement

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

Create a branch as usual.

ubuntu:~/environment/sample_app (master) $ git checkout -b reply-micropost

Add column to model

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

Create a model test

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

Create integration test

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

Change feed

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 %>

Other tests fail due to changing feed

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.

Display and confirm on screen

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.

Time required

7.0 hours from 10/2 to 10/10.

Recommended Posts

I made a reply function for Rails Tutorial extension (Part 2): Change model
I made a reply function for the Rails Tutorial extension (Part 5):
I made a reply function for the Rails Tutorial extension (Part 4): A function that makes the user unique
I tried to make a message function of Rails Tutorial extension (Part 1): Create a model
I tried to make a reply function of Rails Tutorial extension (Part 3): Corrected a misunderstanding of specifications
Rails Tutorial Extension: I created a follower notification function
I tried to make a message function of Rails Tutorial extension (Part 2): Create a screen to display
Tutorial to create a blog with Rails for beginners Part 1
Tutorial to create a blog with Rails for beginners Part 2
Tutorial to create a blog with Rails for beginners Part 0
I made a simple recommendation function.
Rails Tutorial Extension: I tried to create an RSS feed function
I made a Diff tool for Java files
I made a Ruby extension library in C
I made a LINE bot with Rails + heroku
I made a portfolio with Ruby On Rails
I made a function to register images with API in Spring Framework. Part 1 (API edition)
I made a Docker image of SDAPS for Japanese
I made a check tool for the release module
I made a method to ask for Premium Friday
I want to define a function in Rails Console
I made a library for displaying tutorials on Android.
I made a function to register images with API in Spring Framework. Part 2 (Client Edition)
I made a Japanese version of Rails / devise automatic email
I made a development environment with rails6 + docker + postgreSQL + Materialize.
I want to implement a product information editing function ~ part1 ~
Rails: I've summarized the model and database for a moment.
I made a butler who summarizes tweets [Maximum covering model]
[Rails] Implemented a pull-down search function for Active Hash data
I made a chat app.
[Rails] Implementation of tutorial function
[Rails] I made a simple calendar mini app with customized specifications.
Implement a refined search function for multiple models without Rails5 gem.
I want to add a browsing function with ruby on rails
I made a Ruby container image and moved the Lambda function
I made a question that can be used for a technical interview
I made a method to ask for Premium Friday (Java 8 version)
Rails Tutorial (6th Edition) -Explanation of background operation for each function-
I tried to make a group function (bulletin board) with Rails