Since I made a model last time, I will implement a controller this time.
$ rails g controller v1/posts
When executed, a controller and request spec file will be generated.
For the time being, implement the controller as follows.
app/controllers/v1/posts_controller.rb
# frozen_string_literal: true
module V1
#
# post controller
#
class PostsController < ApplicationController
before_action :set_post, only: %i[show update destroy]
def index
# TODO
end
def show
# TODO
end
def create
# TODO
end
def update
# TODO
end
def destroy
# TODO
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.permit(:subject, :body)
end
end
end
Create a controller according to CRUD once without logic. In addition, cutting the namespace called V1 is a method often used in API development. This makes it easier to separate and develop when making version 2 that is not backward compatible.
Then set the routes.
config/routes.rb
# frozen_string_literal: true
Rails.application.routes.draw do
+ namespace "v1" do
+ resources :posts
+ end
end
This will set the CRUD routes. Let's check.
$ rails routes
...
Prefix Verb URI Pattern Controller#Action
v1_posts GET /v1/posts(.:format) v1/posts#index
POST /v1/posts(.:format) v1/posts#create
v1_post GET /v1/posts/:id(.:format) v1/posts#show
PATCH /v1/posts/:id(.:format) v1/posts#update
PUT /v1/posts/:id(.:format) v1/posts#update
DELETE /v1/posts/:id(.:format) v1/posts#destroy
...
As usual, implement the test first. As a behavior
I will go. I won't include a pager for a tutorial on a simple test application, but maybe I'll write an article in the future.
spec/requests/v1/posts_controller.rb
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "V1::Posts", type: :request do
describe "GET /v1/posts#index" do
before do
create_list(:post, 3)
end
it "Normal response code is returned" do
get v1_posts_url
expect(response.status).to eq 200
end
it "The number is returned correctly" do
get v1_posts_url
json = JSON.parse(response.body)
expect(json["posts"].length).to eq(3)
end
it "Responses are returned in descending order of id" do
get v1_posts_url
json = JSON.parse(response.body)
first_id = json["posts"][0]["id"]
expect(json["posts"][1]["id"]).to eq(first_id - 1)
expect(json["posts"][2]["id"]).to eq(first_id - 2)
end
end
end
create_list (: post, 3)
is the process of creating 3 records of post and saving them in the DB.In other words, to summarize the behavior, LINE 10: it "Normal response code is returned" Block start LINE 8: 3 posts are saved LINE 11: Make a get request to v1_posts_url (v1 / posts / index) LINE 12: Response code is: ok (200 normal) LINE 13: it "Normal response code is returned" Block end. Rolled back to 0 post records LINE 14: it "The number is returned correctly" Block start LINE 8: 3 posts are saved LINE 15: Make a get request to v1_posts_url (v1 / posts / index) LINE 16: Convert response.body to JSON.parse and convert to Ruby array LINE 17: Response post is 3 records LINE 18: it "The number is returned correctly" Block end. Rolled back to 0 post records ↓ ...
It will be.
At this point, the controller is not implemented, so of course the test is moss.
Strictly speaking, the final test requires a comparison of created_at, but a simple comparison is made by id. Originally, you should also test limit, but omit it. If you're curious, try implementing a test that makes 21 create_lists and confirms that only 20 are returned.
Tips.
By the way, I will introduce the factoryBot method that I often use.
build (: post)
post is generated 1 record in memory. It will not be reflected in the DB unless it is saved. Equivalent to Post.newcreate (: post)
post and save it in DB. Equivalent to Post.create!build_list (: post, 5)
Generate 5 records of post. Multiple versions of buildcreate_list (: post, 5)
Create 5 records for post. Multiple versions of createIn addition, the requests test requires the following measures.
config/application.rb
...
config.hosts << ".amazonaws.com"
+ config.hosts << "www.example.com"
...
This is because rspec tests are recognized as requests from www.example.com.
app/controllers/v1/posts_controller.rb
...
def index
- # TODO
+ posts = Post.order(created_at: :desc).limit(20)
+ render json: { posts: posts }
end
...
Now you can get the list. I will try to hit the API with curl.
$ curl localhost:8080/v1/posts
{"posts":[{"id":2,"subject":"","body":"hoge","created_at":"2020-09-06T01:07:52.628Z","updated_at":"2020-09-06T01:07:52.628Z"},{"id":1,"subject":"hoge","body":"fuga","created_at":"2020-09-05T13:50:01.797Z","updated_at":"2020-09-05T13:50:01.797Z"}]}
If you get empty data, try creating a post record from rails c
.
Once you've done that, don't forget to run rubocop or rspec, then git commit.