Create a Model

This tutorial describes how to create a model and migration using the souls db If you need to review ActiveRecord, we recommend reading this ActiveRecord first.

create_migration

Finish the quick start and make sure the app is running. In this tutorial, we will proceed with development using the application created in the quick start.

We'll work in the API directory here.

Run the test

The SOULs framework will be developed alongside Rspec tests. Go to the api directory and launch the SOULs API.

First, run the tests.

$ 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 tests have been confirmed.

Creating a migration file

Create a migration file using the souls db create_migration

$ souls db create_migration ${table_name}

${table_name} table name is entered in ${table_name}, specify 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 command generated the following five files:

file name role path
*_create_users.rb migration API directory
create_users.rbs RBS Mother directory
user.rb Model API directory
user_spec.rb Rspec API directory
user_model.rbs RBS Mother directory

ActiveRecord migration file,

Model file,

A type check RBS file has been created for each.

In the SOULs framework, according to the file created by the souls

.rbs (type chceck) and _spec.rb (test) files are automatically generated.

If you are new to RBS and Steep, we recommend that you read the links below first.

Now let's check the generated file.

Migration file

apps/api/db/migrate/20210930151749_create_users.rb
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

The SOULs framework creates the following three columns by default.

Column name Mold
is_deleted Boolean
created_at DateTime
updated_at DateTime

These columns are associated with the files that will be auto-generated by the souls g scaffold command.

Model file

app/models/user_model.rb
class User < ActiveRecord::Base
end

A User Model has been generated. This inherits from ActiveRecord and the usual validations and callbacks can therefore be used.

RBS file

rbs file is defined in the sig directory in the mother directory.

sig/api/db/migrate/create_users.rbs
class CreateUsers
  def change: () -> untyped
  def create_table: (:users) { (untyped) -> untyped } -> untyped
  def add_index: (:users, *untyped) -> untyped
end
sig/api/app/models/user.rbs
class User < ActiveRecord::Base
end

A migration file and an RBS file for the User Model have been generated.

Rspec file

app/api/spec/models/user_spec.rb
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

Rspec file for the User Model has been generated.

Migration file definition

Define the columns User table in the Migration file generated earlier.

db/migrate/20210728081544_create_users.rb
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

Execute souls db command

By running souls help

You can see the SOULs CLI command.

Let's get help for the 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 command to add a new table.

Execute souls db create

$ souls db create
Created database 'souls-api-dev'
Created database 'souls-api-test'

Executing the 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) =============================

You can also view the help details for the command.

$ souls db help create
Usage:
  souls db create

Options:
  --e, [--env=ENV]  # Difine APP Enviroment - development | production
                    # Default: development

Create Database

To run in production, specify the environment variable with --e=production

$ souls db create --e=production
$ souls db migrate --e=production

Create an Article Model

Similarly, Article Model.

Here, in order to dynamically manage the categories of ArticleCategory , this time we will create an Article

We'll also add a Comment table so you can comment on your blog posts.

Execute souls db command

Create a migration file with the souls db create_migration

$ souls db create_migration article_category
$ souls db create_migration article
$ souls db create_migration comment

Migration file definition

Define a migration file for each.

apps/api/db/migrate/[timestamp]_create_article_categories.rb
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
apps/api/db/migrate/[timestamp]_create_articles.rb
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
apps/api/db/migrate/[timestamp]_create_comments.rb
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

Execute souls db command

Run the souls db migrate

$ souls db migrate

Definition of relationship model

Define an ActiveRecord relationship in Model.

apps/api/models/article_category.rb
class ArticleCategory < ActiveRecord::Base
  has_many :article
end
apps/api/models/article.rb
class Article < ActiveRecord::Base
  belongs_to :user
  belongs_to :article_category
  has_many :comment
end
apps/api/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article, dependent: :destroy
end
apps/api/app/models/user.rb
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

Definition of RBS

souls g command generates .rbs template.

Let's edit the user.rbs.

sig/api/app/models/user.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