Edited at

Rails 4.1 で validates_timeliness を使う

More than 3 years have passed since last update.

Rails 4.1 で動く日付時刻用のバリデータを探していたところ、 adzap/validates_timeliness の fork である johncarney/validates_timeliness が Rails 4 に対応しているとのことなので試してみた。

jc-validates_timeliness | RubyGems.org | your community gem host

https://rubygems.org/gems/jc-validates_timeliness

johncarney/validates_timeliness

https://github.com/johncarney/validates_timeliness


環境

% bundle exec rake about

About your application's environment
Ruby version 2.1.3-p242 (x86_64-linux)
RubyGems version 2.2.2
Rack version 1.5
Rails version 4.1.6
JavaScript Runtime Node.js (V8)
Active Record version 4.1.6
Action Pack version 4.1.6
Action View version 4.1.6
Action Mailer version 4.1.6
Active Support version 4.1.6


使ってみる

まずは適当なプロジェクトを作成。

% rails new blog --skip-bundle && cd blog && vim Gemfile

Gemfile に以下を追加


Gemfile

gem 'jc-validates_timeliness'


追記したら bundle install を実行。

% bundle install

必要なファイルを生成。

% bundle exec rails g validates_timeliness:install

これで以下の設定ファイルが作成される。


  • config/initializers/validates_timeliness.rb

  • config/locales/validates_timeliness.en.yml

エラーメッセージを日本語で表示するために locale ファイルをコピー。

% cp config/locales/validates_timeliness.en.yml config/locales/validates_timeliness.ja.yml

コピーした validates_timeliness.ja.yml を以下のように変更した。


validates_timeliness.ja.yml

ja:

errors:
messages:
invalid_date: "は正しい形式で入力してください。"
invalid_time: "は正しい形式で入力してください。"
invalid_datetime: "は正しい形式で入力してください。"
is_at: " %{restriction} である必要があります。"
before: " %{restriction} より前を指定してください。"
on_or_before: " %{restriction} 以前を指定してください。"
after: " %{restriction} より後を指定してください。"
on_or_after: " %{restriction} 以降を指定してください。"
validates_timeliness:
error_value_formats:
date: '%Y-%m-%d'
time: '%H:%M'
datetime: '%Y-%m-%d %H:%M'

error_value_formats はバリデーションエラー時に画面上に表示される日付のフォーマットを指定する。

ここでは秒は不要だろ、ということで %S を除去した。

せっかく日本語リソースを作成したのでついでに config/application.rb にも以下を追記。


config/application.rb

config.active_record.default_timezone = :local

config.time_zone = 'Tokyo'
config.i18n.default_locale = :ja

これだけだと日本語リソースがなくてエラーになるので以下も落としてきておく。

% wget -O config/locales/ja.yml https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml

適当な scaffold を作成してマイグレーション実行。

% bundle exec rails g scaffold post title:string body:text published_at:datetime closed_at:datetime

% bundle exec rake db:create db:migrate

model クラスに以下のような感じでバリデーションを追加。


app/models/post.rb

class Post < ActiveRecord::Base

validates :published_at, timeliness: { on_or_after: :now, format: '%Y/%m/%d %H:%M' }, allow_blank: false
validates :closed_at, timeliness: { after: :published_at, format: '%Y/%m/%d %H:%M' }, allow_blank: false
end

lib/validates_timeliness/extensions.rb がまだ 4.1 に完全に対応していないのか、このままだと datetime_select を使うために config.enable_date_time_select_extension!config.enable_multiparameter_extension! を指定した場合にエラーになる。

仕方ないので、View を編集して text_field に変える。

入力はなんか適当な datepicker ライブラリを使うので取り敢えずはこれで良しとする。


app/views/posts/_form.html.erb

<div class="field">

<%= f.label :published_at %><br>
<%= f.text_field :published_at %>
</div>
<div class="field">
<%= f.label :closed_at %><br>
<%= f.text_field :closed_at %>
</div>

上記の設定で


  • 日付のフォーマットは秒を除いた %Y/%m/%d %H:%M (ex. 2014/11/08 10:30)

  • published_at は現在日時以降

  • closed_at は published_at より後

という条件のバリデーションが設定される。

なんか意外と正式に 4 に対応しているものが見つけられなかったんだけど、定番ってないのだろうか。


参考

[Ruby on Rails] 日付・時刻のバリデーションなら「ValidatesTimeliness」 | CodeNote.net

http://codenote.net/ruby/rails/1797.html

validation - In Rails, how can I validate two date attributes on a comparison basis with validates_timeliness gem? - Stack Overflow

http://stackoverflow.com/questions/13651966/in-rails-how-can-i-validate-two-date-attributes-on-a-comparison-basis-with-vali

validation - Testing that rails model validates date with RSpec - Stack Overflow

http://stackoverflow.com/questions/13138208/testing-that-rails-model-validates-date-with-rspec