remarkable_activerecord 3.1.8 → 3.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/CHANGELOG +140 -138
  2. data/LICENSE +20 -20
  3. data/README +80 -80
  4. data/lib/remarkable_activerecord.rb +29 -29
  5. data/lib/remarkable_activerecord/base.rb +248 -237
  6. data/lib/remarkable_activerecord/describe.rb +27 -27
  7. data/lib/remarkable_activerecord/human_names.rb +36 -36
  8. data/lib/remarkable_activerecord/matchers/accept_nested_attributes_for_matcher.rb +30 -30
  9. data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +59 -59
  10. data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +85 -94
  11. data/lib/remarkable_activerecord/matchers/association_matcher.rb +283 -283
  12. data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +68 -68
  13. data/lib/remarkable_activerecord/matchers/have_default_scope_matcher.rb +38 -38
  14. data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +73 -73
  15. data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +30 -30
  16. data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +85 -85
  17. data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +50 -50
  18. data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +97 -97
  19. data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +44 -44
  20. data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +53 -53
  21. data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +52 -52
  22. data/lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb +150 -150
  23. data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +181 -181
  24. data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +29 -29
  25. data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +233 -233
  26. data/locale/en.yml +261 -261
  27. data/spec/accept_nested_attributes_for_matcher_spec.rb +1 -1
  28. data/spec/allow_mass_assignment_of_matcher_spec.rb +90 -82
  29. data/spec/allow_values_for_matcher_spec.rb +72 -63
  30. data/spec/association_matcher_spec.rb +612 -612
  31. data/spec/describe_spec.rb +3 -3
  32. data/spec/have_column_matcher_spec.rb +73 -73
  33. data/spec/have_default_scope_matcher_spec.rb +1 -1
  34. data/spec/have_index_matcher_spec.rb +87 -87
  35. data/spec/have_readonly_attributes_matcher_spec.rb +47 -47
  36. data/spec/have_scope_matcher_spec.rb +77 -77
  37. data/spec/model_builder.rb +101 -101
  38. data/spec/rcov.opts +1 -1
  39. data/spec/spec.opts +4 -4
  40. data/spec/spec_helper.rb +27 -27
  41. data/spec/validate_acceptance_of_matcher_spec.rb +68 -68
  42. data/spec/validate_associated_matcher_spec.rb +121 -121
  43. data/spec/validate_confirmation_of_matcher_spec.rb +58 -58
  44. data/spec/validate_length_of_matcher_spec.rb +218 -218
  45. data/spec/validate_numericality_of_matcher_spec.rb +179 -179
  46. data/spec/validate_presence_of_matcher_spec.rb +56 -56
  47. data/spec/validate_uniqueness_of_matcher_spec.rb +164 -164
  48. metadata +5 -5
@@ -1,57 +1,57 @@
1
- require File.join(File.dirname(__FILE__), 'allow_values_for_matcher')
2
-
3
- module Remarkable
4
- module ActiveRecord
5
- module Matchers
6
- class ValidateInclusionOfMatcher < AllowValuesForMatcher #:nodoc:
1
+ require File.join(File.dirname(__FILE__), 'allow_values_for_matcher')
2
+
3
+ module Remarkable
4
+ module ActiveRecord
5
+ module Matchers
6
+ class ValidateInclusionOfMatcher < AllowValuesForMatcher #:nodoc:
7
7
  # Don't allow it to behave in the negative way.
8
- undef_method :does_not_match?
8
+ undef_method :does_not_match?
9
9
 
10
10
  default_options :message => :inclusion
11
-
12
- protected
13
-
14
- def valid_values
15
- @options[:in]
16
- end
17
-
18
- def invalid_values
19
- if @in_range
20
- [ @options[:in].first - 1, @options[:in].last + 1 ]
11
+
12
+ protected
13
+
14
+ def valid_values
15
+ @options[:in]
16
+ end
17
+
18
+ def invalid_values
19
+ if @in_range
20
+ [ @options[:in].first - 1, @options[:in].last + 1 ]
21
21
  else
22
22
  value = @options[:in].select{ |i| i.is_a?(String) }.max
23
- value ? [ value.next ] : []
24
- end
25
- end
26
-
27
- end
28
-
29
- # Ensures that given values are valid for the attribute. If a range
30
- # is given, ensures that the attribute is valid in the given range.
31
- #
32
- # If you give that :size accepts ["S", "M", "L"], it will test that "T"
33
- # (the next of the array max value) is not allowed.
34
- #
35
- # == Options
36
- #
37
- # * <tt>:in</tt> - values to test inclusion.
38
- # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
39
- # * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
40
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
41
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
42
- #
43
- # == Examples
44
- #
45
- # should_validate_inclusion_of :size, :in => ["S", "M", "L", "XL"]
46
- # should_validate_inclusion_of :age, :in => 18..100
47
- #
48
- # it { should validate_inclusion_of(:size, :in => ["S", "M", "L", "XL"]) }
49
- # it { should validate_inclusion_of(:age, :in => 18..100) }
50
- #
51
- def validate_inclusion_of(*args, &block)
52
- ValidateInclusionOfMatcher.new(*args, &block).spec(self)
53
- end
54
-
55
- end
56
- end
57
- end
23
+ value ? [ value.next ] : []
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ # Ensures that given values are valid for the attribute. If a range
30
+ # is given, ensures that the attribute is valid in the given range.
31
+ #
32
+ # If you give that :size accepts ["S", "M", "L"], it will test that "T"
33
+ # (the next of the array max value) is not allowed.
34
+ #
35
+ # == Options
36
+ #
37
+ # * <tt>:in</tt> - values to test inclusion.
38
+ # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
39
+ # * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
40
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
41
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
42
+ #
43
+ # == Examples
44
+ #
45
+ # should_validate_inclusion_of :size, :in => ["S", "M", "L", "XL"]
46
+ # should_validate_inclusion_of :age, :in => 18..100
47
+ #
48
+ # it { should validate_inclusion_of(:size, :in => ["S", "M", "L", "XL"]) }
49
+ # it { should validate_inclusion_of(:age, :in => 18..100) }
50
+ #
51
+ def validate_inclusion_of(*args, &block)
52
+ ValidateInclusionOfMatcher.new(*args, &block).spec(self)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -1,150 +1,150 @@
1
- module Remarkable
2
- module ActiveRecord
3
- module Matchers
4
- class ValidateLengthOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :collection => :attributes, :as => :attribute
6
-
7
- optional :within, :alias => :in
8
- optional :minimum, :maximum, :is
9
- optional :token, :separator, :with_kind_of
10
- optional :allow_nil, :allow_blank, :default => true
11
- optional :message, :too_short, :too_long, :wrong_length
12
-
13
- collection_assertions :less_than_min_length?, :exactly_min_length?,
14
- :more_than_max_length?, :exactly_max_length?,
15
- :allow_nil?, :allow_blank?
16
-
17
- before_assert do
18
- # Reassign :in to :within
19
- @options[:within] ||= @options.delete(:in) if @options.key?(:in)
20
-
21
- if @options[:is]
22
- @min_value, @max_value = @options[:is], @options[:is]
23
- elsif @options[:within]
24
- @min_value, @max_value = @options[:within].first, @options[:within].last
25
- elsif @options[:maximum]
26
- @min_value, @max_value = nil, @options[:maximum]
27
- elsif @options[:minimum]
28
- @min_value, @max_value = @options[:minimum], nil
29
- end
30
- end
31
-
32
- default_options :too_short => :too_short, :too_long => :too_long, :wrong_length => :wrong_length
33
-
34
- protected
35
- def allow_nil?
36
- super(default_message_for(:too_short))
37
- end
38
-
39
- def allow_blank?
40
- super(default_message_for(:too_short))
41
- end
42
-
43
- def less_than_min_length?
44
- @min_value.nil? || @min_value <= 1 || bad?(value_for_length(@min_value - 1), default_message_for(:too_short))
45
- end
46
-
47
- def exactly_min_length?
48
- @min_value.nil? || @min_value <= 0 || good?(value_for_length(@min_value), default_message_for(:too_short))
49
- end
50
-
51
- def more_than_max_length?
52
- @max_value.nil? || bad?(value_for_length(@max_value + 1), default_message_for(:too_long))
53
- end
54
-
55
- def exactly_max_length?
56
- @max_value.nil? || @min_value == @max_value || good?(value_for_length(@max_value), default_message_for(:too_long))
57
- end
58
-
59
- def value_for_length(value)
60
- if @options[:with_kind_of]
61
- [@options[:with_kind_of].new] * value
62
- else
63
- ([@options.fetch(:token, 'x')] * value).join(@options.fetch(:separator, ''))
64
- end
65
- end
66
-
67
- def interpolation_options
68
- { :minimum => @min_value, :maximum => @max_value }
69
- end
70
-
71
- # Returns the default message for the validation type.
72
- # If user supplied :message, it will return it. Otherwise it will return
73
- # wrong_length on :is validation and :too_short or :too_long in the other
74
- # types.
75
- #
76
- def default_message_for(validation_type)
77
- return :message if @options[:message]
78
- @options.key?(:is) ? :wrong_length : validation_type
79
- end
80
- end
81
-
82
- # Validates the length of the given attributes. You have also to supply
83
- # one of the following options: minimum, maximum, is or within.
84
- #
85
- # Note: this method is also aliased as <tt>validate_size_of</tt>.
86
- #
87
- # == Options
88
- #
89
- # * <tt>:minimum</tt> - The minimum size of the attribute.
90
- # * <tt>:maximum</tt> - The maximum size of the attribute.
91
- # * <tt>:is</tt> - The exact size of the attribute.
92
- # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
93
- # * <tt>:in</tt> - A synonym(or alias) for :within.
94
- # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
95
- # * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
96
- # * <tt>:too_short</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is too short.
97
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % range.first</tt>
98
- # * <tt>:too_long</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is too long.
99
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.too_long') % range.last</tt>
100
- # * <tt>:wrong_length</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is the wrong length.
101
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % range.last</tt>
102
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
103
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % value</tt>
104
- #
105
- # It also accepts an extra option called :with_kind_of. If you are validating
106
- # the size of an association array, you have to specify the kind of the array
107
- # being validated. For example, if your post accepts maximum 10 comments, you
108
- # can do:
109
- #
110
- # should_validate_length_of :comments, :maximum => 10, :with_kind_of => Comment
111
- #
112
- # Finally, it also accepts :token and :separator, to specify how the
113
- # tokenizer should work. For example, if you are splitting the attribute
114
- # per word:
115
- #
116
- # validates_length_of :essay, :minimum => 100, :tokenizer => lambda {|str| str.scan(/\w+/) }
117
- #
118
- # You could do this:
119
- #
120
- # should_validate_length_of :essay, :minimum => 100, :token => "word", :separator => " "
121
- #
122
- # == Gotcha
123
- #
124
- # In Rails 2.3.x, when :message is supplied, it overwrites the messages
125
- # supplied in :wrong_length, :too_short and :too_long. However, in earlier
126
- # versions, Rails ignores the :message option.
127
- #
128
- # == Examples
129
- #
130
- # it { should validate_length_of(:password).within(6..20) }
131
- # it { should validate_length_of(:password).maximum(20) }
132
- # it { should validate_length_of(:password).minimum(6) }
133
- # it { should validate_length_of(:age).is(18) }
134
- #
135
- # should_validate_length_of :password, :within => 6..20
136
- # should_validate_length_of :password, :maximum => 20
137
- # should_validate_length_of :password, :minimum => 6
138
- # should_validate_length_of :age, :is => 18
139
- #
140
- # should_validate_length_of :password do |m|
141
- # m.minimum 6
142
- # m.maximum 20
143
- # end
144
- #
145
- def validate_length_of(*attributes, &block)
146
- ValidateLengthOfMatcher.new(*attributes, &block).spec(self)
147
- end
148
- end
149
- end
150
- end
1
+ module Remarkable
2
+ module ActiveRecord
3
+ module Matchers
4
+ class ValidateLengthOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :collection => :attributes, :as => :attribute
6
+
7
+ optional :within, :alias => :in
8
+ optional :minimum, :maximum, :is
9
+ optional :token, :separator, :with_kind_of
10
+ optional :allow_nil, :allow_blank, :default => true
11
+ optional :message, :too_short, :too_long, :wrong_length
12
+
13
+ collection_assertions :less_than_min_length?, :exactly_min_length?,
14
+ :more_than_max_length?, :exactly_max_length?,
15
+ :allow_nil?, :allow_blank?
16
+
17
+ before_assert do
18
+ # Reassign :in to :within
19
+ @options[:within] ||= @options.delete(:in) if @options.key?(:in)
20
+
21
+ if @options[:is]
22
+ @min_value, @max_value = @options[:is], @options[:is]
23
+ elsif @options[:within]
24
+ @min_value, @max_value = @options[:within].first, @options[:within].last
25
+ elsif @options[:maximum]
26
+ @min_value, @max_value = nil, @options[:maximum]
27
+ elsif @options[:minimum]
28
+ @min_value, @max_value = @options[:minimum], nil
29
+ end
30
+ end
31
+
32
+ default_options :too_short => :too_short, :too_long => :too_long, :wrong_length => :wrong_length
33
+
34
+ protected
35
+ def allow_nil?
36
+ super(default_message_for(:too_short))
37
+ end
38
+
39
+ def allow_blank?
40
+ super(default_message_for(:too_short))
41
+ end
42
+
43
+ def less_than_min_length?
44
+ @min_value.nil? || @min_value <= 1 || bad?(value_for_length(@min_value - 1), default_message_for(:too_short))
45
+ end
46
+
47
+ def exactly_min_length?
48
+ @min_value.nil? || @min_value <= 0 || good?(value_for_length(@min_value), default_message_for(:too_short))
49
+ end
50
+
51
+ def more_than_max_length?
52
+ @max_value.nil? || bad?(value_for_length(@max_value + 1), default_message_for(:too_long))
53
+ end
54
+
55
+ def exactly_max_length?
56
+ @max_value.nil? || @min_value == @max_value || good?(value_for_length(@max_value), default_message_for(:too_long))
57
+ end
58
+
59
+ def value_for_length(value)
60
+ if @options[:with_kind_of]
61
+ [@options[:with_kind_of].new] * value
62
+ else
63
+ ([@options.fetch(:token, 'x')] * value).join(@options.fetch(:separator, ''))
64
+ end
65
+ end
66
+
67
+ def interpolation_options
68
+ { :minimum => @min_value, :maximum => @max_value }
69
+ end
70
+
71
+ # Returns the default message for the validation type.
72
+ # If user supplied :message, it will return it. Otherwise it will return
73
+ # wrong_length on :is validation and :too_short or :too_long in the other
74
+ # types.
75
+ #
76
+ def default_message_for(validation_type)
77
+ return :message if @options[:message]
78
+ @options.key?(:is) ? :wrong_length : validation_type
79
+ end
80
+ end
81
+
82
+ # Validates the length of the given attributes. You have also to supply
83
+ # one of the following options: minimum, maximum, is or within.
84
+ #
85
+ # Note: this method is also aliased as <tt>validate_size_of</tt>.
86
+ #
87
+ # == Options
88
+ #
89
+ # * <tt>:minimum</tt> - The minimum size of the attribute.
90
+ # * <tt>:maximum</tt> - The maximum size of the attribute.
91
+ # * <tt>:is</tt> - The exact size of the attribute.
92
+ # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
93
+ # * <tt>:in</tt> - A synonym(or alias) for :within.
94
+ # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
95
+ # * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
96
+ # * <tt>:too_short</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is too short.
97
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % range.first</tt>
98
+ # * <tt>:too_long</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is too long.
99
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.too_long') % range.last</tt>
100
+ # * <tt>:wrong_length</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt> when attribute is the wrong length.
101
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % range.last</tt>
102
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
103
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % value</tt>
104
+ #
105
+ # It also accepts an extra option called :with_kind_of. If you are validating
106
+ # the size of an association array, you have to specify the kind of the array
107
+ # being validated. For example, if your post accepts maximum 10 comments, you
108
+ # can do:
109
+ #
110
+ # should_validate_length_of :comments, :maximum => 10, :with_kind_of => Comment
111
+ #
112
+ # Finally, it also accepts :token and :separator, to specify how the
113
+ # tokenizer should work. For example, if you are splitting the attribute
114
+ # per word:
115
+ #
116
+ # validates_length_of :essay, :minimum => 100, :tokenizer => lambda {|str| str.scan(/\w+/) }
117
+ #
118
+ # You could do this:
119
+ #
120
+ # should_validate_length_of :essay, :minimum => 100, :token => "word", :separator => " "
121
+ #
122
+ # == Gotcha
123
+ #
124
+ # In Rails 2.3.x, when :message is supplied, it overwrites the messages
125
+ # supplied in :wrong_length, :too_short and :too_long. However, in earlier
126
+ # versions, Rails ignores the :message option.
127
+ #
128
+ # == Examples
129
+ #
130
+ # it { should validate_length_of(:password).within(6..20) }
131
+ # it { should validate_length_of(:password).maximum(20) }
132
+ # it { should validate_length_of(:password).minimum(6) }
133
+ # it { should validate_length_of(:age).is(18) }
134
+ #
135
+ # should_validate_length_of :password, :within => 6..20
136
+ # should_validate_length_of :password, :maximum => 20
137
+ # should_validate_length_of :password, :minimum => 6
138
+ # should_validate_length_of :age, :is => 18
139
+ #
140
+ # should_validate_length_of :password do |m|
141
+ # m.minimum 6
142
+ # m.maximum 20
143
+ # end
144
+ #
145
+ def validate_length_of(*attributes, &block)
146
+ ValidateLengthOfMatcher.new(*attributes, &block).spec(self)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -1,188 +1,188 @@
1
- module Remarkable
2
- module ActiveRecord
3
- module Matchers
4
- class ValidateNumericalityOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
- arguments :collection => :attributes, :as => :attribute
6
-
7
- optional :equal_to, :greater_than_or_equal_to, :greater_than,
8
- :less_than_or_equal_to, :less_than, :message
9
-
10
- optional :only_integer, :odd, :even, :allow_nil, :allow_blank, :default => true
11
-
12
- collection_assertions :only_numeric_values?, :allow_blank?, :allow_nil?,
13
- :only_integer?, :only_odd?, :only_even?, :equals_to?,
14
- :less_than_minimum?, :more_than_maximum?
15
-
16
- # Before assertions, we rearrange the values.
17
- #
18
- # Notice that :less_than gives a maximum value while :more_than given
19
- # a minimum value. While :equal_to generate both.
20
- #
21
- before_assert do
22
- @maximum_values = {}
23
- @minimum_values = {}
24
-
25
- if value = @options[:equal_to]
26
- @maximum_values[:equal_to] = value
27
- @minimum_values[:equal_to] = value
28
- elsif value = @options[:less_than]
29
- @maximum_values[:less_than] = value - 1
30
- elsif value = @options[:greater_than]
31
- @minimum_values[:greater_than] = value + 1
32
- elsif value = @options[:less_than_or_equal_to]
33
- @maximum_values[:less_than_or_equal_to] = value
34
- elsif value = @options[:greater_than_or_equal_to]
35
- @minimum_values[:greater_than_or_equal_to] = value
36
- end
37
- end
38
-
39
- private
40
-
41
- def allow_nil?
42
- super(default_message_for(:not_a_number))
43
- end
44
-
45
- def allow_blank?
46
- super(default_message_for(:not_a_number))
47
- end
48
-
49
- def only_numeric_values?
50
- bad?("abcd", default_message_for(:not_a_number))
51
- end
52
-
53
- def only_integer?
54
- assert_bad_or_good_if_key(:only_integer, valid_value_for_test.to_f, default_message_for(:not_a_number))
55
- end
56
-
57
- # In ActiveRecord, when we supply :even, does not matter the value, it
58
- # considers that should even values should be accepted.
59
- #
60
- def only_even?
61
- return true unless @options[:even]
62
- bad?(even_valid_value_for_test + 1, default_message_for(:even))
63
- end
64
-
65
- # In ActiveRecord, when we supply :odd, does not matter the value, it
66
- # considers that should odd values should be accepted.
67
- #
68
- def only_odd?
69
- return true unless @options[:odd]
70
- bad?(even_valid_value_for_test, default_message_for(:odd))
71
- end
72
-
73
- # Check equal_to for all registered values.
74
- #
75
- def equals_to?
76
- values = {}
77
- @maximum_values.each { |k, v| values[k] = v }
78
- @minimum_values.each { |k, v| values[k] = v }
79
-
80
- values.each do |key, value|
81
- return false, :count => value unless good?(value, default_message_for(key))
82
- end
83
- true
84
- end
85
-
86
- # Check more_than_maximum? for equal_to, less_than and
87
- # less_than_or_equal_to options.
88
- #
89
- def more_than_maximum?
90
- @maximum_values.each do |key, value|
91
- return false, :count => value unless bad?(value + 1, default_message_for(key))
92
- end
93
- true
94
- end
95
-
96
- # Check less_than_minimum? for equal_to, more_than and
97
- # more_than_or_equal_to options.
98
- #
99
- def less_than_minimum?
100
- @minimum_values.each do |key, value|
101
- return false, :count => value unless bad?(value - 1, default_message_for(key))
102
- end
103
- true
104
- end
105
-
106
- # Returns a valid value for test.
107
- #
108
- def valid_value_for_test
109
- value = @options[:equal_to] || @options[:less_than_or_equal_to] || @options[:greater_than_or_equal_to]
110
-
111
- value ||= @options[:less_than] - 1 if @options[:less_than]
112
- value ||= @options[:greater_than] + 1 if @options[:greater_than]
113
-
114
- value ||= 10
115
-
116
- if @options[:even]
117
- value = (value / 2) * 2
118
- elsif @options[:odd]
119
- value = ((value / 2) * 2) + 1
120
- end
121
-
122
- value
123
- end
124
-
125
- # Returns a valid even value for test.
126
- # The method valid_value_for_test checks for :even option but does not
127
- # return necessarily an even value
128
- #
129
- def even_valid_value_for_test
130
- (valid_value_for_test / 2) * 2
131
- end
132
-
133
- # Returns the default message for each key (:odd, :even, :equal_to, ...).
134
- # If the user provided a message, we use it, otherwise we should use
135
- # the given key as message.
136
- #
137
- # For example, a default_message_for(:odd), if none is provided, will be
138
- # :odd. So we have create :odd_message in the options hash, that when
139
- # called later, will return :odd.
140
- #
141
- def default_message_for(key)
142
- if @options[:message]
143
- :message
144
- else
145
- @options[:"#{key}_message"] = key
146
- :"#{key}_message"
147
- end
148
- end
149
- end
150
-
151
- # Ensures that the given attributes accepts only numbers.
152
- #
153
- # == Options
154
- #
155
- # * <tt>:only_integer</tt> - when supplied, checks if it accepts only integers or not
156
- # * <tt>:odd</tt> - when supplied, checks if it accepts only odd values or not
157
- # * <tt>:even</tt> - when supplied, checks if it accepts only even values or not
158
- # * <tt>:equal_to</tt> - when supplied, checks if attributes are only valid when equal to given value
159
- # * <tt>:less_than</tt> - when supplied, checks if attributes are only valid when less than given value
160
- # * <tt>:greater_than</tt> - when supplied, checks if attributes are only valid when greater than given value
161
- # * <tt>:less_than_or_equal_to</tt> - when supplied, checks if attributes are only valid when less than or equal to given value
162
- # * <tt>:greater_than_or_equal_to</tt> - when supplied, checks if attributes are only valid when greater than or equal to given value
163
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
164
- # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
165
- #
166
- # == Examples
167
- #
168
- # it { should validate_numericality_of(:age).odd }
169
- # it { should validate_numericality_of(:age).even }
170
- # it { should validate_numericality_of(:age).only_integer }
171
- # it { should validate_numericality_of(:age, :odd => true) }
172
- # it { should validate_numericality_of(:age, :even => true) }
1
+ module Remarkable
2
+ module ActiveRecord
3
+ module Matchers
4
+ class ValidateNumericalityOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
5
+ arguments :collection => :attributes, :as => :attribute
6
+
7
+ optional :equal_to, :greater_than_or_equal_to, :greater_than,
8
+ :less_than_or_equal_to, :less_than, :message
9
+
10
+ optional :only_integer, :odd, :even, :allow_nil, :allow_blank, :default => true
11
+
12
+ collection_assertions :only_numeric_values?, :allow_blank?, :allow_nil?,
13
+ :only_integer?, :only_odd?, :only_even?, :equals_to?,
14
+ :less_than_minimum?, :more_than_maximum?
15
+
16
+ # Before assertions, we rearrange the values.
17
+ #
18
+ # Notice that :less_than gives a maximum value while :more_than given
19
+ # a minimum value. While :equal_to generate both.
20
+ #
21
+ before_assert do
22
+ @maximum_values = {}
23
+ @minimum_values = {}
24
+
25
+ if value = @options[:equal_to]
26
+ @maximum_values[:equal_to] = value
27
+ @minimum_values[:equal_to] = value
28
+ elsif value = @options[:less_than]
29
+ @maximum_values[:less_than] = value - 1
30
+ elsif value = @options[:greater_than]
31
+ @minimum_values[:greater_than] = value + 1
32
+ elsif value = @options[:less_than_or_equal_to]
33
+ @maximum_values[:less_than_or_equal_to] = value
34
+ elsif value = @options[:greater_than_or_equal_to]
35
+ @minimum_values[:greater_than_or_equal_to] = value
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def allow_nil?
42
+ super(default_message_for(:not_a_number))
43
+ end
44
+
45
+ def allow_blank?
46
+ super(default_message_for(:not_a_number))
47
+ end
48
+
49
+ def only_numeric_values?
50
+ bad?("abcd", default_message_for(:not_a_number))
51
+ end
52
+
53
+ def only_integer?
54
+ assert_bad_or_good_if_key(:only_integer, valid_value_for_test.to_f, default_message_for(:not_a_number))
55
+ end
56
+
57
+ # In ActiveRecord, when we supply :even, does not matter the value, it
58
+ # considers that should even values should be accepted.
59
+ #
60
+ def only_even?
61
+ return true unless @options[:even]
62
+ bad?(even_valid_value_for_test + 1, default_message_for(:even))
63
+ end
64
+
65
+ # In ActiveRecord, when we supply :odd, does not matter the value, it
66
+ # considers that should odd values should be accepted.
67
+ #
68
+ def only_odd?
69
+ return true unless @options[:odd]
70
+ bad?(even_valid_value_for_test, default_message_for(:odd))
71
+ end
72
+
73
+ # Check equal_to for all registered values.
74
+ #
75
+ def equals_to?
76
+ values = {}
77
+ @maximum_values.each { |k, v| values[k] = v }
78
+ @minimum_values.each { |k, v| values[k] = v }
79
+
80
+ values.each do |key, value|
81
+ return false, :count => value unless good?(value, default_message_for(key))
82
+ end
83
+ true
84
+ end
85
+
86
+ # Check more_than_maximum? for equal_to, less_than and
87
+ # less_than_or_equal_to options.
88
+ #
89
+ def more_than_maximum?
90
+ @maximum_values.each do |key, value|
91
+ return false, :count => value unless bad?(value + 1, default_message_for(key))
92
+ end
93
+ true
94
+ end
95
+
96
+ # Check less_than_minimum? for equal_to, more_than and
97
+ # more_than_or_equal_to options.
98
+ #
99
+ def less_than_minimum?
100
+ @minimum_values.each do |key, value|
101
+ return false, :count => value unless bad?(value - 1, default_message_for(key))
102
+ end
103
+ true
104
+ end
105
+
106
+ # Returns a valid value for test.
107
+ #
108
+ def valid_value_for_test
109
+ value = @options[:equal_to] || @options[:less_than_or_equal_to] || @options[:greater_than_or_equal_to]
110
+
111
+ value ||= @options[:less_than] - 1 if @options[:less_than]
112
+ value ||= @options[:greater_than] + 1 if @options[:greater_than]
113
+
114
+ value ||= 10
115
+
116
+ if @options[:even]
117
+ value = (value / 2) * 2
118
+ elsif @options[:odd]
119
+ value = ((value / 2) * 2) + 1
120
+ end
121
+
122
+ value
123
+ end
124
+
125
+ # Returns a valid even value for test.
126
+ # The method valid_value_for_test checks for :even option but does not
127
+ # return necessarily an even value
128
+ #
129
+ def even_valid_value_for_test
130
+ (valid_value_for_test / 2) * 2
131
+ end
132
+
133
+ # Returns the default message for each key (:odd, :even, :equal_to, ...).
134
+ # If the user provided a message, we use it, otherwise we should use
135
+ # the given key as message.
136
+ #
137
+ # For example, a default_message_for(:odd), if none is provided, will be
138
+ # :odd. So we have create :odd_message in the options hash, that when
139
+ # called later, will return :odd.
140
+ #
141
+ def default_message_for(key)
142
+ if @options[:message]
143
+ :message
144
+ else
145
+ @options[:"#{key}_message"] = key
146
+ :"#{key}_message"
147
+ end
148
+ end
149
+ end
150
+
151
+ # Ensures that the given attributes accepts only numbers.
173
152
  #
174
- # should_validate_numericality_of :age, :price
153
+ # == Options
154
+ #
155
+ # * <tt>:only_integer</tt> - when supplied, checks if it accepts only integers or not
156
+ # * <tt>:odd</tt> - when supplied, checks if it accepts only odd values or not
157
+ # * <tt>:even</tt> - when supplied, checks if it accepts only even values or not
158
+ # * <tt>:equal_to</tt> - when supplied, checks if attributes are only valid when equal to given value
159
+ # * <tt>:less_than</tt> - when supplied, checks if attributes are only valid when less than given value
160
+ # * <tt>:greater_than</tt> - when supplied, checks if attributes are only valid when greater than given value
161
+ # * <tt>:less_than_or_equal_to</tt> - when supplied, checks if attributes are only valid when less than or equal to given value
162
+ # * <tt>:greater_than_or_equal_to</tt> - when supplied, checks if attributes are only valid when greater than or equal to given value
163
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
164
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
165
+ #
166
+ # == Examples
167
+ #
168
+ # it { should validate_numericality_of(:age).odd }
169
+ # it { should validate_numericality_of(:age).even }
170
+ # it { should validate_numericality_of(:age).only_integer }
171
+ # it { should validate_numericality_of(:age, :odd => true) }
172
+ # it { should validate_numericality_of(:age, :even => true) }
173
+ #
174
+ # should_validate_numericality_of :age, :price
175
175
  # should_validate_numericality_of :price, :only_integer => false, :greater_than => 10
176
176
  #
177
177
  # should_validate_numericality_of :price do |m|
178
178
  # m.only_integer = false
179
179
  # m.greater_than = 10
180
180
  # end
181
- #
182
- def validate_numericality_of(*attributes, &block)
183
- ValidateNumericalityOfMatcher.new(*attributes, &block).spec(self)
184
- end
185
-
186
- end
187
- end
188
- end
181
+ #
182
+ def validate_numericality_of(*attributes, &block)
183
+ ValidateNumericalityOfMatcher.new(*attributes, &block).spec(self)
184
+ end
185
+
186
+ end
187
+ end
188
+ end