module Remarkable module ActiveRecord module Matchers class ValidateLengthOfMatcher < Remarkable::ActiveRecord::Base #:nodoc: arguments :collection => :attributes, :as => :attribute optional :within, :alias => :in optional :minimum, :maximum, :is optional :token, :separator, :with_kind_of optional :allow_nil, :allow_blank, :default => true optional :message, :too_short, :too_long, :wrong_length collection_assertions :less_than_min_length?, :exactly_min_length?, :more_than_max_length?, :exactly_max_length?, :allow_nil?, :allow_blank? before_assert do # Reassign :in to :within @options[:within] ||= @options.delete(:in) if @options.key?(:in) if @options[:is] @min_value, @max_value = @options[:is], @options[:is] elsif @options[:within] @min_value, @max_value = @options[:within].first, @options[:within].last elsif @options[:maximum] @min_value, @max_value = nil, @options[:maximum] elsif @options[:minimum] @min_value, @max_value = @options[:minimum], nil end end default_options :too_short => :too_short, :too_long => :too_long, :wrong_length => :wrong_length protected def allow_nil? super(default_message_for(:too_short)) end def allow_blank? super(default_message_for(:too_short)) end def less_than_min_length? @min_value.nil? || @min_value <= 1 || bad?(value_for_length(@min_value - 1), default_message_for(:too_short)) end def exactly_min_length? @min_value.nil? || @min_value <= 0 || good?(value_for_length(@min_value), default_message_for(:too_short)) end def more_than_max_length? @max_value.nil? || bad?(value_for_length(@max_value + 1), default_message_for(:too_long)) end def exactly_max_length? @max_value.nil? || @min_value == @max_value || good?(value_for_length(@max_value), default_message_for(:too_long)) end def value_for_length(value) if @options[:with_kind_of] [@options[:with_kind_of].new] * value else ([@options.fetch(:token, 'x')] * value).join(@options.fetch(:separator, '')) end end def interpolation_options { :minimum => @min_value, :maximum => @max_value } end # Returns the default message for the validation type. # If user supplied :message, it will return it. Otherwise it will return # wrong_length on :is validation and :too_short or :too_long in the other # types. # def default_message_for(validation_type) return :message if @options[:message] @options.key?(:is) ? :wrong_length : validation_type end end # Validates the length of the given attributes. You have also to supply # one of the following options: minimum, maximum, is or within. # # Note: this method is also aliased as validate_size_of. # # == Options # # * :minimum - The minimum size of the attribute. # * :maximum - The maximum size of the attribute. # * :is - The exact size of the attribute. # * :within - A range specifying the minimum and maximum size of the attribute. # * :in - A synonym(or alias) for :within. # * :allow_nil - when supplied, validates if it allows nil or not. # * :allow_blank - when supplied, validates if it allows blank or not. # * :too_short - value the test expects to find in errors.on(:attribute) when attribute is too short. # Regexp, string or symbol. Default = I18n.translate('activerecord.errors.messages.too_short') % range.first # * :too_long - value the test expects to find in errors.on(:attribute) when attribute is too long. # Regexp, string or symbol. Default = I18n.translate('activerecord.errors.messages.too_long') % range.last # * :wrong_length - value the test expects to find in errors.on(:attribute) when attribute is the wrong length. # Regexp, string or symbol. Default = I18n.translate('activerecord.errors.messages.wrong_length') % range.last # * :message - value the test expects to find in errors.on(:attribute). # Regexp, string or symbol. Default = I18n.translate('activerecord.errors.messages.wrong_length') % value # # It also accepts an extra option called :with_kind_of. If you are validating # the size of an association array, you have to specify the kind of the array # being validated. For example, if your post accepts maximum 10 comments, you # can do: # # should_validate_length_of :comments, :maximum => 10, :with_kind_of => Comment # # Finally, it also accepts :token and :separator, to specify how the # tokenizer should work. For example, if you are splitting the attribute # per word: # # validates_length_of :essay, :minimum => 100, :tokenizer => lambda {|str| str.scan(/\w+/) } # # You could do this: # # should_validate_length_of :essay, :minimum => 100, :token => "word", :separator => " " # # == Gotcha # # In Rails 2.3.x, when :message is supplied, it overwrites the messages # supplied in :wrong_length, :too_short and :too_long. However, in earlier # versions, Rails ignores the :message option. # # == Examples # # it { should validate_length_of(:password).within(6..20) } # it { should validate_length_of(:password).maximum(20) } # it { should validate_length_of(:password).minimum(6) } # it { should validate_length_of(:age).is(18) } # # should_validate_length_of :password, :within => 6..20 # should_validate_length_of :password, :maximum => 20 # should_validate_length_of :password, :minimum => 6 # should_validate_length_of :age, :is => 18 # # should_validate_length_of :password do |m| # m.minimum 6 # m.maximum 20 # end # def validate_length_of(*attributes, &block) ValidateLengthOfMatcher.new(*attributes, &block).spec(self) end end end end