Rails6のアプリに、validates追加とRSpecでのテストを追加してみました。
過去の経験だと、どうしても納期優先でRSpecが後回し(最終的にはやらない)になってしまうので、簡単に流用できるように残しておく事にします。

モデル作成

spaceモデルにサブドメイン(subdomain)とスペース名(name)の例

$ rails g model space subdomain:string name:string
または
$ rails g scaffold space subdomain:string name:string

DBで出来る制限は入れておいた方が無難なので、NOT NULL制約とユニーク制約を追加しておきます。
NOT NULLでも空は許容されてしまうので、validatesで担保します。

db/migrate/20200518002450_create_spaces.rbを修正

class CreateSpaces < ActiveRecord::Migration[6.0]
  def change
    create_table :spaces do |t|
      t.string :subdomain, null: false
      t.string :name, null: false

      t.timestamps
    end
    add_index :spaces, :subdomain, unique: true
  end
end
$ rails db:migrate

validates追加

サブドメインは1〜32文字。アルファベット(小文字)・数字・ハイフン(先頭不可)です。
連続ハイフンは不可と書いてあるサイトもありましたが、AWSのRoute53でワイルドカード設定したドメインで名前解決出来たので、制限は入れない事にしました。
名前は表示上の問題だけなので、一旦、必須(1文字以上)と32文字までとします。

app/models/space.rbの2行目辺り(classの中)に追加

  validates :subdomain, presence: true
  validates :subdomain, length: { in: 1..32 }
  validates :subdomain, format: { with: /\A[a-z0-9][a-z0-9\d\-]*\z/ }
  validates :subdomain, uniqueness: true
  validates :name, presence: true
  validates :name, length: { maximum: 32 }

翻訳追加

RailsAdmin等を導入していれば、この段階で使われますが、いずれ使う事が多いので追加しておきます。
参考: RailsアプリにサクッとRailsAdminを導入

config/locales/ja.yml

ja:
  activerecord:
    attributes:
      space:
        subdomain: "サブドメイン"
        name: "スペース名"
        created_at: "作成日"
        updated_at: "更新日"
    models:
      space: "スペース"
    errors:
      models:
        space:
          attributes:
            subdomain:
              taken: "既に使用されています。"
              blank: "入力してください。"
              too_short: "%{count}文字以上で入力してください。"
              too_long: "%{count}文字以下で入力してください。"
              invalid: "アルファベット(小文字)・数字・ハイフン(先頭不可)のみで入力してください。"
            name:
              blank: "入力してください。"
              too_long: "%{count}文字以下で入力してください。"

テスト追加

FactoryBotは導入済みの想定です。
参考: Railsアプリにサクッとdeviseを導入

spec/factories/spaces.rb

FactoryBot.define do
  factory :space do
    subdomain { 'myspace' }
    name      { 'マイスペース' }
  end
end

spec/models/space_spec.rb

require 'rails_helper'

RSpec.describe Space, type: :model do
  describe 'validates subdomain' do
    subdomain_minimum = 1
    context "#{subdomain_minimum - 1}文字" do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * (subdomain_minimum - 1)
        expect(space).not_to be_valid
      end
    end
    context "#{subdomain_minimum}文字" do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * subdomain_minimum
        expect(space).to be_valid
      end
    end
    context '32文字' do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * 32
        expect(space).to be_valid
      end
    end
    context '33文字' do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * 33
        expect(space).not_to be_valid
      end
    end
    context 'アルファベット(小文字)・数字・ハイフン(先頭不可)' do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * [subdomain_minimum - 4, 1].max + 'z09-'
        expect(space).to be_valid
      end
    end
    context 'アルファベット(大文字)' do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.subdomain = 'A' * subdomain_minimum
        expect(space).not_to be_valid
      end
    end
    context 'ハイフン(先頭)' do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.subdomain = '-' + 'a' * [subdomain_minimum - 1, 1].max
        expect(space).not_to be_valid
      end
    end
    context 'ハイフン(後尾)' do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.subdomain = 'a' * [subdomain_minimum - 1, 1].max + '-'
        expect(space).to be_valid
      end
    end
    context '重複' do
      it 'NG' do
        space1 = FactoryBot.build(:space)
        space1.save
        space2 = FactoryBot.build(:space)
        space2.subdomain = space1.subdomain
        expect(space2).not_to be_valid
      end
    end
  end

  describe 'validates name' do
    context '0文字' do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.name = ''
        expect(space).not_to be_valid
      end
    end
    context '1文字' do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.name = 'a'
        expect(space).to be_valid
      end
    end
    context '32文字' do
      it 'OK' do
        space = FactoryBot.build(:space)
        space.name = 'a' * 32
        expect(space).to be_valid
      end
    end
    context '33文字' do
      it 'NG' do
        space = FactoryBot.build(:space)
        space.name = 'a' * 33
        expect(space).not_to be_valid
      end
    end
  end
end
$ rspec spec/models/space_spec.rb 
13 examples, 0 failures

【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/93c100e2011a56a7338447515407f85e0cf2a2ea

備考

実際にはサブドメインが1文字OKだと都合が悪い事もある。3〜5文字とか?
下記のみで変更可能になっています。

app/models/space.rb

  validates :subdomain, length: { in: 1..32 }

spec/models/space_spec.rb

    subdomain_minimum = 1

共通化

上記は冗長なので、この後、追加したConfig(gem)で共通化しました。
Config(gem)で環境毎に異なる定数を管理

config/settings.yml

subdomain_minimum: 1
subdomain_maximum: 32

app/models/space.rb

-  validates :subdomain, length: { in: 1..32 }
+  validates :subdomain, length: { in: Settings['subdomain_minimum']..Settings['subdomain_maximum'] }

【参考】ここまでのコミット内容
https://dev.azure.com/nightonly/rails-app-origin/_git/rails-app-origin/commit/184ff1498e734e24346e2290352a7e27bb298c84

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です