こんにちは、エンジニアの菅野です。
今回はRailsの最近の変更で地味に嬉しかった enumのvalidateオプションについて書いていきます。
概要
Rails7.1からActiveRecordのenumに validate
オプションが指定できるようになりました。
PR
これにより validate: true
が指定された場合、
個別にvalidationを書かずとも対象外の値が入力された際にバリデーションエラーとして扱ってくれるようになりました 🎉
実践
例えばこんなテーブルがあったとします。
# migration class CreateCountries < ActiveRecord::Migration[7.1] def change create_table :countries do |t| t.string :name, null: false t.string :code, limit: 2, null: false t.integer :continent, limit: 1, null: false t.datetime :created_at, null: false end end end
モデルクラスは以下で、まだ validate
オプションはつけていません。
class Country < ApplicationRecord enum :continent, { asia: 'Asia', africa: 'Africa', europe: 'Europe', north_america: 'NorthAmerica', south_america: 'SouthAmerica', australia_oceania: 'Australia/Oceania' } validates :name, presence: true validates :code, presence: true end
まずはサンプルとして日本のレコードをつくります。
[1] pry(main)> japan = Country.create(name: 'Japan', code: 'JP', continent: :asia) TRANSACTION (0.4ms) BEGIN Country Create (2.4ms) INSERT INTO `countries` (`name`, `code`, `continent`, `created_at`) VALUES ('Japan', 'JP', 'Asia', '2023-11-02 02:22:45.486031') TRANSACTION (3.1ms) COMMIT => #<Country:0x0000ffff954d74d0 id: 1, name: "Japan", code: "JP", continent: "asia", created_at: Thu, 02 Nov 2023 02:22:45.486031000 UTC +00:00> [2] pry(main)> japan.continent => "asia"
続いて continent
に範囲外の値を指定します。
試しに日本を南極大陸に移動させてみましょう。
[3] pry(main)> japan.continent = :antarctica ArgumentError: 'antarctica' is not a valid continent
値を代入した維持点で ArgumentError
が発生してしまいました。
今度はモデルクラスのenum定義で validate
オプションを有効にしてみます。
enum :continent, { asia: 'Asia', africa: 'Africa', europe: 'Europe', north_america: 'NorthAmerica', south_america: 'SouthAmerica', australia_oceania: 'Australia/Oceania' }, validate: true
改めて範囲外の値を代入します。しかし今回ArgumentErrorは発生しません。
[2] pry(main)> japan = Country.last Country Load (3.0ms) SELECT `countries`.* FROM `countries` ORDER BY `countries`.`id` DESC LIMIT 1 => #<Country:0x0000ffff8e9ba788 id: 1, name: "Japan", code: "JP", continent: "asia", created_at: Thu, 02 Nov 2023 02:22:45.486031000 UTC +00:00> [3] pry(main)> japan.continent = :antarctica => :antarctica
続いてモデルのバリデーションをかけたところで、初めてバリデーションエラーが発生するようになりました。
[3] pry(main)> japan.continent = :antarctica => :antarctica [4] pry(main)> japan.valid? => false [5] pry(main)> japan.errors.messages => {:continent=>["is not included in the list"]} [6] pry(main)>
代入時点で無条件に例外がスローされることもなくなり、他フィールドと同じようにActiveRecordのエラーとして扱えるようになりましたので、地味に嬉しい変更ではないかと思います。
利用バージョン
ruby '3.2.2' rails '7.1.1'
まとめ
これまで個別に validates :xxx, in: { inclusion: %w[bla bla bla] }
などと記述していたenum値のバリデーションが、7.1からoption一発で宣言できるようになりました。嬉しいですね。