Model を作成する
このチュートリアルは souls db
コマンドを使って マイグレーションファイル を作成する方法を説明します。ActiveRecord について復習する必要がある方はこちらの ActiveRecord を先に読むことをお勧めします。
クイックスタート を終了し、アプリが動いていることを確認してください。 このチュートリアルではクイックスタートで作成したアプリを使って開発を進めていきます。
ここでは API ディレクトリで作業を行います。
テストを実行する
SOULs フレームワークではテストと共に開発を進めていきます。
api ディレクトリに移動して、SOULs API を起動することを確認してください。
まずはテストを実行します。
$ cd apps/api
$ souls test
Inspecting 32 files
................................
31 files inspected, no offenses detected
Run options: exclude {:uses_external_service=>true, :long=>true}
All examples were filtered out
Randomized with seed 10663
Finished in 0.13387 seconds (files took 1.38 seconds to load)
0 examples, 0 failures
Randomized with seed 10663
0 つのテストが確認されました。
Migration ファイルの作成
souls db create_migration
コマンドを使って Migration ファイルを作成します。
$ souls db create_migration ${table_name}
${table_name}
にはテーブル名が入るので、
ここでは user
を指定します。
$ souls db create_migration user
db/migrate/20210930154336_create_users.rb
Created file! : sig/api/db/migrate/create_users.rbs
Created file! : ./app/models/user.rb
Created file! : ./spec/models/user_spec.rb
Created file! : sig/api/app/models/user_model.rbs
souls db create_migration
コマンドによって以下の 5 つのファイルが生成されました。
ファイル名 | 役割 | パス |
---|---|---|
*_create_users.rb | マイグレーション | API ディレクトリ |
create_users.rbs | RBS | マザーディレクトリ |
user.rb | Model | API ディレクトリ |
user_spec.rb | Rspec | API ディレクトリ |
user_model.rbs | RBS | マザー ディレクトリ |
ActiveRecord のマイグレーションファイル、
Model ファイル、
それぞれに対する 型チェック
の RBS ファイルが作成されました。
SOULs フレームワークでは souls
コマンドによって作成されたファイルに合わせて、
.rbs
ファイルと _spec.rb
ファイルが自動で生成されます。
RBS と Steep について初めての方は以下のリンクを先に読むことをお勧めします。
それでは生成されたファイルを確認してみます。
Migration ファイル
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.boolean :is_deleted, null: false, default: false
t.timestamps
end
end
end
SOULs フレームワークではデフォルトで以下の3つのカラムが作成されます。
カラム名 | 型 |
---|---|
is_deleted | Boolean |
created_at | DateTime |
updated_at | DateTime |
これらのカラムは後に登場する souls g scaffold
コマンドで自動生成されるファイルと
関連するので、これらのカラムは変更しないようにしましょう。
Model ファイル
class User < ActiveRecord::Base
end
User Model が生成されました。
RBS ファイル
マザーディレクトリにある sig
ディレクトリ内に rbs
ファイルが定義されています。
``
class CreateUsers
def change: () -> untyped
def create_table: (:users) { (untyped) -> untyped } -> untyped
def add_index: (:users, *untyped) -> untyped
end
``
class User < ActiveRecord::Base
end
マイグレーションファイル と User Model に対する RBS ファイルが生成されました。
Rspec ファイル
RSpec.describe "User Model テスト", type: :model do
describe "User データを書き込む" do
it "valid User Model" do
expect(FactoryBot.build(:user)).to be_valid
end
end
end
User Model に対する Rspec
ファイルが生成されました。
Migration ファイルの定義
先程生成された Migration ファイルに User
テーブルのカラムを定義します。
class CreateUsers < ActiveRecord::Migration[6.1]
def change
create_table :users do |t|
t.string :uid, null: false, unique: true
t.string :username, null: false, default: ""
t.string :screen_name, null: false, default: ""
t.string :last_name, null: false, default: ""
t.string :first_name, null: false, default: ""
t.string :last_name_kanji, null: false, default: ""
t.string :first_name_kanji, null: false, default: ""
t.string :last_name_kana, null: false, default: ""
t.string :first_name_kana, null: false, default: ""
t.string :email, null: false, unique: true
t.string :tel, null: false, default: ""
t.string :icon_url, null: false, default: ""
t.string :birthday, null: false, default: ""
t.string :gender, null: false, default: ""
t.string :lang, null: false, default: "ja"
t.string :category, null: false, default: "user"
t.integer :roles_mask, null: false, default: 1
t.boolean :is_deleted, null: false, default: false
t.timestamps
end
add_index :users, :uid
add_index :users, :screen_name
add_index :users, :email, unique: true
add_index :users, :username
add_index :users, :is_deleted
end
end
souls db コマンドの実行
souls help
と実行することで、
SOULs CLI コマンドを確認することができます。
souls db
コマンドのヘルプを表示させてみます。
$ souls db help
Commands:
souls db add_column [CLASS_NAME] # Create ActiveRecord Migration File
souls db change_column [CLASS_NAME] # Create ActiveRecord Migration File
souls db create # Create Database
souls db create_migration [CLASS_NAME] # Create ActiveRecord Migration File
souls db create_migration_rbs [CLASS_NAME] # Generate ActiveRecord Migration's RBS Template
souls db drop_table [CLASS_NAME] # Create ActiveRecord Migration File
souls db help [COMMAND] # Describe subcommands or one specific subcommand
souls db migrate # Migrate Database
souls db migrate_reset # Reset Database
souls db model [CLASS_NAME] # Generate Model Template
souls db model_rbs [CLASS_NAME] # Generate GraphQL Model RBS from schema.rb
souls db remove_column [CLASS_NAME] # Create ActiveRecord Migration File
souls db rename_column [CLASS_NAME] # Create ActiveRecord Migration File
souls db reset # Reset Database and Seed
souls db rspec_model [CLASS_NAME] # Generate Rspec Model Test from schema.rb
souls db seed # Insert Seed Data
souls db
コマンドを実行して新しいテーブルを追加します。
souls db create
コマンドの実行
$ souls db create
Created database 'souls-api-dev'
Created database 'souls-api-test'
souls db migrate
コマンドの実行
$ souls db migrate
== 20210930154336 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0193s
-- add_index(:users, :uid)
-> 0.0054s
-- add_index(:users, :screen_name)
-> 0.0050s
-- add_index(:users, :email, {:unique=>true})
-> 0.0050s
-- add_index(:users, :username)
-> 0.0052s
-- add_index(:users, :is_deleted)
-> 0.0052s
== 20210930154336 CreateUsers: migrated (0.0454s) =============================
コマンドのヘルプ詳細を表示することもできます。
$ souls db help create
Usage:
souls db create
Options:
--e, [--env=ENV] # Difine APP Enviroment - development | production
# Default: development
Create Database
本番環境での実行は --e=production
で環境変数を指定します。
$ souls db create --e=production
$ souls db migrate --e=production
Article Model を作成する
同様に、Article
Model を作成します。
ここでは Article
のカテゴリーを動的に管理するために、今回は ArticleCategory
を Article
にリレーションを付けて作成してみます。
ブログの記事にコメントも書けるように Comment
テーブルも追加します。
Migration を作成する
先程と同様に souls db create_migration
コマンドで Migration ファイルを作成します。
$ souls db create_migration article_category
$ souls db create_migration article
$ souls db create_migration comment
Migration ファイルの定義
それぞれ Migration ファイルを定義します。
class CreateArticleCategories < ActiveRecord::Migration[6.1]
def change
create_table :article_categories do |t|
t.string :name, null: false
t.text :tags, array: true, default: []
t.boolean :is_deleted, null: false, default: false
t.timestamps
end
add_index :article_categories, :name
add_index :article_categories, :is_deleted
end
end
class CreateArticles < ActiveRecord::Migration[6.1]
def change
create_table :articles do |t|
t.belongs_to :user
t.string :title, null: false, unique: true
t.text :body, null: false, default: ""
t.string :thumnail_url, null: false, default: ""
t.datetime :public_date, null: false, default: Time.now + 30.days
t.belongs_to :article_category, null: false
t.boolean :is_public, default: false, null: false
t.boolean :just_created, default: true, null: false
t.string :slug, null: false, unique: true
t.text :tags, array: true, default: []
t.boolean :is_deleted, null: false, default: false
t.timestamps
end
add_index :articles, :slug, unique: true
add_index :articles, :title, unique: true
add_index :articles, :is_public
add_index :articles, :is_deleted
end
end
class CreateComments < ActiveRecord::Migration[6.1]
def change
create_table :comments do |t|
t.belongs_to :article
t.string :from, null: false, default: "名無し"
t.text :body, null: false, default: ""
t.boolean :is_deleted, null: false, default: false
t.timestamps
end
end
end
souls db コマンドの実行
souls db migrate
コマンドを実行します。
$ souls db migrate
Model の定義
Model に ActiveRecord
のリレーションを定義します。
class ArticleCategory < ActiveRecord::Base
has_many :article
end
class Article < ActiveRecord::Base
belongs_to :user
belongs_to :article_category
has_many :comment
end
class Comment < ActiveRecord::Base
belongs_to :article, dependent: :destroy
end
class User < ActiveRecord::Base
include RoleModel
has_many :article
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
private_constant :VALID_EMAIL_REGEX
validates :email, presence: true, uniqueness: true, format: { with: VALID_EMAIL_REGEX }
roles :normal, :user, :admin, :master
before_create :assign_initial_roles
# Scope
default_scope -> { order(created_at: :desc) }
def assign_initial_roles
roles << [:normal]
end
end
RBS ファイルの定義
souls g
コマンドを使って、生成されたファイルには、
そのファイルに対応する .rbs
ファイルが自動で生成されます。
新たにファイルにコードを追加したときには、
そのファイルに対応する .rbs
ファイルに追記します。
class User < ActiveRecord::Base
VALID_EMAIL_REGEX: Regexp
def assign_initial_roles: -> Array[Symbol]
def self.roles: (:normal, :user, :admin, :master) -> untyped
def self.default_scope: (*untyped) -> untyped
def self.order: (created_at: :desc) -> untyped
def roles: (*untyped) -> untyped
end