Storyモデルのバリデーションを行う。
Storyがhas_many:taggings なとき、1つのstoryが持てるtaggingsの数を制限したい。
方法1: Sexy Validationを使う
taggingsはArrayなので、lengthメソッドあるいはsizeメソッドでサイズを知ることができる。だからもしかしてvalidatesで:lengthが使えるんじゃないかなーと思っていたら、
使えた。
validates :taggings, :length => {
:maximum => STORY_TAGGINGS_LIMIT_SIZE,
:too_long => "タグ数が限界を超えました! 限界タグ数は%{count}です!"
}
とすればバリデーションは完成。
なお、タグづけの方法は
- Railscasts#382 http://railscasts.com/episodes/382-tagging?language=ja&view=asciicast
- Railscasts#258(Revised) http://railscasts.com/episodes/258-token-fields-revised?view=asciicast
を参考にして作った。この場合は、taggingsを直接入力しないので
validates :tag_ids, :length => {
:maximum => STORY_TAGGINGS_LIMIT_SIZE,
:too_long => "タグ数が限界を超えました! 限界タグ数は%{count}です!"
}
のように、validatesの引数は:tag_idsや:tag_listに変えることになる。
方法2: 独自validate_eachメソッドを作る方法
上記のSexy Validationを使う方法がシンプルでよいのだけど、ほかの方法もある。
当初はvalidatesの:lengthオプションがarray相手に使えることを知らなかったので、以下の手法を用いた。
app/modelディレクリにsize_in_array_validator.rbというファイルを作る。
class SizeInArrayValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.size > STORY_TAGGINGS_LIMIT_SIZE
record.errors.add(attribute, "too much!")
end
end
end
modelディレクトリに入れれば自動的に読み込まれる。
app/model/story.rbの中に、以下のコードを入れる。
validates :taggings, :size_in_array => true
これでバリデーションが動いた。
参考:Railsで任意のValidateを設定するには? QA@IT http://qa.atmarkit.co.jp/q/2508
不可解なエラー
独自validate_eachメソッドを作ったところ、Rspec 2.13.0 + Factory Girl 4.2.0でのエラーが発生した。
$ rspec
したら、
.../gems/activerecord-3.2.13/lib/active_record/validations.rb:56:in `save!': Validation failed: Email already exists. (ActiveRecord::RecordInvalid)
で、factory_girlがFactoryGirl.create(:story)を作る際になぜかsequenceが動かない……? という状況になった。
独自バリデーションが原因な気がしたのでコメントアウトした。
# validates :taggings, :size_in_array => true
rspecをやりなおすと、エラーは発生しなかった。
どうしてこうなったのか本当によくわからない。
Sexy Validationを使えばこのエラーは発生しない。