Published on

Railsの実例実装でわかる❗️バリデーションをカスタムしてハッピーハッピーハッピ〜ハピハピハピハピハピ〜になろう❗️😸

Authors

この記事は 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を継承したクラスを作ります。

実装例: 許可されたメールアドレスのドメインを制限する

  1. バリデータークラスを作成

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を通じて、許可するドメインのリストを外部から渡せるようにしています
  1. モデルで使用

作成したバリデータークラスをモデル内で利用します。

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つのテストケースを用意しています:
    1. 許可されたドメインのメールアドレスがエラーにならないことを確認
    2. 許可されていないドメインのメールアドレスがエラーになることを確認

おわりに

Railsのカスタムバリデーターを使うことで、特定の要件に応じた柔軟なデータ検証が可能になります。 簡単な要件であればモデル内にメソッドを定義する方法が適していますが、再利用性が求められる場合はカスタムクラスを作成すると便利です。

これであなたもカスタムハッピーに!😼

ハッピーハッピーハッピー猫