file_column の validation で日本語のメッセージを使えるようにする。
趣旨
ファイルアップロードを劇的に楽にするおなじみ file_column プラグイン。作者の Sebastian Kanthak さんは、現在 Google にお勤めの凄腕ハッカー。写真を見る限りは、なかなかのイケメンである。(日本のハッカーで誰が一番イケメンかなあ)
それはさておき、file_column の validation では、ファイル種別やファイルサイズによるチェックが可能である。しかし、英語でしかメッセージを表示してくれない。そこで、エラーメッセージを自分で設定できるようにハックしてみた。
使い方
モデルクラスで、
file_column :image include FileColumn::Validations validates_file_format_of :image, :in => ["gif", "png", "jpg"], :message => "のファイル形式は gif, png, jpg だけです。" validates_filesize_of :image, :in => 100..300.kilobytes, :too_large_message => "のサイズが大きすぎます。", :too_small_message => "のサイズが小さすぎます。"
のように宣言するだけ。簡単だなー。
ソースコード
これは file_column の lib/validation.rb 全体と対応する。したがって、これを validation.rb の中身全部と取り替えてもよい。あるいは、どこか別の場所に保存しておいて config/environment.rb あたりで、Rails の初期化時に読み込むようにしてもよい。
module FileColumn module Validations #:nodoc: def self.append_features(base) super base.extend(ClassMethods) end # This module contains methods to create validations of uploaded files. All methods # in this module will be included as class methods into <tt>ActiveRecord::Base</tt> # so that you can use them in your models like this: # # class Entry < ActiveRecord::Base # file_column :image # validates_filesize_of :image, :in => 0..1.megabyte # end module ClassMethods EXT_REGEXP = /\.([A-z0-9]+)$/ unless defined?(EXT_REGEXP) # This validates the file type of one or more file_columns. A list of file columns # should be given followed by an options hash. # # Required options: # * <tt>:in</tt> => list of extensions or mime types. If mime types are used they # will be mapped into an extension via FileColumn::ClassMethods::MIME_EXTENSIONS. # # Examples: # validates_file_format_of :field, :in => ["gif", "png", "jpg"] # validates_file_format_of :field, :in => ["image/jpeg"] def validates_file_format_of(*attrs) options = attrs.pop if attrs.last.is_a?Hash raise ArgumentError, "Please include the :in option." if !options || !options[:in] options[:in] = [options[:in]] if options[:in].is_a?String message = options[:message] || "is not a valid format." raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Array validates_each(attrs, options) do |record, attr, value| unless value.blank? mime_extensions = record.send("#{attr}_options")[:mime_extensions] extensions = options[:in].map{|o| mime_extensions[o] || o } record.errors.add attr, message unless extensions.include?(value.scan(EXT_REGEXP).flatten.first) end end end # This validates the file size of one or more file_columns. A list of file columns # should be given followed by an options hash. # # Required options: # * <tt>:in</tt> => A size range. Note that you can use ActiveSupport's # numeric extensions for kilobytes, etc. # # Examples: # validates_filesize_of :field, :in => 0..100.megabytes # validates_filesize_of :field, :in => 15.kilobytes..1.megabyte def validates_filesize_of(*attrs) options = attrs.pop if attrs.last.is_a?Hash raise ArgumentError, "Please include the :in option." if !options || !options[:in] raise ArgumentError, "Invalid value for option :in" unless options[:in].is_a?Range too_small_message = options[:too_small_message] || "is smaller than the allowed size range." too_large_message = options[:too_large_message] || "is larger than the allowed size range." validates_each(attrs, options) do |record, attr, value| unless value.blank? size = File.size(value) record.errors.add attr, too_small_message if size < options[:in].first record.errors.add attr, too_large_message if size > options[:in].last end end end IMAGE_SIZE_REGEXP = /^(\d+)x(\d+)$/ unless defined?(IMAGE_SIZE_REGEXP) # Validates the image size of one or more file_columns. A list of file columns # should be given followed by an options hash. The validation will pass # if both image dimensions (rows and columns) are at least as big as # given in the <tt>:min</tt> option. # # Required options: # * <tt>:min</tt> => minimum image dimension string, in the format NNxNN # (columns x rows). # # Example: # validates_image_size :field, :min => "1200x1800" # # This validation requires RMagick to be installed on your system # to check the image's size. def validates_image_size(*attrs) options = attrs.pop if attrs.last.is_a?Hash raise ArgumentError, "Please include a :min option." if !options || !options[:min] minimums = options[:min].scan(IMAGE_SIZE_REGEXP).first.collect{|n| n.to_i} rescue [] raise ArgumentError, "Invalid value for option :min (should be 'XXxYY')" unless minimums.size == 2 too_small_message = options[:too_small_message] || "is too small, must be at least %dx%d" invalid_message = options[:invalid_message] || "invalid image" require 'RMagick' validates_each(attrs, options) do |record, attr, value| unless value.blank? begin img = ::Magick::Image::read(value).first record.errors.add(attr, invalid_message % [minimums[0], minimums[1]]) if ( img.rows < minimums[1] || img.columns < minimums[0] ) rescue ::Magick::ImageMagickError record.errors.add(attr, invalid_message) end img = nil GC.start end end end end end end