Rails APIプロジェクトを新規作成し、API疎通確認してherokuにdeployまでの備忘録

## 構築するRails環境
- Github
- ruby 2.5.8
- rails 5.1.6
- apiモードで作成
- local DBはsqlite, productionはpostgresqlを使用
- global領域にrails等はインストールしたくない
- herokuのsslに対応させる
- herokuにdeploy

## でき上がるrailsアプリができること
- ユーザ作成
- ログイン
- ログインしてるユーザーはルアーの使い方を投稿できる（微妙にマニアックですみません）
- ユーザーとルアーの使い方のデータは一対多

## Githubでプロジェクト作成
- GithubでRepository新規作成
- git clone


## 環境を整える
- git cloneしたディレクトリ配下に移動

- rubyのバージョンを設定

` rbenv local 2.5.8 `

- rubyのバージョンを確認

` rbenv version `

- bundle initでGemfileを作成

` bundle init `

- Gemfileの編集
ここで今回使いたいrubyバージョンとrailsバージョンを指定

```Gemfile
ruby '2.5.8'
gem 'rails', '5.1.6'
```

- グローバルではなくプロジェクトの ` --path vendor/bundle ` 配下にrailsをinstall

` bundle install --path vendor/bundle `


` [DEPRECATED] The `--path` flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use `bundle config set path 'vendor/bundle'`, and stop using this flag `

globalに設定

` bundle config set path 'vendor/bundle' ` 


## 新規Rails Appの作成

- プロジェクトディレクトリの` --path vendor/bundle `配下にrailsが入ったので、` rails new ` でrails appを新規作成。
- git repositoryと同じ名前のプロジェクト名にしたいときは ` rails new . `
- 今回はAPIモードで作りたいので ` --api ` オプション
- ` -B ` オプションをつけて、このタイミングでは ` bundle install ` しない

` bundle exec rails new . --api -B `

MySQLではじめたい場合
` bundle exec rails new . --api -B --database=mysql ` 

- Gemfileを上書きしていいか聞かれるので ` Y ` （yes）で上書き

- Railsプロジェクトに必要なpumaとかGemfileが入る

- 作成されたGemfileを編集

```Gemfile
ruby '2.5.8'
gem 'rails', '5.1.6'
gem 'puma', '~> 3.7'

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug', '9.0.6', platform: :mri
end

group :development do
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :production do
  gem 'pg', '0.20.0'
end

```

- 適用したいGemfileがようやくできたので、このGemfileで ` bundle instal `
- macにpostgresqlをインストールしていない&localではsqliteを使うので、` --without production ` オプションをつけて本番環境はローカルmacに構築されないようにする

` bundle install --path vendor/bundle --without production `


## Rails Appの機能作成
- scaffold, referenceは使わずにやってます。

- user modelの作成

` bundle exec rails g model user name:string password:string token:token `

- lure modelの作成

` bundle exec rails g model lure name:string usage:string `


- DBの作成

` bundle exec rails db:migrate `

- 一対多関係を作成

` bundle exec rails g migration add_user_id_to_lures user_id:integer `

` bundle exec rails db:migrate `

- User Modelの編集

```
class User < ApplicationRecord
  has_secure_token
  has_many :lures, dependent: :destroy
end
```


- Lure Modelの編集

```
class Lure < ApplicationRecord
  belongs_to :user
end
```

- User Controllerの作成

` bundle exec rails g controller users `

- Lure Controllerの作成

` bundle exec rails g controller lures `

- routeを設定
- ` ~~/api/v1/login ` みたいにして、APIバージョンが上がることを想定した作りにしておきたいのでrouteは以下のように

```
Rails.application.routes.draw do
  namespace 'api' do
    namespace 'v1' do
      resources :users
      resources :lure
    end
  end
end
```

- UsersControllerの編集
- この時、app -> controllers ディレクトリ配下にapiディレクトリを作成
- さらにapiディレクトリ配下にv1ディレクトリを作成
- UsersController　LuresControllerファイルをapp/controllers/api/v1ディレクトリ配下に移動
- class Api::V1::UsersControllerはAPIモジュール配下のv1モジュール配下のUsersControllerという意味（モジュール名は大文字始まり）

```
class Api::V1::UsersController < ApplicationController
  # 
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    users = User.order(created_at: :desc)
    render json: { status: 'SUCCESS', message: 'loaded users', data: users }
  end

  def show
    user = User.find(params[:id])
    render json: { status: 'SUCCESS', message: 'loaded the user', data: user }
  end
  
  def create
    user = User.new(user_params)
    if user.save
      render json: { status: 'SUCCESS', message: 'User was successfully created.', data: user }
    else
      render json: { status: :unprocessable_entity, message: user.errors }
    end
  end

  def destroy
    user = User.find(params[:id])
    user.destroy
    render json: { status: 'SUCCESS', message: 'deleted the user', data: user }
  end

  def update
    user = User.find(params[:id])
    if user.update(user_params)
      render json: { status: 'SUCCESS', message: 'updated the user', data: user }
    else
      render json: { status: 'SUCCESS', message: 'loaded the user', data: user }
    end
  end

    private

    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:name, :password, :token)
    end
end
```

```
class Api::V1::LuresController < ApplicationController
  include ActionController::HttpAuthentication::Token::ControllerMethods
  # tokenある人だけ以下を実行できます
  before_action :authenticate

  def index
    lures = Lure.order(created_at: :desc)
    render json: { status: 'SUCCESS', message: 'loaded lures', data: lures }
  end

  def show
    lure = Lure.find(params[:id])
    render json: { status: 'SUCCESS', message: 'loaded the lure', data: lure }
  end

  def create
    lure = @auth_user.lures.build(lure_params)
    logger.debug("lure_params:::")
    logger.debug(lure_params)
    if lure.save
        render json: { status: 'SUCCESS', message: 'Lure was successfully created.', data: lure }
      else
        render json: { status: :unprocessable_entity, message: lure.errors }
      end
  end
  
  def destroy
    lure = Lure.find(params[:id])
    lure.destroy
    render json: { status: 'SUCCESS', message: 'deleted the lure', data: lure }
  end

  def update
    lure = Lure.find(params[:id])
    if lure.update(lure_params)
      render json: { status: 'SUCCESS', message: 'updated the lure', data: lure }
    else
      render json: { status: 'SUCCESS', message: 'loaded the lure', data: lure }
    end
  end

    private
    def authenticate
      authenticate_or_request_with_http_token do |token,options|
        @auth_user = User.find_by(token: token)
        @auth_user != nil ? true : false
      end
    end

    def lure_params
      params.require(:lure).permit(:name, :usage)
    end
end
```

- rails 起動

` bundle exec rails s `

- curlコマンドでuser作成

` curl -X POST  -H 'Content-Type:application/json' -d '{ "name": "bright", "password": "password" }' http://0.0.0.0:3000/api/v1/users `


- レスポンスが以下のようであればOK

```
{"status":"SUCCESS","message":"User was successfully created.","data":{"id":4,"name":"bright","password":"password","token":"UK789txu3ZsripoXnugPjVG3","created_at":"2019-06-13T11:41:23.017Z","updated_at":"2019-06-13T11:41:23.017Z"
```

- ログインの作成
- ログインはアクションがあればいいのでコントローラだけ作成

```
bundle exec rails g controller login login
```

- routesの編集

```
Rails.application.routes.draw do
  get 'login/login'
  namespace 'api' do
    namespace 'v1' do
      resources :users
      resources :lure
      post 'login/login'
    end
  end
end
```

- ログインコントローラの編集

```
class Api::V1::LoginController < ApplicationController
  def login
    login_user = User.find_by(name:params[:name], password:params[:password])
    if login_user != nil
      render json: { status: 'SUCCESS', message: 'user logged in', data: login_user.token }
    else
      render json: { status: 'FAILER', message: 'no auth' }
    end
  end
end
```

- 作成したユーザでログインできるか疎通確認

` curl -X POST -H 'Content-Type:application/json' -d '{ "name": "bright", "password": "password" }' http://0.0.0.0:3000/api/v1/login/login `

- トークンが返って来ればOK

- 返ってきたtokenをAuthorization HEADERに加えて、ルアーを作成してみる

` curl -X POST -H 'Authorization: Token UK789txu3ZsripoXnugPjVG3' -H 'Content-Type:application/json' -d '{ "name": "Spinnerbait", "usage": "it can be used as a fish finder, a deep water bait, a shallow water bait, a bait that covers a lot of area in a short time." }' http://0.0.0.0:3000/api/v1/lures `

- 以下のレスポンスが返って来ればOK

```
{"status":"SUCCESS","message":"Lure was successfully created.","data":{"id":1,"name":"Spinnerbait","usage":"it can be used as a fish finder, a deep water bait, a shallow water bait, a bait that covers a lot of area in a short time.","created_at":"2019-06-13T13:23:41.359Z","updated_at":"2019-06-13T13:23:41.359Z","user_id":4}}
```


- ルアーをGETしてみる

` curl -X GET -H 'Authorization: Token UK789txu3ZsripoXnugPjVG3' -H 'Content-Type:application/json' http://0.0.0.0:3000/api/v1/lures/1 `

```
{"status":"SUCCESS","message":"loaded the lure","data":{"id":1,"name":"Spinnerbait","usage":"it can be used as a fish finder, a deep water bait, a shallow water bait, a bait that covers a lot of area in a short time.","created_at":"2019-06-13T13:23:41.359Z","updated_at":"2019-06-13T13:23:41.359Z","user_id":4}}
```

## herokuにdeploy

- herokuにプロジェクト作成

` heroku create `


- herokuにpush

` git push heroku master `


- heroku環境でmigrate

` heroku run rails db:migrate `

` curl -X POST  -H 'Content-Type:application/json' -d '{ "name": "bright", "password": "password" }' https:// [herokuに発行されたAPP ID] .herokuapp.com/api/v1/users  `

```
{"status":"SUCCESS","message":"User was successfully created.","data":{"id":1,"name":"bright","password":"password","token":" [token]","created_at":"2019-06-13T13:42:44.413Z","updated_at":"2019-06-13T13:42:44.413Z"}}
```

- ログインできるか確認

` curl -X POST -H 'Content-Type:application/json' -d '{ "name": "bright", "password": "password" }' https://[herokuに発行されたAPP ID].herokuapp.com/api/v1/login/login `

```
{"status":"SUCCESS","message":"user logged in","data":" [herokuに発行されたAPP ID]"}
```

- token使ってルアーの使い方を作成

` curl -X POST -H 'Authorization: Token  [herokuに発行されたAPP ID]' -H 'Content-Type:application/json' -d '{ "name": "Spinnerbait", "usage": "it can be used as a fish finder, a deep water bait, a shallow water bait, a bait that covers a lot of area in a short time." }' https://[herokuに発行されたAPP ID].herokuapp.com/api/v1/lures `


- responseが以下であればOK

```
{"status":"SUCCESS","message":"Lure was successfully created.","data":{"id":1,"name":"Spinnerbait","usage":"it can be used as a fish finder, a deep water bait, a shallow water bait, a bait that covers a lot of area in a short time.","created_at":"2019-06-13T13:45:27.939Z","updated_at":"2019-06-13T13:45:27.939Z","user_id":1}}
```


## 参考
https://www.gitignore.io/api/rails
https://www.sejuku.net/blog/26617
https://qiita.com/ochiochi/##items/966b884eb17045dfb929