Ruby on Railsを少し触ってみた
Ruby on Railsをあまり触ったことがないので、少しだけ触ってみたのでまとめておく
メモ
使用したRailsのバージョン
rails: 8.0.1 database: postgresql
テスト等で使えそうなライブラリ
- rpec-rails
- factory_bot
- テスト時にモデルファクトリを簡単に定義できる
- faker
- 名前などの様々な架空のデータを生成できる
- gimei
- 日本の人名や住所などを生成でき、カナ名も取得できる(Fakerだとカナ名は無理)
プロジェクトの作成
# APIモードで新規プロジェクトを作成する rails new app --API --database=postgresql
データベースの接続設定
app/config/database.yml
の接続情報を修正する
default: # 共通パラメータ # ... development: # 開発用DB username: postgres password: postgres host: localhost port: 5432 test: # テスト用DB # ... production: # 本番用DB # ...
DB操作コマンド
# データベースの作成 rails db:create # データベースの削除 rails db:drop # migrateで作成したスキーマをデータベースに適用する rails db:schema:load # 最新のマイグレーションの実行(マイグレーションがすでにある場合) rails db:migrate # シードを挿入 rails db:seed # データベースのリセット(drop -> create -> seed) rails db:reset
マイグレーション
スキーマを定義する場合は次のコマンドを生成する
# モデルの作成 ## gはgenerateの省略(generateでも可) rails g model User username:string hashed_password:string
app/db/migrations
にタイムスタンプ付きのマイグレーションファイルが作成されるので、コマンドで生成できない部分を追記する。
ユーザーと投稿モデルをそれぞれ新規マイグレーションした場合のサンプルコードを下記に示す。
# ユーザーモデルの作成 class CreateUser < ActiveRecord::Migration[8.0] def change create_table :users do |t| t.string email, null: false t.string hashed_password, null: false t.string nickname, null: false t.timestamps end add_index :users, :email, unique: true end end # 投稿モデルの作成 class CreatePost < ActiveRecord::Migration[8.0] def change create_table :posts do |t| t.references :users, null: false, foreign_key: true t.string :title, null: false t.string :content, null: false t.timestamps end end end
参照する側ではt.refernces <参照リソース名の複数形>
のように宣言し、引数にはnull制約や外部キー制約を設定できる。
モデルの関連付け
アプリケーション側のロジックを管理するクラスはApplicationRecordを継承したモデルクラスapp/models/xxx.rb
を修正する。
アプリケーション上で属性として関連モデルを取得する場合にも、マイグレーションで参照を関連付けたうえで、別途このクラス内で関連付けを定義する必要がある。
ユーザーと投稿が1:N(has many)の関係にある場合には、次のような定義を追加する。
class User < ApplicationRecord has_many :posts end class Post < ApplicationRecord belongs_to :user end
こうすることで、Userからは複数のPosts、Postからは一つのUserが対応付けされる。
シードデータの用意
db/seeds.rb
を編集してシードデータを用意する。
# hashmapのリストを渡すと一括登録できる User.create!(email: "admin@example.com", nickname: "admin", hashed_password: BCrypt::Password.create("password"))
コントローラの作成
定義したモデルに対して、APIのアクセスポイントを定義するにはコントローラを生成する。
# usersはURLのパス/usersに対応する rails g controller users # api/v1のようなプレフィックスが欲しい場合は、次のようにする rails g controller api/v1/users
次のメソッドを実装することで、それぞれ対応するエンドポイントを作成することができる。 (名前はその慣例に従う必要があるが、カスタムすることもたぶんできる)
class UsersController < ApplicationController before_action :set_user, only: [ :show, :update, :destroy ] # GET /users def index users = User.all render json: users.as_json, status: :ok end # GET /users/{id} def show render json: @user.as_json, status: :ok end # POST /users def create user = User.new(user_params) if user.save render json: user.as_json, status: :created else render json: user.errors.full_messages, :unprocessable_entity end end # PATCH/PUT /users def update if @user.update(user_params) render json: user.as_json, status: :ok else render json: user.errors.full_messages, status: :unprocessable_entity end end # DELETE def destroy @user.destory head :no_content end private # パラメータの必須フィールドを指定する def user_params params.require(:user).permit(:email, :password, nickname) end # before_actionで紐づけることで任意のメソッドの呼び出し時に変数をセットできる def set_user @user = User.find(params[:id]) end end
プライベートメソッド
- before_actionとプライベートメソッドを組み合わせて、必要なパラメータのバリデーションを行うことができる(railsではよく用いられている様子)
レスポンス形式をカスタマイズする
レスポンスフィールドをカスタマイズしたい場合には、いくつか方法があるようだがApplicationRecordのas_jsonメソッドの引数onlyに返したいフィールドを指定すれば良い。 共通化したい場合などは、as_jsonメソッドをオーバーライドする。
class User < ApplicationRecord # ... def as_json super(only: [:id, :email, :nickname, :created_at, :updated_at, :deleted_at]) end end
テスト
RSpecの用意
次のコマンドを打つとspec
というディレクトリと設定ファイルが生成される。
rails g rspec:install
FactoryBotでテストデータを生成する
spec/factories
にUserモデルのファクトリを定義してみる。
FactoryBot.define do factory :users do transient do password { "password" } end email { Faker::Internet.email } nickname { "nickname" } after(:build) do | user, evaluator | user.hashed_password = BCrypt::Password.create(evaluator.password) end end end
テストコード内で次のように呼び出す
# 単体を作成する # フィールドは引数に渡して設定できる user = FactoryBot.create(:user, password: "12345") # 複数作成する users = FactoryBot.create_list(:user, 10, password: "password")
APIのテストを書く
次のコマンドを打って、Usersコントローラのテストファイルを生成する。
spec/requests/users.spec
が作成される。
rails g rspec:controller users
一覧取得と作成のAPIテストを書いてみる。
RSpec.describe "Users", type: :request do describe "GET /index" do it "ユーザー一覧の取得に成功する" do FactoryBot.create_list(:user, 10) get "/api/v1/users" expect(response).to have_http_stastus(:ok) json = JSON.parse(response.body) expect(json.length).to 10 end end describe "POST /" do valid_params = { nickname: "nickname", email: "test@example.com", password: "password" } expect { post "/api/v1/users", params: { user: valid_params } }.to change(User, :count).by(1) expect(response).to have_http_status(:created) json = JSON.parse(response.body) expect(json[:nicname]).to eq(valid_params[:nickname]) expect(json[:nicname]).to eq(valid_params[:email]) end end