Skip to content

Instantly share code, notes, and snippets.

@x1wins
Last active October 17, 2018 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save x1wins/de6563c102378ae3b3c2514d8e8657cc to your computer and use it in GitHub Desktop.
Save x1wins/de6563c102378ae3b3c2514d8e8657cc to your computer and use it in GitHub Desktop.
rails only api, knock authentication, authorization, post model sample, curl sample

Table of contents

create rails project

  • api only
  • rails version 5.1.6 (knock not working in 5.2)
  • postgresql
rails _5.1.6_ new sample-api2 --api --database=postgresql -T

postgresql docker

~/.profile

vi ~/.profile

POSTGRES_USER="postgres"

POSTGRES_DB="postgres"
POSTGRES_CONTAINER_NAME="postgres"
POSTGRES_PASSWORD=""
source ~/.profile


or

echo 'POSTGRES_USER="postgres"

POSTGRES_DB="postgres"
POSTGRES_CONTAINER_NAME="postgres"
POSTGRES_PASSWORD=""
'>> ~/.profile

source ~/.profile


docker run

docker \
run \
--volume ~/postgres:/var/lib/postgresql/data \
--env POSTGRES_USER=${POSTGRES_USER} \
--env POSTGRES_DB=${POSTGRES_DB} \
--env POSTGRES_PASSWORD=${POSTGRES_PASSWORD} \
--name ${POSTGRES_CONTAINER_NAME} \
--publish 5433:5432 \
postgres:9.6.10;

if not working postgresql

gem update --system
xcode-select --install # Then agree to the terms, even if you have done this before!
brew install postgresql
bundle install

https://medium.com/@nick.hartunian/knock-jwt-auth-for-rails-api-create-react-app-6765192e295a

database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  host: localhost
  port: 5433
  username: postgres
  password:
  # For details on connection pooling, see Rails configuration guide
  # http://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: sample-api3_development

seeds.rb

admin = User.new
admin.email = 'admin@bananas.com'
admin.password = 'bananaKing'
admin.password_confirmation = 'bananaKing'
admin.admin = true
admin.save
user = User.new
user.email = 'user@bananas.com'
user.password = 'bananaBro'
user.password_confirmation = 'bananaBro'
user.save

gemfile

gem 'knock'
$ bundle install
$ rails generate knock:install
rake db:create
rake db:migrate
rails generate knock:token_controller user
rails g scaffold user email password_digest admin:boolean
class User < ActiveRecord::Base
  has_secure_password
end
class ApplicationController < ActionController::API
  include Knock::Authenticable

  def is_owner user_id
    unless user_id == current_user.id
      render json: nil, status: 403
      return
    end
  end
end
rails g scaffold Post title:string body:text
rake db:migrate
rake db:seed
rails generate migration AddUserRefToPosts user:references
rake db:migrate
class PostsController < ApplicationController
  before_action :authenticate_user

routes.rb

Rails.application.routes.draw do
  scope '/api' do
    resources :posts
    resources :users
    post 'user_token' => 'user_token#create'
  end
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-three

posts_constroller.rb

def destroy
    if @post.user_id == current_user.id
      @post.destroy
    else
      render json: nil, status: 403
    end
end

or

class PostsController < ApplicationController
  before_action :authenticate_user
  before_action :set_post, only: [:show, :update, :destroy]
  before_action only: [:update, :destroy] do
    is_owner @post.user_id
  end
def post_params
  params.require(:post).permit(:title, :body).merge(user_id: current_user.id)
end

CURL

admin

curl -X POST -i http://localhost:3000/api/v1/user_token -H "Content-Type: application/json" -d '{"auth":{"email":"admin@bananas.com","password":"bananaKing"}}'
curl -X POST -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzgzMzE2NDUsInN1YiI6MX0.dldfr3gqSfS1KlfmrJakiDFIj2qcY1l2jP2unxGu81w" \
-d '{"post":{"title":"hello3","body":"hell"}}'
curl -X GET -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzgzMzE2NDUsInN1YiI6MX0.dldfr3gqSfS1KlfmrJakiDFIj2qcY1l2jP2unxGu81w" | jq

user

curl -X POST -i http://localhost:3000/api/v1/user_token -H "Content-Type: application/json" -d '{"auth":{"email":"user@bananas.com","password":"bananaBro"}}'
curl -X POST -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc" \
-d '{"title":"hello3","body":"hell"}'
curl -X DELETE -i http://localhost:3000/api/v1/posts/1 -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc"
curl -X DELETE -i http://localhost:3000/api/v1/posts/6 -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc"

curl -X POST -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json"
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc"
-d '{"title":"hello123","body":"hell"}'

for loop curl

#!/bin/bash
for i in {1..100000}
do
   echo -e "\nWelcome $i times \n"
   curl -X POST -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
   -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc" \
   -d '{"title":"hello '$i'","body":"hell"}'
done

sample url

curl -X POST -i http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc" \
-d '{"title":"hello123","body":"hell"}'

curl -X DELETE -i http://localhost:3000/api/v1/posts/10256 -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc"
curl -X DELETE -i http://localhost:3000/api/v1/posts/10256 -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzgzMzE2NDUsInN1YiI6MX0.dldfr3gqSfS1KlfmrJakiDFIj2qcY1l2jP2unxGu81w"

apache bench mark

echo "{\"title\":\"hello123\",\"body\":\"hell\"}" > post_loc.txt
$ ab -p post_loc.txt -T application/json \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc' \
-c 100 -n 100000 http://127.0.0.1:3000/api/v1/posts
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            3000

Document Path:          /api/v1/posts
Document Length:        159 bytes

Concurrency Level:      100
Time taken for tests:   573.782 seconds
Complete requests:      100000
Failed requests:        32258
   (Connect: 0, Receive: 0, Length: 32258, Exceptions: 0)
Total transferred:      44864516 bytes
Total body sent:        34100000
HTML transferred:       15932258 bytes
Requests per second:    174.28 [#/sec] (mean)
Time per request:       573.782 [ms] (mean)
Time per request:       5.738 [ms] (mean, across all concurrent requests)
Transfer rate:          76.36 [Kbytes/sec] received
                        58.04 kb/s sent
                        134.40 kb/s total

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0      30
Processing:    14  573 104.9    554    1678
Waiting:        8  572 104.9    553    1678
Total:         14  573 104.9    554    1678

Percentage of the requests served within a certain time (ms)
  50%    554
  66%    566
  75%    575
  80%    583
  90%    609
  95%    659
  98%    813
  99%   1254
 100%   1678 (longest request)

curl json reformating

brew install jq
curl -X GET http://localhost:3000/api/v1/posts -H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1Mzc4OTA4NzMsInN1YiI6Mn0.xvHvrFWjK7jV-YE4Q05YzrENqHwFeFPAvwOUSV2b0kc" | jq

testing

rails test

or

rails test test/controllers/posts_controller_test.rb

or

rails test test/controllers/users_controller_test.rb

posts.yml

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  title: MyString
  body: MyText
  user_id: 1

two:
  title: MyString
  body: MyText
  user_id: 2

users.yml

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html

one:
  id: 1
  email: aaaa@aa.com
  password_digest: <%= BCrypt::Password.create('secret', cost: 4) %>
  admin: false

two:
  id: 2
  email: string@aa.com
  password_digest: <%= BCrypt::Password.create('secret', cost: 4) %>
  admin: false

posts_controller_test.rb

require 'test_helper'

class PostsControllerTest < ActionDispatch::IntegrationTest
  setup do
    @post = posts(:one)
  end

  def authenticated_header
    token = Knock::AuthToken.new(payload: { sub: users(:one).id }).token

    {
        'Authorization': "Bearer #{token}"
    }
  end

  test "should get index" do
    get posts_url, as: :json, headers: authenticated_header
    assert_response :success
  end

  test "should create post" do
    assert_difference('Post.count') do
      post posts_url, params: { post: { body: @post.body, title: @post.title } }, as: :json, headers: authenticated_header
    end

    assert_response 201
  end

  test "should show post" do
    get post_url(@post), as: :json, headers: authenticated_header
    assert_response :success
  end

  test "should update post" do
    patch post_url(@post), params: { post: { body: @post.body, title: @post.title } }, as: :json, headers: authenticated_header
    assert_response 200
  end

  test "should destroy post" do
    assert_difference('Post.count', -1) do
      delete post_url(@post), as: :json, headers: authenticated_header
    end

    assert_response 204
  end
end

users_controller_test.rb

require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:one)
  end

  test "should create user_token" do
    post user_token_url, params: { auth: { email: @user.email, password:'secret' } }, as: :json
    assert_response 201
  end

end

rspec

tutorial

https://www.rubydoc.info/gems/factory_bot/file/GETTING_STARTED.md https://tenderlovemaking.com/2015/01/23/my-experience-with-minitest-and-rspec.html https://github.com/rspec/rspec-rails https://relishapp.com/rspec/rspec-rails/docs/generators https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one

group :development, :test do
  gem 'rspec-rails', '~> 3.8'
end
group :test do
  gem 'factory_bot_rails', '~> 4.0'
  gem 'faker'
  gem 'database_cleaner'
end
$ bundle install
$ rails generate rspec:install
Running via Spring preloader in process 25843
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

rspec code generate

$ rails generate rspec:scaffold post
Running via Spring preloader in process 25895
      create  spec/controllers/posts_controller_spec.rb
      create  spec/routing/posts_routing_spec.rb
      invoke  rspec
      create    spec/requests/posts_spec.rb
$ rails generate rspec:model post

if you want spec/requests

mkdir spec/requests && touch spec/requests/{posts_spec.rb,users_spec.rb}

spec/requests/posts_spec.rb

require 'rails_helper'

RSpec.describe "Posts", type: :request do

  let(:user) { create(:user) }
  let(:headers) { authenticated_header(user.id) }

  describe "GET #index" do
    it "returns a success response" do
      get posts_path, params: {}, headers: headers
      expect(response).to be_successful
    end
  end

end
mkdir spec/factories
touch spec/factories/{posts.rb,users.rb}

spec/factories/posts.rb

FactoryBot.define do
  factory :post do
    title { Faker::Lorem.word }
    body { Faker::Lorem.word }
    user_id { Faker::Number.number(1) }
  end
end

spec/factories/users.rb

FactoryBot.define do
  factory :user do
    email { 'user_factory@bananas.com' }
    password_digest { BCrypt::Password.create('bananaBro', cost: 4) }
    admin {false}
  end
end

spec/support/controller_spec_helper

mkdir spec/support
touch spec/support/controller_spec_helper.rb
# spec/support/controller_spec_helper.rb
module ControllerSpecHelper
  def authenticated_header user_id
    token = Knock::AuthToken.new(payload: { sub: user_id }).token
    {
        'Authorization': "Bearer #{token}"
    }
  end
end

spec/rails_helper.rb

add FactoryBot

  # add `FactoryBot` methods
  config.include FactoryBot::Syntax::Methods

add database cleaner

# require database cleaner at the top level
require 'database_cleaner'

RSpec.configure do |config|
  # start by truncating all the tables but then use the faster transaction strategy the rest of the time.
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    DatabaseCleaner.strategy = :transaction
  end

  # start the transaction strategy as examples are run
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

add spec/support/**/*.rb for ControllerSpecHelper

  Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
  config.include ControllerSpecHelper
$ bundle exec rspec ./spec/controllers/posts_controller_spec.rb

https://relishapp.com/rspec/rspec-rails/docs/gettingstarted

$ bundle exec rspec spec/controllers/posts_controller_spec.rb --format documentation

PostsController
  GET #index
    returns a success response
  GET #show
    returns a success response
  POST #create
    with valid params
      creates a new Post
      renders a JSON response with the new post
    with invalid params
      renders a JSON response with errors for the new post
  PUT #update
    with valid params
      updates the requested post (PENDING: Add a hash of attributes valid for your model)
      renders a JSON response with the post
    with invalid params
      renders a JSON response with errors for the post
  DELETE #destroy
    destroys the requested post

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) PostsController PUT #update with valid params updates the requested post
     # Add a hash of attributes valid for your model
     # ./spec/controllers/posts_controller_spec.rb:106


Finished in 1.22 seconds (files took 1.85 seconds to load)
9 examples, 0 failures, 1 pending

posts_controller_spec.rb

require 'rails_helper'

# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator.  If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails.  There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec.  Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
#
# Also compared to earlier versions of this generator, there are no longer any
# expectations of assigns and templates rendered. These features have been
# removed from Rails core in Rails 5, but can be added back in via the
# `rails-controller-testing` gem.

RSpec.describe PostsController, type: :controller do

  # This should return the minimal set of attributes required to create a valid
  # Post. As you add validations to Post, be sure to
  # adjust the attributes here as well.
  let(:valid_attributes) {
    {
        title: Faker::Lorem.word,
        body: Faker::Lorem.word,
        user_id: user.id
    }
  }

  let(:invalid_attributes) {
    {
        title: "",
        body: ""
    }
  }

  let(:user) { create(:user) }
  # let(:user) { create(:user) }
  # let(:valid_attributes) { create(:post, :user_id => user.id).attributes }
  let(:posts) { create_list(:post, 10) }



  let(:headers) { authenticated_header(user.id) }

  describe "GET #index" do
    it "returns a success response" do
      puts "hello world #{valid_attributes} "
      # puts "hello world #{valid_attributes.attributes} "
      # puts "user #{user.attributes} "
      # puts "user2 #{valid_attributes.user_id} "
      post = Post.create! valid_attributes
      request.headers.merge! headers
      get :index, params: {}
      expect(response).to be_successful
    end
  end

  describe "GET #show" do
    it "returns a success response" do
      post = Post.create! valid_attributes
      request.headers.merge! headers
      get :show, params: {id: post.to_param}
      expect(response).to be_successful
    end
  end

  describe "POST #create" do
    context "with valid params" do
      it "creates a new Post" do
        expect {
          request.headers.merge! headers
          post :create, params: {post: valid_attributes}
        }.to change(Post, :count).by(1)
      end

      it "renders a JSON response with the new post" do
        request.headers.merge! headers
        post :create, params: {post: valid_attributes}
        expect(response).to have_http_status(:created)
        expect(response.content_type).to eq('application/json')
        expect(response.location).to eq(post_url(Post.last))
      end
    end

    context "with invalid params" do
      it "renders a JSON response with errors for the new post" do
        request.headers.merge! headers
        post :create, params: {post: invalid_attributes}
        expect(response).to have_http_status(:unprocessable_entity)
        expect(response.content_type).to eq('application/json')
      end
    end
    context "with invalid authenticate" do
      it "renders a JSON response with errors for the new post" do
        post :create, params: {post: valid_attributes}
        expect(response).to have_http_status(:unauthorized)
      end
    end
  end

  describe "PUT #update" do
    context "with valid params" do
      let(:new_attributes) {
        {
            title: "update",
            body: "welcome to hell o"
        }
      }

      it "updates the requested post" do
        post = Post.create! valid_attributes
        request.headers.merge! headers
        put :update, params: {id: post.to_param, post: new_attributes}
        post.reload
      end

      it "renders a JSON response with the post" do
        post = Post.create! valid_attributes
        request.headers.merge! headers
        put :update, params: {id: post.to_param, post: valid_attributes}
        expect(response).to have_http_status(:ok)
        expect(response.content_type).to eq('application/json')
      end
    end

    context "with invalid params" do
      it "renders a JSON response with errors for the post" do
        post = Post.create! valid_attributes
        request.headers.merge! headers
        put :update, params: {id: post.to_param, post: invalid_attributes}
        expect(response).to have_http_status(:unprocessable_entity)
        expect(response.content_type).to eq('application/json')
      end
    end
  end

  describe "DELETE #destroy" do
    it "destroys the requested post" do
      post = Post.create! valid_attributes
      expect {
        request.headers.merge! headers
        delete :destroy, params: {id: post.to_param}
      }.to change(Post, :count).by(-1)
    end
  end

end

spec/requests/posts_spec.rb

require 'rails_helper'

RSpec.describe "Posts", type: :request do
  describe "GET /posts" do
    it "works! (now write some real specs)" do
      get posts_path
      expect(response).to have_http_status(:unauthorized)
    end
  end
end

spec/routing/posts_routing_spec.rb

require "rails_helper"

RSpec.describe PostsController, type: :routing do
  describe "routing" do
    it "routes to #index" do
      expect(:get => "/api/v1/posts").to route_to("posts#index")
    end

    it "routes to #show" do
      expect(:get => "/api/v1/posts/1").to route_to("posts#show", :id => "1")
    end


    it "routes to #create" do
      expect(:post => "/api/v1/posts").to route_to("posts#create")
    end

    it "routes to #update via PUT" do
      expect(:put => "/api/v1/posts/1").to route_to("posts#update", :id => "1")
    end

    it "routes to #update via PATCH" do
      expect(:patch => "/api/v1/posts/1").to route_to("posts#update", :id => "1")
    end

    it "routes to #destroy" do
      expect(:delete => "/api/v1/posts/1").to route_to("posts#destroy", :id => "1")
    end
  end
end

bundle exec rspec

$ bundle exec rspec
hello world {:title=>"sint", :body=>"quibusdam", :user_id=>41}
.................

Finished in 0.93529 seconds (files took 1.62 seconds to load)
17 examples, 0 failures

bundle exec rspec --format documentation

$ bundle exec rspec --format documentation

PostsController
  GET #index
hello world {:title=>"repellendus", :body=>"aperiam", :user_id=>51}
    returns a success response
  GET #show
    returns a success response
  POST #create
    with valid params
      creates a new Post
      renders a JSON response with the new post
    with invalid params
      renders a JSON response with errors for the new post
    with invalid authenticate
      renders a JSON response with errors for the new post
  PUT #update
    with valid params
      updates the requested post
      renders a JSON response with the post
    with invalid params
      renders a JSON response with errors for the post
  DELETE #destroy
    destroys the requested post

Posts
  GET /posts
    works! (now write some real specs)

PostsController
  routing
    routes to #index
    routes to #show
    routes to #create
    routes to #update via PUT
    routes to #update via PATCH
    routes to #destroy

Finished in 0.96735 seconds (files took 1.61 seconds to load)
17 examples, 0 failures

user spec

$ rails generate rspec:scaffold user
Running via Spring preloader in process 91026
      create  spec/controllers/users_controller_spec.rb
      create  spec/routing/users_routing_spec.rb
      invoke  rspec
      create    spec/requests/users_spec.rb
require 'rails_helper'

# This spec was generated by rspec-rails when you ran the scaffold generator.
# It demonstrates how one might use RSpec to specify the controller code that
# was generated by Rails when you ran the scaffold generator.
#
# It assumes that the implementation code is generated by the rails scaffold
# generator.  If you are using any extension libraries to generate different
# controller code, this generated spec may or may not pass.
#
# It only uses APIs available in rails and/or rspec-rails.  There are a number
# of tools you can use to make these specs even more expressive, but we're
# sticking to rails and rspec-rails APIs to keep things simple and stable.
#
# Compared to earlier versions of this generator, there is very limited use of
# stubs and message expectations in this spec.  Stubs are only used when there
# is no simpler way to get a handle on the object needed for the example.
# Message expectations are only used when there is no simpler way to specify
# that an instance is receiving a specific message.
#
# Also compared to earlier versions of this generator, there are no longer any
# expectations of assigns and templates rendered. These features have been
# removed from Rails core in Rails 5, but can be added back in via the
# `rails-controller-testing` gem.

RSpec.describe UsersController, type: :controller do

  # This should return the minimal set of attributes required to create a valid
  # User. As you add validations to User, be sure to
  # adjust the attributes here as well.
  let(:valid_attributes) {
    {
      email: Faker::Internet.email,
      password_digest: BCrypt::Password.create(Faker::Internet.password, cost: 4),
      admin: false
    }
  }

  let(:invalid_attributes) {
    {
        email: "",
        password_digest: nil,
        admin: false
    }
  }

  let(:user) { create(:user) }

  describe "GET #index" do
    it "returns a success response" do
      user = User.create! valid_attributes
      get :index, params: {}
      expect(response).to be_successful
    end
  end

  describe "GET #show" do
    it "returns a success response" do
      user = User.create! valid_attributes
      request.headers.merge! authenticated_header(user.id)
      get :show, params: {id: user.to_param}
      expect(response).to be_successful
    end
  end

  describe "POST #create" do
    context "with valid params" do
      it "creates a new User" do
        expect {
          post :create, params: {user: valid_attributes}
        }.to change(User, :count).by(1)
      end

      it "renders a JSON response with the new user" do

        post :create, params: {user: valid_attributes}
        expect(response).to have_http_status(:created)
        expect(response.content_type).to eq('application/json')
        expect(response.location).to eq(user_url(User.last))
      end
    end

    context "with invalid params" do
      it "renders a JSON response with errors for the new user" do

        post :create, params: {user: invalid_attributes}
        expect(response).to have_http_status(:unprocessable_entity)
        expect(response.content_type).to eq('application/json')
      end
    end
  end

  describe "PUT #update" do
    context "with valid params" do
      let(:new_attributes) {
        {
            email: Faker::Internet.email,
            password_digest: BCrypt::Password.create(Faker::Internet.password, cost: 4),
            admin: true
        }
      }

      it "updates the requested user" do
        user = User.create! valid_attributes
        request.headers.merge! authenticated_header(user.id)
        put :update, params: {id: user.to_param, user: new_attributes}
        user.reload
        expect(response).to have_http_status(:ok)
        expect(response.content_type).to eq('application/json')
        expect(user.email).to eq(new_attributes[:email])
        expect(user.password_digest).to eq(new_attributes[:password_digest])
        expect(user.admin).to eq(new_attributes[:admin])
      end

      it "renders a JSON response with the user" do
        user = User.create! valid_attributes
        request.headers.merge! authenticated_header(user.id)
        put :update, params: {id: user.to_param, user: valid_attributes}
        expect(response).to have_http_status(:ok)
        expect(response.content_type).to eq('application/json')
      end
    end

    context "with invalid params" do
      it "renders a JSON response with errors for the user" do
        user = User.create! valid_attributes
        request.headers.merge! authenticated_header(user.id)
        put :update, params: {id: user.to_param, user: invalid_attributes}
        expect(response).to have_http_status(:unprocessable_entity)
        expect(response.content_type).to eq('application/json')
      end
    end
  end

  describe "DELETE #destroy" do
    it "destroys the requested user" do
      user = User.create! valid_attributes
      expect {
        request.headers.merge! authenticated_header(user.id)
        delete :destroy, params: {id: user.to_param}
      }.to change(User, :count).by(-1)
    end
  end

end

paging

class PostsController < ApplicationController
  before_action :authenticate_user
  before_action :set_post, only: [:show, :update, :destroy]
  before_action only: [:update, :destroy] do
    is_owner @post.user_id
  end

  # GET /posts
  def index
    @posts = Post.paginate(page: params[:page], per_page: 20).order('id DESC')

    render json: @posts
  end

post.rb

class Post < ApplicationRecord
  # validations
  validates_presence_of :title, :body
  self.per_page = 20
end
#### Gemfile
```ruby
gem 'will_paginate', '~> 3.1.0'

Allow anything through CORS Policy

gem 'rack-cors', require: 'rack/cors'

config/application.rb

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :options]
  end
end

error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment