Railsのバリデーションが上手くいかない


nilを許容で、値がある場合にformatのバリデーションをかけたいのですが、formatにそってない値の場合、saveの際に全てnilに変換されて、通ってしまいます。
saveを通さずにバリデーションで警告したいのですが、
いい方法があればご教示下さい。

下記Gistに貼ったような事を試しましたが、どちらもformatに合わない値はnilに勝手に変換されてsaveされてしまいます。
https://gist.github.com/chucker34/f1c9f8d3d3dda7d6ba02
https://gist.github.com/chucker34/b783c95ebab0fecdbbc7

環境は下記です。
ruby '2.1.2'
rails'4.1.4'

宜しくお願いします。

回答の条件
  • 1人10回まで
  • 13歳以上
  • 登録:
  • 終了:2014/11/14 00:00:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:a-kuma3 No.1

回答回数4964ベストアンサー獲得回数2150

ポイント100pt

Ruby on Rails の String#to_date の実装の実装がこんな感じです。

# File 'activesupport/lib/active_support/core_ext/string/conversions.rb', line 43

def to_date
  ::Date.parse(self, false) unless blank?
end
Class: String — Documentation for rails (4.0.0)

irb とかで試してみれば分かりますが、Date#parse は、自由度が かなり高いです。
さすがに 'a' とか渡すと例外が出ますが、'190' なんかでは、きちんと(?)parse してくれちゃいます ><
つまり、質問の二番目の実装では、例外が出ないので rescue に制御が渡らない...


試してませんけど、こんな感じでバリデータを実装してみたらどうでしょうか。

class DateValidator < ActiveModel::EachValidator
 
  def validate_each(record, attribute, value)
    unless value.blank? and /\A\d{4}\-\d{2}\-\d{2}\Z/ =~ value
      record.errors[attribute] << (options[:message] || "2015-01-01の形式で入力してください")
    end
  end

end
他8件のコメントを見る
id:chucker34

すいません。というより

raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
raw_value ||= value

の処理で、何故nilを許容できているのかつかめてないです・・・

2014/11/10 18:38:54
id:a-kuma3

の処理で、何故nilを許容できているのかつかめてないです・・・

ここ、nil を許容している、というところではなく、日付として不当な文字列を渡しているのに nil で保存されちゃう、というところに対応する部分です(だと、思います)。

record が、属性名_before_type_cast というメソッドの呼び出しが可能であれば、それを呼び出して Validator に渡される前に変換される前の生の値を取り出す。
呼び出せてないと raw_value は nil のままだから、value を使う。
その生の値に対して、blank? とか to_datetime で例外が出ないよね、というバリデーションをやってる。

ということだと思います。

2014/11/11 09:30:56

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません