最初に
みなさん、Custom Validator使ってますか?
rails guideのここにも記載されていますがActiveModel::Validator
やActiveModel::EachValidator
を使って自作のvalidatorを作成することができます。
異なるmodelで同じようなvalidationを実行する場合は以下のようなメリットがあるので是非使っていきましょう。
・modelのコードを減らせる
・specの行数を減らせる
(Validator Classへspecを書けば良いので同じようなspecを減らせます)
実装例
よくあるパターンだと思うのですが異なるmodelでメールアドレスカラムを持っており、同じようなvalidationを実装する場合の実装例を記載します。
- 以下のようなEmailのValidator Classを作成します。
app/validators/email_validator.rb
# frozen_string_literal: true
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i)
record.errors.add(attribute, options[:message] || "の形式が不正です")
end
end
end
- deviseを使っていてdeviseと同じEmailのvalidationを使いたい場合は以下のような書き方もできます。
unless value.match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i)
↓
unless value.match(Devise.email_regexp)
- modelでの使い方は以下になります。
class Person < ApplicationRecord
validates :email, presence: true, email: true
end
- エラーメッセージは以下のような感じになると思います。
メールアドレスの形式が不正です
- エラーメッセージを変えたい場合もあると思います。
- その場合は以下のようにメッセージを個別で設定することができます。
class User < ApplicationRecord
validates :email, allow_nil: true, email: { message: "正しいメールアドレスの形式で入力してください" }
end
- 次にspecの書き方のサンプルを記載します。
# frozen_string_literal: true
require "rails_helper"
describe EmailValidator do
let(:model_class) do
Struct.new(:mail_address) do
include ActiveModel::Validations
def self.name
"DummyModel"
end
validates :mail_address, allow_nil: true, email: true
end
end
describe "#validate" do
subject { model_class.new(email) }
describe "登録可能な形式" do
context "nil は登録できる" do
let(:email) { nil }
it { is_expected.to be_valid }
end
context "「abc@example.com」は登録できる" do
let(:email) { "abc@example.com" }
it { is_expected.to be_valid }
end
end
describe "登録不可能な形式" do
context "「abc」は登録できない" do
let(:email) { "abc" }
it { is_expected.not_to be_valid }
end
context "「abcd.@example.com」は登録できない" do
let(:email) { "abcd.@example.com" }
it { is_expected.not_to be_valid }
end
end
end
end
最後に
email以外にも画像やURLのvalidationなど使えるところは色々あるのでお試し下さい!