remarkable_activemodel 4.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ module Remarkable
2
+ module ActiveModel
3
+ module Matchers
4
+ class AllowValuesForMatcher < Remarkable::ActiveModel::Base #:nodoc:
5
+ include Remarkable::Negative
6
+ arguments :collection => :attributes, :as => :attribute
7
+
8
+ optional :message
9
+ optional :in, :splat => true
10
+ optional :allow_nil, :allow_blank, :default => true
11
+
12
+ collection_assertions :is_valid?, :is_invalid?, :allow_nil?, :allow_blank?
13
+
14
+ default_options :message => /.*/
15
+
16
+ before_assert do
17
+ first_value = @options[:in].is_a?(Array) ? @options[:in].first : @options[:in]
18
+ @in_range = first_value.is_a?(Range)
19
+
20
+ @options[:in] = if @in_range
21
+ first_value.to_a[0,2] + first_value.to_a[-2,2]
22
+ else
23
+ [*@options[:in]].compact
24
+ end
25
+
26
+ @options[:in].uniq!
27
+ end
28
+
29
+ protected
30
+
31
+ def is_valid?
32
+ assert_collection :value, valid_values do |value|
33
+ good?(value)
34
+ end
35
+ end
36
+
37
+ def is_invalid?
38
+ assert_collection :value, invalid_values do |value|
39
+ bad?(value)
40
+ end
41
+ end
42
+
43
+ def valid_values
44
+ @options[:in]
45
+ end
46
+
47
+ def invalid_values
48
+ []
49
+ end
50
+
51
+ def interpolation_options
52
+ options = if @in_range
53
+ { :in => (@options[:in].first..@options[:in].last).inspect }
54
+ elsif @options[:in].is_a?(Array)
55
+ { :in => array_to_sentence(@options[:in], true, '[]') }
56
+ else
57
+ { :in => @options[:in].inspect }
58
+ end
59
+
60
+ options.merge!(:behavior => @behavior.to_s)
61
+ end
62
+
63
+ end
64
+
65
+ # Ensures that the attribute can be set to the given values. It checks
66
+ # for any message in the given attribute unless a :message is explicitely
67
+ # given.
68
+ #
69
+ # == Options
70
+ #
71
+ # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
72
+ # * <tt>:allow_blank</tt> - when supplied, validates if it allows blank or not.
73
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors[:attribute]</tt>.
74
+ # Regexp, string or symbol. Default = <tt>/.*/</tt>
75
+ #
76
+ # == Examples
77
+ #
78
+ # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
79
+ # it { should allow_values_for(:isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0") }
80
+ #
81
+ def allow_values_for(attribute, *args, &block)
82
+ options = args.extract_options!
83
+ AllowValuesForMatcher.new(attribute, options.merge!(:in => args), &block).spec(self)
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,50 @@
1
+ module Remarkable
2
+ module ActiveModel
3
+ module Matchers
4
+ class ValidateAcceptanceOfMatcher < Remarkable::ActiveModel::Base #:nodoc:
5
+ arguments :collection => :attributes, :as => :attribute
6
+
7
+ optional :accept, :message
8
+ optional :allow_nil, :default => true
9
+
10
+ collection_assertions :requires_acceptance?, :accept_is_valid?, :allow_nil?
11
+
12
+ default_options :message => :accepted
13
+
14
+ protected
15
+
16
+ def requires_acceptance?
17
+ bad?(false)
18
+ end
19
+
20
+ def accept_is_valid?
21
+ return true unless @options.key?(:accept)
22
+ good?(@options[:accept])
23
+ end
24
+
25
+ end
26
+
27
+ # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
28
+ #
29
+ # == Options
30
+ #
31
+ # * <tt>:accept</tt> - the expected value to be accepted.
32
+ # * <tt>:allow_nil</tt> - when supplied, validates if it allows nil or not.
33
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors[:attribute]</tt>.
34
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
35
+ #
36
+ # == Examples
37
+ #
38
+ # should_validate_acceptance_of :eula, :terms
39
+ # should_validate_acceptance_of :eula, :terms, :accept => true
40
+ #
41
+ # it { should validate_acceptance_of(:eula, :terms) }
42
+ # it { should validate_acceptance_of(:eula, :terms, :accept => true) }
43
+ #
44
+ def validate_acceptance_of(*attributes, &block)
45
+ ValidateAcceptanceOfMatcher.new(*attributes, &block).spec(self)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ module Remarkable
2
+ module ActiveModel
3
+ module Matchers
4
+ class ValidateConfirmationOfMatcher < Remarkable::ActiveModel::Base #:nodoc:
5
+ arguments :collection => :attributes, :as => :attribute
6
+
7
+ optional :message
8
+ collection_assertions :responds_to_confirmation?, :confirms?
9
+
10
+ default_options :message => :confirmation
11
+
12
+ protected
13
+
14
+ def responds_to_confirmation?
15
+ @subject.respond_to?(:"#{@attribute}_confirmation=")
16
+ end
17
+
18
+ def confirms?
19
+ @subject.send(:"#{@attribute}_confirmation=", 'something')
20
+ bad?('different')
21
+ end
22
+
23
+ end
24
+
25
+ # Ensures that the model cannot be saved if one of the attributes is not confirmed.
26
+ #
27
+ # == Options
28
+ #
29
+ # * <tt>:message</tt> - value the test expects to find in <tt>errors[:attribute]</tt>.
30
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.confirmation')</tt>
31
+ #
32
+ # == Examples
33
+ #
34
+ # should_validate_confirmation_of :email, :password
35
+ #
36
+ # it { should validate_confirmation_of(:email, :password) }
37
+ #
38
+ def validate_confirmation_of(*attributes, &block)
39
+ ValidateConfirmationOfMatcher.new(*attributes, &block).spec(self)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,57 @@
1
+ require File.join(File.dirname(__FILE__), 'allow_values_for_matcher')
2
+
3
+ module Remarkable
4
+ module ActiveModel
5
+ module Matchers
6
+ class ValidateExclusionOfMatcher < AllowValuesForMatcher #:nodoc:
7
+ # Don't allow it to behave in the negative way.
8
+ undef_method :does_not_match?
9
+
10
+ default_options :message => :exclusion
11
+
12
+ protected
13
+
14
+ def valid_values
15
+ if @in_range
16
+ [ @options[:in].first - 1, @options[:in].last + 1 ]
17
+ else
18
+ value = @options[:in].select{ |i| i.is_a?(String) }.max
19
+ value ? [ value.next ] : []
20
+ end
21
+ end
22
+
23
+ def invalid_values
24
+ @options[:in]
25
+ end
26
+
27
+ end
28
+
29
+ # Ensures that given values are not valid for the attribute. If a range
30
+ # is given, ensures that the attribute is not valid in the given range.
31
+ #
32
+ # If you give that :username does not accept ["admin", "user"], it will
33
+ # test that "uses" (the next of the array max value) is allowed.
34
+ #
35
+ # == Options
36
+ #
37
+ # * <tt>:in</tt> - values to test exclusion.
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[:attribute]</tt>.
41
+ # Regexp, string or symbol. Default = <tt>I18n.translate('activerecord.errors.messages.exclusion')</tt>
42
+ #
43
+ # == Examples
44
+ #
45
+ # it { should validate_exclusion_of(:username, :in => ["admin", "user"]) }
46
+ # it { should validate_exclusion_of(:age, :in => 30..60) }
47
+ #
48
+ # should_validate_exclusion_of :username, :in => ["admin", "user"]
49
+ # should_validate_exclusion_of :age, :in => 30..60
50
+ #
51
+ def validate_exclusion_of(*args, &block)
52
+ ValidateExclusionOfMatcher.new(*args, &block).spec(self)
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,57 @@
1
+ require File.join(File.dirname(__FILE__), 'allow_values_for_matcher')
2
+
3
+ module Remarkable
4
+ module ActiveModel
5
+ module Matchers
6
+ class ValidateInclusionOfMatcher < AllowValuesForMatcher #:nodoc:
7
+ # Don't allow it to behave in the negative way.
8
+ undef_method :does_not_match?
9
+
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 ]
21
+ else
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[: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
@@ -0,0 +1,150 @@
1
+ module Remarkable
2
+ module ActiveModel
3
+ module Matchers
4
+ class ValidateLengthOfMatcher < Remarkable::ActiveModel::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[: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[: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[: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[: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