Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 184 lines (160 sloc) 5.847 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
module Shoulda # :nodoc:
  module Matchers
    module ActiveModel # :nodoc:

      # Ensures that the length of the attribute is validated. Only works with
      # string/text columns because it uses a string to check length.
      #
      # Options:
      # * <tt>is_at_least</tt> - minimum length of this attribute
      # * <tt>is_at_most</tt> - maximum length of this attribute
      # * <tt>is_equal_to</tt> - exact requred length of this attribute
      # * <tt>with_short_message</tt> - value the test expects to find in
      # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
      # translation for :too_short.
      # * <tt>with_long_message</tt> - value the test expects to find in
      # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
      # translation for :too_long.
      # * <tt>with_message</tt> - value the test expects to find in
      # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
      # translation for :wrong_length. Used in conjunction with
      # <tt>is_equal_to</tt>.
      #
      # Examples:
      # it { should ensure_length_of(:password).
      # is_at_least(6).
      # is_at_most(20) }
      # it { should ensure_length_of(:name).
      # is_at_least(3).
      # with_short_message(/not long enough/) }
      # it { should ensure_length_of(:ssn).
      # is_equal_to(9).
      # with_message(/is invalid/) }
      def ensure_length_of(attr)
        EnsureLengthOfMatcher.new(attr)
      end

      class EnsureLengthOfMatcher < ValidationMatcher # :nodoc:
        include Helpers

        def initialize(attribute)
          super(attribute)
          @options = {}
        end

        def is_at_least(length)
          @options[:minimum] = length
          @short_message ||= :too_short
          self
        end

        def is_at_most(length)
          @options[:maximum] = length
          @long_message ||= :too_long
          self
        end

        def is_equal_to(length)
          @options[:minimum] = length
          @options[:maximum] = length
          @short_message ||= :wrong_length
          @long_message ||= :wrong_length
          self
        end

        def with_short_message(message)
          if message
            @short_message = message
          end
          self
        end

        def with_long_message(message)
          if message
            @long_message = message
          end
          self
        end

        def with_message(message)
          if message
            @short_message = message
            @long_message = message
          end
          self
        end

        def description
          description = "ensure #{@attribute} has a length "
          if @options.key?(:minimum) && @options.key?(:maximum)
            if @options[:minimum] == @options[:maximum]
              description << "of exactly #{@options[:minimum]}"
            else
              description << "between #{@options[:minimum]} and #{@options[:maximum]}"
            end
          else
            description << "of at least #{@options[:minimum]}" if @options[:minimum]
            description << "of at most #{@options[:maximum]}" if @options[:maximum]
          end
          description
        end

        def matches?(subject)
          super(subject)
          translate_messages!
          lower_bound_matches? && upper_bound_matches?
        end

        private

        def translate_messages!
          if Symbol === @short_message
            @short_message = default_error_message(@short_message,
                                                   model_name: @subject.class.to_s.underscore,
                                                   instance: @subject,
                                                   attribute: @attribute,
                                                   count: @options[:minimum])
          end

          if Symbol === @long_message
            @long_message = default_error_message(@long_message,
                                                  model_name: @subject.class.to_s.underscore,
                                                  instance: @subject,
                                                  attribute: @attribute,
                                                  count: @options[:maximum])
          end
        end

        def lower_bound_matches?
          disallows_lower_length? && allows_minimum_length?
        end

        def upper_bound_matches?
          disallows_higher_length? && allows_maximum_length?
        end

        def disallows_lower_length?
          if @options.key?(:minimum)
            @options[:minimum] == 0 ||
              disallows_length_of?(@options[:minimum] - 1, @short_message)
          else
            true
          end
        end

        def disallows_higher_length?
          if @options.key?(:maximum)
            disallows_length_of?(@options[:maximum] + 1, @long_message)
          else
            true
          end
        end

        def allows_minimum_length?
          if @options.key?(:minimum)
            allows_length_of?(@options[:minimum], @short_message)
          else
            true
          end
        end

        def allows_maximum_length?
          if @options.key?(:maximum)
            allows_length_of?(@options[:maximum], @long_message)
          else
            true
          end
        end

        def allows_length_of?(length, message)
          allows_value_of(string_of_length(length), message)
        end

        def disallows_length_of?(length, message)
          disallows_value_of(string_of_length(length), message)
        end

        def string_of_length(length)
          'x' * length
        end
      end
    end
  end
end
Something went wrong with that request. Please try again.