Rails 基礎 Part 02 -- 3 カラムの Migration ファイルから作成した API の振り返り + 改善
Tags: ruby, rails, crud, api
Published: 2023-05-04 14:43:34



# 前回の振り返り

前回は Rails でシンプルな GET のみの API を実装した。
前回生成されたファイルたちは下記。
一部改善を入れる

---

## 1. migration

```ruby
db/migrate/20230423145010_create_bank_transactions.rb
```

bank_transactions テーブルで
下記のカラムを生成するマイグレーション定義。

1. 口座番号: 文字列
2. 金額: 少数
3. 説明: 文字列
4. createdAt
5. updatedAt

```ruby
class CreateBankTransactions < ActiveRecord::Migration[7.0]
  def change
    create_table :bank_transactions do |t|
      t.string :account_number
      t.decimal :amount
      t.string :description

      t.timestamps
    end
  end
end
```

---

## 2. seed -- 一部改善

テストデータを入れるところ。
かえでは開発で一番大事だと考えている。

```ruby
db/seeds.rb
```

下記のカラムに挿入される初期データ。

1. 口座番号: 文字列
2. 金額: 少数
3. 説明: 文字列

```ruby
BankTransaction.delete_all

BankTransaction.create!(
  [ 
    { 
      account_number: "1234567",
      amount: 250_000,
      description: "おちんぎん"
    },
    {
      account_number: "1234567",
      amount: -21_000,
      description: "奨学金"
    },
    {
      account_number: "1234567",
      amount: -3_000,
      description: "docomo携帯料金"
    },
  ]
)

puts "BankTransaction records: "
BankTransaction.all.each do |bank_transactions|
  json = bank_transactions.as_json
  pretty_json = JSON.pretty_generate(json)
  puts pretty_json
end
```

一番最初に全てのデータを削除する。
その後 3 レコード挿入している。
また、挿入後に結果を出力するカスタムスクリプトも入れている。

`!` マークは、型が違った時に適切なエラーを返す。
この場合は `RecordInvalid` が返ってくる。
`!` マークなしだと false で返ってくるので、原因の特定に時間がかかるだろう。

---

## 3. Models

```ruby
app/models/bank_transaction.rb
```

BankTransaction クラスに ApplicationRecord を継承させている。

```ruby
class BankTransaction < ApplicationRecord
end
```

ApplicationRecord とは ActiveRecord を使うためのもの。
ActiveRecord とは Rails の ORM。
ORM とはデータベースのレコードを、Rails で扱える `インスタンス` にするもの。

これによって、DB のテーブルを使って、

* 1対多のアソシエーション
* 必須やユニークのバリデーション

これらを作ることができる。
今回はまだ導入していない。

---

## 3.5 Route

まずこれを通さないと、コントローラーまで行かない。

config.route.rb を編集する。

```ruby
resources :bank_transactions, only: [:index, :show]
```

生成時には 

* GET ALL の index
* GET DETAIL の show

これらのみが有効化されている。

* CREATE
* UPDATE
* DELETE

これらは無効化されている。

```rails
  resources :bank_transactions, only: [:index, :show, :create, :udate, :delete]
```

こうすることで有効化できる。

---

---

## 4. Controllers

---

### 4.0 共通メソッド

rails コマンドで生成されている。

```ruby
class BankTransactionsController < ApplicationController
  before_action :set_bank_transaction, only: %i[ show update destroy ]
```

1. `before_action: methodName` を使うと、各 CRUD アクションが実行されたる直前に methodName が発火する
2. `only:` によって create 以外に絞られる。

set_bank_transaction は以下になっている

```ruby
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_bank_transaction
      begin
        @bank_transaction = BankTransaction.find(params[:id])
      rescue ActiveRecord::RecordNotFound
        render json: {
          status: 404,
          error: "bank_transaction Not Found"
        },
        status: :not_found
      end
    end
```

引数の id で BankTransaction のエンティティを引っ張る。
それを @bank_transaction というインスタンス変数に詰める。

インスタンス変数は、同一名の Controlle の他のアクションや、View で使うことができる変数。

今回は View はないが、show, update, delete のアクションがある。
これらで @bank_transaction を使用できる。

id のデータが存在しないときのエラーハンドリングも入れておく。
（カスタマイズ）

これを入れないと exception メッセージが長くてデバックが面倒。

---

自動で下記のアクションが作成されている。

### 4.1 GET ALL

```ruby
  # GET /bank_transactions
  def index
    @bank_transactions = BankTransaction.all

    render json: @bank_transactions
  end
```

```json
[
  {
    "id": 14,
    "account_number": "1234567",
    "amount": "250000.0",
    "description": "おちんぎん",
    "created_at": "2023-05-04T15:29:45.998Z",
    "updated_at": "2023-05-04T15:29:45.998Z"
  },
  {
    "id": 15,
    "account_number": "1234567",
    "amount": "-21000.0",
    "description": "奨学金",
    "created_at": "2023-05-04T15:29:46.002Z",
    "updated_at": "2023-05-04T15:29:46.002Z"
  },
  {
    "id": 16,
    "account_number": "1234567",
    "amount": "-3000.0",
    "description": "docomo携帯料金",
    "created_at": "2023-05-04T15:29:46.006Z",
    "updated_at": "2023-05-04T15:29:46.006Z"
  }
]
```

### 4.2 GET Detail

```ruby
  # GET /bank_transactions/1
  def show
    render json: @bank_transaction
  end
```

```json
{
  "id": 15,
  "account_number": "1234567",
  "amount": "-21000.0",
  "description": "奨学金",
  "created_at": "2023-05-04T15:29:46.002Z",
  "updated_at": "2023-05-04T15:29:46.002Z"
}
```

### 4.3 POST (create)

-d {key:value} でデータをつける
-X POST で POST メソッドにする
-i でレスポンスコードを見る

これで POST する。

```ruby
curl -i -X POST -H "Content-Type: application/json" \
  -d '{"account_number": "1234567", "amount": 997777.0, "description": "POST TEST "}' \
  http://localhost:3000/bank_transactions      
HTTP/1.1 201 Created
...

{"id":22,"account_number":"1234567","amount":"997777.0","description":"POST TEST ","created_at":"2023-05-07T05:22:20.825Z","updated_at":"2023-05-07T05:22:20.825Z"}%  
```

すると、新しくデータができている


### 4.4 PUT

```ruby
  # PATCH/PUT /bank_transactions/1
  def update
    if @bank_transaction.update(bank_transaction_params)
      render json: @bank_transaction
    else
      render json: @bank_transaction.errors, status: :unprocessable_entity
    end
  end
```

```ruby
curl localhost:3000/bank_transactions/23 | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   168    0   168    0     0   8351      0 --:--:-- --:--:-- --:--:-- 12923
{
  "id": 23,
  "account_number": "1234567",
  "amount": "250000.0",
  "description": "おちんぎん",
  "created_at": "2023-05-07T07:35:04.777Z",
  "updated_at": "2023-05-07T07:35:04.777Z"
}
```

```ruby
curl -i -X PUT \
  -H "Content-Type: application/json" \
  -d '{"account_number": "1234567", "amount": 300000, "description": "昇給！"}' \
  http://localhost:3000/bank_transactions/23

HTTP/1.1 200 OK
```

```ruby
curl localhost:3000/bank_transactions/23 | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   162    0   162    0     0   8197      0 --:--:-- --:--:-- --:--:-- 12461
{
  "id": 23,
  "account_number": "1234567",
  "amount": "300000.0",
  "description": "昇給！",
  "created_at": "2023-05-07T07:35:04.777Z",
  "updated_at": "2023-05-07T07:40:21.788Z"
}
```

### 4.5 DELETE (destroy)

リソースを「破壊」する。

```ruby
  # DELETE /bank_transactions/1
  def destroy
    @bank_transaction.destroy
  end
```

```ruby
curl localhost:3000/bank_transactions/24 | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   162    0   162    0     0   8152      0 --:--:-- --:--:-- --:--:-- 12461
{
  "id": 24,
  "account_number": "1234567",
  "amount": "-21000.0",
  "description": "奨学金",
  "created_at": "2023-05-07T07:35:04.782Z",
  "updated_at": "2023-05-07T07:35:04.782Z"
}
```

```ruby
curl -i -X DELETE \
  -H "Content-Type: application/json" \
  http://localhost:3000/bank_transactions/23

HTTP/1.1 204 No Content
```

```
curl localhost:3000/bank_transactions/23 | jq .                    
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                        
                                 Dload  Upload   Total   Spent    Left  Speed                          
100 15874  100 15874    0     0   375k      0 --:--:-- --:--:-- --:--:--  455k                         
{                                                                                                      
  "status": 404,                                                                                       
  "error": "Not Found",                                                                                
  "exception": "#<ActiveRecord::RecordNotFound: Couldn't find BankTransaction with 'id'=23>",     
```