##環境
- Ruby 1.9.2p320
- Ruby on Rails 3.2.3
##要件
例えばウェブサイトに以下のような入力フォームがあるとします。
- あなたの好みを教えてください。(必須項目)
- ゾンビ
- 幽霊
- ナイアーラトテップ
この3つの選択肢はチェックボックスで複数選択できるようにします。また、1つも選択されなかったときはエラーになるようにします。
##方法
以下のようなテーブルを定義します。
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.boolean :like_ghost
t.boolean :like_nyarlathotep
t.boolean :like_zombie
t.timestamps
end
end
end
次に以下のようなビューを用意します。
p あなたの好みを教えてください。(必須項目)
= form_for(@user) do |f|
= hidden_field_tag 'user[like]'
= f.check_box :like_zombie
| ゾンビ
= f.check_box :like_ghost
| 幽霊
= f.check_box :like_nyarlathotep
| ナイアーラトテップ
= f.submit '保存'
バリデーションをするために hidden_field_tag
メソッドを使用して like
というフィールドを用意しています。
次に以下のようなモデルを用意します。
# coding: utf-8
class User < ActiveRecord::Base
attr_accessor :like
attr_accessible :like, :like_ghost, :like_nyarlathotep, :like_zombie
validates :like, present_like: true
end
like
はデータベースのフィールドとして存在していないため、attr_accessor
メソッドで読み書きできるようにする必要があります。そして、like_*
が1つも選択されなかったときにバリデーションエラーにするため、 present_like
という独自のバリデーションを作成します。
# coding: utf-8
class PresentLikeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
like_name = ['ghost', 'nyarlathotep', 'zombie']
like_values = like_name.map { |name| record.__send__("like_#{name}") }
unless like_values.include?(true)
record.errors[attribute] << (options[:message] || 'を選択してください。')
end
end
end
validate_each
メソッドに渡される3つの引数にはそれぞれ以下のようなデータが渡されます。
- record: #<User id: nil, like_ghost: false, like_nyarlathotep: false, like_zombie: false, created_at: nil, updated_at: nil>
- attribute: :like
- value: ""
like_name.map { |name| record.__send__("like_#{name}") }
でレコードに入っている各 like_*
の値が入った配列を生成し、include?
メソッドを使用して1つでも true
が入っているか (チェックボックスが選択されているか) どうかを判別しています。
record.errors[attribute] << (options[:message] || 'を選択してください。')
の options
には、モデル内で
validates :like, present_like: { message: 'を選択してね' }
などとしたときに、{ message: 'を選択してね' }
が格納されます。ちなみに前の例では :present_like
に true
を指定していましたが、 false
などを指定しても問題なく動作します。
この独自バリデーションのコードを present_like_validator.rb
という名前で保存します。_validator
をファイル名の最後に付けないといけないようです。
以上です。もっと良い方法があったら教えてもらえると嬉しいです。