remarkable_activemodel 4.0.0.alpha1

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.
@@ -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