この記事は ex-crowdworks Advent Calendar 2024の15日目の記事です。
はじめに
今年、株式会社クラウドワークスを退職した@nisyuuです。珈琲は豆を挽くところから作っています。
エンジニアとしてクラウドワークステック(旧クラウドテック)というフリーランスと企業をマッチングするエージェントサービスを開発していました。
Railsには、データが正しい形で保存されるようにするためのバリデーション機能があります。例えば、入力が必須であることや、同じ値が重複しないことを簡単にチェックできます。
ただし、標準の機能だけでは対応できない特別な条件を追加したい場合、カスタムバリデーターを作成する必要があります。
本記事では、カスタムバリデーターの仕組みと作り方をわかりやすく解説します。
カスタムバリデーターの実装方法
カスタムバリデーションを実装する方法は大きく分けて2つあります。
1. モデル内にカスタムメソッドを定義する
比較的簡単なバリデーションは、モデル内にメソッドを定義して実装できます。この方法は特定のモデルだけで利用する場合に適しています。
実装例: 禁止された単語をチェックする
以下は、投稿内容に禁止された単語が含まれている場合にエラーを発生させる例です。
class Post < ApplicationRecord
validate :content_cannot_include_restricted_words
private
def content_cannot_include_restricted_words
restricted_words = %w[禁止語句 不適切]
if restricted_words.any? { |word| content&.include?(word) }
errors.add(:content, "この投稿には禁止された言葉が含まれています。")
end
end
end
解説
-
validate
メソッドでカスタムメソッドcontent_cannot_include_restricted_words
を呼び出しています - 条件を満たさない場合、
errors.add
でエラーメッセージを追加します
2. 再利用可能なカスタムバリデータークラスを作成する
複数のモデルで同じバリデーションを利用する場合は、専用のクラスを作成する方法が適しています。この方法では、ActiveModel::EachValidator
を継承したクラスを作ります。
実装例: 許可されたメールアドレスのドメインを制限する
- バリデータークラスを作成
app/validators
ディレクトリを作成し、その中にクラスファイルを追加します。
例: app/validators/email_domain_validator.rb
class EmailDomainValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
allowed_domains = options[:domains] || []
domain = value&.split('@')&.last
unless allowed_domains.include?(domain)
record.errors.add(attribute, "許可されたドメイン(#{allowed_domains.join(', ')})のメールアドレスを使ってください。")
end
end
end
解説
- このクラスは
ActiveModel::EachValidator
を継承し、1つの属性(フィールド)に対してバリデーションを行います -
options
を通じて、許可するドメインのリストを外部から渡せるようにしています
- モデルで使用
作成したバリデータークラスをモデル内で利用します。
class User < ApplicationRecord
validates :email, email_domain: { domains: %w[example.com test.com] }
end
解説
-
validates
メソッドにカスタムバリデーターemail_domain
を指定しています - オプションで許可するドメインを渡しています
カスタムバリデーターのテスト
バリデーションが正しく動作することを確認するため、テストを作成します。ここではRSpecを使った例を示します。
テスト例
require 'rails_helper'
describe User, type: :model do
it '許可されたドメインのメールを受け入れる' do
user = User.new(email: 'test@example.com')
user.validate
expect(user.errors[:email]).to be_empty
end
it '許可されていないドメインのメールを拒否する' do
user = User.new(email: 'test@invalid.com')
user.validate
expect(user.errors[:email]).to include("許可されたドメイン(example.com, test.com)のメールアドレスを使ってください。")
end
end
解説
- 2つのテストケースを用意しています:
- 許可されたドメインのメールアドレスがエラーにならないことを確認
- 許可されていないドメインのメールアドレスがエラーになることを確認
おわりに
Railsのカスタムバリデーターを使うことで、特定の要件に応じた柔軟なデータ検証が可能になります。
簡単な要件であればモデル内にメソッドを定義する方法が適していますが、再利用性が求められる場合はカスタムクラスを作成すると便利です。
これであなたもカスタムハッピーに!😼