RailsにおけるValidationの処理をまとめてみた
Validationのお作法をまとめてみました
バリデーションの基本的なパターンは存在性、長さ、フォーマット、一意性の確認となります。 整理のため、これら4つの処理をRailsで行う際のお作法をまとめてみます。テストコードも最後に付記しています。
存在性の検証
Userというname、emailを持つクラスを考えてみます。 「presence: true」で存在性を確認することができます。
class User < ActiveRecord::Base attr_accessible :name, :email validates :name, presence: true end
長さの検証
同じくUserクラスで長さの検証を行います。名前が長すぎると困るので50文字に制限した処理にします。 length: { maximum: 50 }を付けることで検証が可能です。
class User < ActiveRecord::Base attr_accessible :name, :email validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true end
フォーマットの検証
emailが正しいフォーマットで入力されているかを検証します。 正規表現とformat: { with:定数 }で検証できます。
class User < ActiveRecord::Base attr_accessible :name, :email validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } end
一意性の検証
メールアドレスに一意性を持たせるための処理を書きます。 ただし、一意性のテストには実際のレコードをデータベースに登録して検証する必要があります。 また、登録者が(クリックの連打などで)二重登録をしてしまった場合、容易に一意性が崩れてしまいます。そこで、データベースにemailの索引を持たせて、一意性を検証する必要があります。また、メールアドレスは大文字・小文字を区別しないので、データベースに登録前に小文字に戻すという一手間も必要になります。
大文字・小文字の区別を無視した検証が以下の通りです。uniquenessとcase_sensible: falseを書く必要があります。
class User < ActiveRecord::Base . . . validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false } end
索引を持たせるため、データモデリングの更新が必要です。以下のmigrationを行います。
rails generate migration add_index_to_users_email
作成されたファイルを以下のように書き換えます。 usersテーブルのemailカラムにインデックスを追加しています。
class AddIndexToUsersEmail < ActiveRecord::Migration def change add_index :users, :email, unique: true end end
最後にmigrationを忘れずに
bundle exec rake db:migrate
データベース登録前にemailを全て小文字に変換します。 ActiveRecordのオブジェクトが持続している間に、オブジェクトを呼び出すコールバックの機能を用いて、小文字に変換後のデータをsaveします。 before_saveメソッドで呼び出し、downcaseメソッドで小文字に変換してください。
class User < ActiveRecord::Base attr_accessible :name, :email before_save { |user| user.email = email.downcase } . . . end
テストコード
テストコードは以下のとおりです。
require 'spec_helper' describe User do before { @user = User.new(name:"Example User",email:"user@example.com") } subject { @user } it { should respond_to(:name) } it { should respond_to(:email) } it { should be_valid } describe "when name is not present" do before { @user.name = "" } it { should_not be_valid } end describe "when email is not present" do before { @user.email = "" } it { should_not be_valid } end describe "when name is too long" do before { @user.name = "a" * 51 } it { should_not be_valid } end describe "when email format is invalid" do it "should be invalid" do addresses = %w[user@foo,com user_at_foo.org example.user@foo. foo@bar_baz.com foo@bar+baz.com] addresses.each do |invalid_address| @user.email = invalid_address @user.should_not be_valid end end end describe "when email format is valid" do it "should be valid" do addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn] addresses.each do |valid_address| @user.email = valid_address @user.should be_valid end end end describe "when email address is already taken" do before do user_with_same_email = @user.dup user_with_same_email.email = @user.email.upcase user_with_same_email.save end it { should_not be_valid } end end