ActiveRecord Validation #2

2014-08-26T00:00:00+00:00 rspec Ruby Rails

前回の「ActiveRecord Validation #1」の続き

length

名前通りlengthを検証するヘルパー

require "rails_helper"

class Entry < ActiveRecord::Base
  # allow_blankをfalseでminimumが指定されてない場合?においてはminimumが1に設定される模様
  validates :name, length: {
    minimum: 2,
    maximum: 8,
    too_short: "%{attribute} too short %{count} words",
    too_long: "%{attribute} too long %{count} words",
  }
end

describe Entry do
  it "lengthのテスト(minimum)" do
    entry = Entry.new({ name: "a" })
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq([:name, ["Name too short 2 words"]])
  end

  it "lengthのテスト(maximum)" do
    entry = Entry.new({ name: "x" * 10 })
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq([:name, ["Name too long 8 words"]])
  end
end

っていう感じでminimum/maximumを設定する事でlengthによる制限をチェック出来る。isオプションを使えば指定したlengthでのチェックをする事も出来るし

class Entry < ActiveRecord::Base
  validates :name, length: { in: 2..8 }
end

な感じでinオプションでRange型で指定する事も出来る模様

numericality

数値かどうかを検証するヘルパー

require "rails_helper"

class Entry < ActiveRecord::Base
  validates :age, numericality: { only_integer: true }
end

describe Entry do
  it "numericalityのテスト" do
    expect(Entry.new({ age: 1 }).valid?).to eq true

    entry = Entry.new({ age: 1.0 })
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq [:age, ["must be an integer"]]
  end
end

てな感じでonly_integerを設定すると整数値は可能だけど小数点型等を指定するとエラーになる

又、指定した値より大きいかとかodd/evenか等も検証する事が可能

require "rails_helper"

class Entry < ActiveRecord::Base
  validates :age, numericality: {
    #greater_than: 0,
    greater_than_or_equal_to: 1,
    less_than: 10,
    # odd: true
    even: true
  }
end

describe Entry do
  it "numericalityのテスト" do
    expect(Entry.new({ age: 2 }).valid?).to eq true
    expect(Entry.new({ age: 2.0 }).valid?).to eq true

    # greater_thanオプションによりfalseになる
    expect(Entry.new({ age: 0 }).valid?).to eq false

    # less_thanオプションによりfalse
    expect(Entry.new({ age: 10 }).valid?).to eq false

    # evenで少数点型で指定したとしてもto_iされて評価される模様
    expect(Entry.new({ age: 2.1 }).valid?).to eq true

    entry = Entry.new
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq([:age, ["is not a number"]])
  end
end

absence

空白かどうかを検証するヘルパー。presenceの逆的な感じ?

require "rails_helper"

class Entry < ActiveRecord::Base
  validates :name, absence: true
end

describe Entry do
  it "absenceのテスト" do
    expect(Entry.new.valid?).to eq true

    entry = Entry.new({ name: "hoge" })
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq [:name, ["must be blank"]]
  end
end

uniqueness

カラムに対する一意性をチェック出来るヘルパー

require "rails_helper"

class Entry < ActiveRecord::Base
  validates :name, uniqueness: { case_sensitive: true }
end

describe Entry do
  fixtures :entry

  it "uniquenessのテスト" do
    # case_sensitiveオプションをtrueにするとテストが通る
    expect(Entry.new({ name: "HOGE" }).valid?).to eq true

    entry = Entry.new({ name: "hoge" })
    expect(entry.valid?).to eq false

    errors = entry.errors
    expect(errors).not_to be_empty

    messages = errors.messages
    expect(messages).not_to be_empty

    message = messages.first
    expect(message).to eq [:name, ["has already been taken"]]
  end
end

っていう感じでcase_sensitiveオプションで大文字小文字を評価するかを指定出来る。上記の場合であると

begin transaction

SELECT
    1 AS one
FROM
    "entries"
WHERE
    "entries"."name" = "HOGE"
LIMIT 1

SELECT
    1 AS one
FROM
    "entries"
WHERE
    "entries"."name" = "hoge"
LIMIT 1

rollback transaction

っていうような感じなSQLが発行(fixtures関係のSQLは除外)される。でcase_sensitiveオプションを変えてテストが成功するように修正した場合のSQLは

begin transaction

SELECT
    1 AS one
FROM
    "entries"
WHERE
    LOWER("entries"."name") = LOWER("HOGE")
LIMIT 1

SELECT
    1 AS one
FROM
    "entries"
WHERE
    LOWER("entries"."name") = LOWER("hoge")
LIMIT 1

rollback transaction

というようにcase_sensitiveをfalseにした場合は大文字小文字かどうかを評価しないので値を検索する際にはlowerされて検索されて検証される

又、uniquenessはscopeを使う事で複合一意制約を利用する事も出来る。詳しくは「Railsの複数一意制約について」が参考になるかと

とまぁ基本的なバリデーションヘルパーな所はこれで終わり。まだまだ続く

ActiveRecord Validation #3 - validates_with - ActiveRecord Validation #1