remarkable_activerecord 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG +47 -0
  2. data/LICENSE +20 -0
  3. data/README +2 -0
  4. data/lib/remarkable_activerecord/base.rb +238 -0
  5. data/lib/remarkable_activerecord/human_names.rb +37 -0
  6. data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +34 -0
  7. data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +94 -0
  8. data/lib/remarkable_activerecord/matchers/association_matcher.rb +235 -0
  9. data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +68 -0
  10. data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +57 -0
  11. data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +30 -0
  12. data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +80 -0
  13. data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +51 -0
  14. data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +99 -0
  15. data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +45 -0
  16. data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +47 -0
  17. data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +47 -0
  18. data/lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb +123 -0
  19. data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +184 -0
  20. data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +29 -0
  21. data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +151 -0
  22. data/lib/remarkable_activerecord.rb +29 -0
  23. data/locale/en.yml +253 -0
  24. data/spec/allow_mass_assignment_of_matcher_spec.rb +57 -0
  25. data/spec/allow_values_for_matcher_spec.rb +56 -0
  26. data/spec/association_matcher_spec.rb +616 -0
  27. data/spec/have_column_matcher_spec.rb +73 -0
  28. data/spec/have_index_matcher_spec.rb +68 -0
  29. data/spec/have_readonly_attributes_matcher_spec.rb +47 -0
  30. data/spec/have_scope_matcher_spec.rb +69 -0
  31. data/spec/model_builder.rb +101 -0
  32. data/spec/rcov.opts +2 -0
  33. data/spec/spec.opts +4 -0
  34. data/spec/spec_helper.rb +27 -0
  35. data/spec/validate_acceptance_of_matcher_spec.rb +68 -0
  36. data/spec/validate_associated_matcher_spec.rb +122 -0
  37. data/spec/validate_confirmation_of_matcher_spec.rb +58 -0
  38. data/spec/validate_exclusion_of_matcher_spec.rb +88 -0
  39. data/spec/validate_inclusion_of_matcher_spec.rb +84 -0
  40. data/spec/validate_length_of_matcher_spec.rb +165 -0
  41. data/spec/validate_numericality_of_matcher_spec.rb +180 -0
  42. data/spec/validate_presence_of_matcher_spec.rb +52 -0
  43. data/spec/validate_uniqueness_of_matcher_spec.rb +150 -0
  44. metadata +112 -0
@@ -0,0 +1,84 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_inclusion_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :product, :title => :string, :size => :string do
9
+ validates_inclusion_of :title, :size, options
10
+ end
11
+
12
+ validate_inclusion_of(:title, :size)
13
+ end
14
+
15
+ describe 'messages' do
16
+ before(:each){ @matcher = define_and_validate(:in => 2..10) }
17
+
18
+ it 'should contain a description' do
19
+ @matcher.in(2..10)
20
+ @matcher.description.should == 'ensure inclusion of title and size in 2..10'
21
+
22
+ @matcher = validate_inclusion_of(:title, :size).in('X', 'Y', 'Z')
23
+ @matcher.description.should == 'ensure inclusion of title and size in "X", "Y", and "Z"'
24
+ end
25
+
26
+ it 'should set is_valid? message' do
27
+ @matcher.in(1..10).matches?(@model)
28
+ @matcher.failure_message.should == 'Expected Product to be valid when title is set to 1'
29
+ end
30
+
31
+ it 'should set is_invalid? message' do
32
+ @matcher.in(3..10).matches?(@model)
33
+ @matcher.failure_message.should == 'Expected Product to be invalid when title is set to 2'
34
+ end
35
+
36
+ it 'should set allow_nil? message' do
37
+ @matcher.allow_nil.matches?(@model)
38
+ @matcher.failure_message.should == 'Expected Product to allow nil values for title'
39
+ end
40
+
41
+ it 'should set allow_blank? message' do
42
+ @matcher.allow_blank.matches?(@model)
43
+ @matcher.failure_message.should == 'Expected Product to allow blank values for title'
44
+ end
45
+ end
46
+
47
+ describe 'matchers' do
48
+ it { should define_and_validate(:in => ['X', 'Y', 'Z']).in('X', 'Y', 'Z') }
49
+ it { should_not define_and_validate(:in => ['X', 'Y', 'Z']).in('A') }
50
+
51
+ it { should define_and_validate(:in => 2..3).in(2..3) }
52
+ it { should define_and_validate(:in => 2..20).in(2..20) }
53
+ it { should_not define_and_validate(:in => 2..20).in(1..20) }
54
+ it { should_not define_and_validate(:in => 2..20).in(3..20) }
55
+ it { should_not define_and_validate(:in => 2..20).in(2..19) }
56
+ it { should_not define_and_validate(:in => 2..20).in(2..21) }
57
+
58
+ it { should define_and_validate(:in => ['X', 'Y', 'Z'], :message => 'valid').in('X', 'Y', 'Z').message('valid') }
59
+
60
+ create_optional_boolean_specs(:allow_nil, self, :in => ['X'])
61
+ create_optional_boolean_specs(:allow_blank, self, :in => ['X'])
62
+ end
63
+
64
+ describe 'macros' do
65
+ describe 'with array' do
66
+ before(:each){ define_and_validate(:in => ['X', 'Y', 'Z']) }
67
+
68
+ should_validate_inclusion_of :title, :in => ['X']
69
+ should_validate_inclusion_of :title, :size, :in => ['X']
70
+ should_not_validate_inclusion_of :title, :size, :in => ['A']
71
+ end
72
+
73
+ describe 'with range' do
74
+ before(:each){ define_and_validate(:in => 2..20) }
75
+
76
+ should_validate_inclusion_of :title, :in => 2..20
77
+ should_not_validate_inclusion_of :title, :in => 1..20
78
+ should_not_validate_inclusion_of :title, :in => 3..20
79
+ should_not_validate_inclusion_of :title, :in => 2..19
80
+ should_not_validate_inclusion_of :title, :in => 2..21
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1,165 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_length_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ options = options.merge(:within => 3..5) if options.slice(:in, :within, :maximum, :minimum, :is).empty?
9
+
10
+ @model = define_model :product, :size => :string, :category => :string do
11
+ validates_length_of :size, options
12
+ end
13
+
14
+ validate_length_of(:size)
15
+ end
16
+
17
+ describe 'messages' do
18
+ before(:each){ @matcher = define_and_validate }
19
+
20
+ it 'should contain a description' do
21
+ @matcher.within(3..5)
22
+ @matcher.description.should == 'ensure length of size is within 3..5 characters'
23
+
24
+ @matcher.within(nil).in(3..5)
25
+ @matcher.description.should == 'ensure length of size is within 3..5 characters'
26
+
27
+ @matcher.in(nil).is(3)
28
+ @matcher.description.should == 'ensure length of size is equal to 3 characters'
29
+
30
+ @matcher.is(nil).maximum(5)
31
+ @matcher.description.should == 'ensure length of size is maximum 5 characters'
32
+
33
+ @matcher.maximum(nil).minimum(3)
34
+ @matcher.description.should == 'ensure length of size is minimum 3 characters'
35
+
36
+ @matcher.allow_nil(false)
37
+ @matcher.description.should == 'ensure length of size is minimum 3 characters and not allowing nil values'
38
+
39
+ @matcher.allow_blank
40
+ @matcher.description.should == 'ensure length of size is minimum 3 characters, not allowing nil values, and allowing blank values'
41
+ end
42
+
43
+ it 'should set less_than_min_length? message' do
44
+ @matcher.within(4..5).matches?(@model)
45
+ @matcher.failure_message.should == 'Expected Product to be invalid when size length is less than 4 characters'
46
+ end
47
+
48
+ it 'should set exactly_min_length? message' do
49
+ @matcher.should_receive(:less_than_min_length?).and_return(true)
50
+ @matcher.within(2..5).matches?(@model)
51
+ @matcher.failure_message.should == 'Expected Product to be valid when size length is 2 characters'
52
+ end
53
+
54
+ it 'should set more_than_max_length? message' do
55
+ @matcher.within(3..4).matches?(@model)
56
+ @matcher.failure_message.should == 'Expected Product to be invalid when size length is more than 4 characters'
57
+ end
58
+
59
+ it 'should set exactly_max_length? message' do
60
+ @matcher.should_receive(:more_than_max_length?).and_return(true)
61
+ @matcher.within(3..6).matches?(@model)
62
+ @matcher.failure_message.should == 'Expected Product to be valid when size length is 6 characters'
63
+ end
64
+
65
+ it 'should set allow_blank? message' do
66
+ @matcher.within(3..5).allow_blank.matches?(@model)
67
+ @matcher.failure_message.should == 'Expected Product to allow blank values for size'
68
+ end
69
+
70
+ it 'should set allow_nil? message' do
71
+ @matcher.within(3..5).allow_nil.matches?(@model)
72
+ @matcher.failure_message.should == 'Expected Product to allow nil values for size'
73
+ end
74
+ end
75
+
76
+ describe 'matcher' do
77
+ # Wrap specs without options. Usually a couple specs.
78
+ describe 'without options' do
79
+ before(:each){ define_and_validate }
80
+
81
+ it { should validate_length_of(:size, :within => 3..5) }
82
+ it { should_not validate_length_of(:category, :within => 3..5) }
83
+ end
84
+
85
+ describe "with message option" do
86
+
87
+ if RAILS_VERSION =~ /^2.3/
88
+ it { should define_and_validate(:message => 'not valid').within(3..5).message('not valid') }
89
+ it { should_not define_and_validate(:message => 'not valid').within(3..5).message('valid') }
90
+ else
91
+ it { should define_and_validate(:too_short => 'not valid', :too_long => 'not valid').within(3..5).message('not valid') }
92
+ it { should_not define_and_validate(:too_short => 'not valid', :too_long => 'not valid').within(3..5).message('valid') }
93
+ end
94
+
95
+ it { should define_and_validate(:is => 4, :message => 'not valid').is(4).message('not valid') }
96
+ it { should_not define_and_validate(:is => 4, :message => 'not valid').is(4).message('valid') }
97
+ end
98
+
99
+ describe "with too_short option" do
100
+ it { should define_and_validate(:too_short => 'not valid').within(3..5).too_short('not valid') }
101
+ it { should_not define_and_validate(:too_short => 'not valid').within(3..5).too_short('valid') }
102
+ end
103
+
104
+ describe "with too_long option" do
105
+ it { should define_and_validate(:too_long => 'not valid').within(3..5).too_long('not valid') }
106
+ it { should_not define_and_validate(:too_long => 'not valid').within(3..5).too_long('valid') }
107
+ end
108
+
109
+ describe "with wrong_length option" do
110
+ it { should define_and_validate(:is => 4, :wrong_length => 'not valid').is(4).wrong_length('not valid') }
111
+ it { should_not define_and_validate(:is => 4, :wrong_length => 'not valid').is(4).wrong_length('valid') }
112
+ end
113
+
114
+ describe "with within option" do
115
+ it { should define_and_validate(:within => 3..5).within(3..5) }
116
+ it { should_not define_and_validate(:within => 3..5).within(2..5) }
117
+ it { should_not define_and_validate(:within => 3..5).within(4..5) }
118
+ it { should_not define_and_validate(:within => 3..5).within(3..4) }
119
+ it { should_not define_and_validate(:within => 3..5).within(3..6) }
120
+ end
121
+
122
+ describe "with in option" do
123
+ it { should define_and_validate(:in => 3..5).within(3..5) }
124
+ it { should_not define_and_validate(:in => 3..5).within(2..5) }
125
+ it { should_not define_and_validate(:in => 3..5).within(4..5) }
126
+ it { should_not define_and_validate(:in => 3..5).within(3..4) }
127
+ it { should_not define_and_validate(:in => 3..5).within(3..6) }
128
+ end
129
+
130
+ describe "with minimum option" do
131
+ it { should define_and_validate(:minimum => 3).minimum(3) }
132
+ it { should_not define_and_validate(:minimum => 3).minimum(2) }
133
+ it { should_not define_and_validate(:minimum => 3).minimum(4) }
134
+ end
135
+
136
+ describe "with maximum option" do
137
+ it { should define_and_validate(:maximum => 3).maximum(3) }
138
+ it { should_not define_and_validate(:maximum => 3).maximum(2) }
139
+ it { should_not define_and_validate(:maximum => 3).maximum(4) }
140
+ end
141
+
142
+ describe "with is option" do
143
+ it { should define_and_validate(:is => 3).is(3) }
144
+ it { should_not define_and_validate(:is => 3).is(2) }
145
+ it { should_not define_and_validate(:is => 3).is(4) }
146
+ end
147
+
148
+ # Those are macros to test optionals which accept only boolean values
149
+ create_optional_boolean_specs(:allow_nil, self)
150
+ create_optional_boolean_specs(:allow_blank, self)
151
+ end
152
+
153
+ # In macros we include just a few tests to assure that everything works properly
154
+ describe 'macros' do
155
+ before(:each) { define_and_validate }
156
+
157
+ should_validate_length_of :size, :in => 3..5
158
+ should_validate_length_of :size, :within => 3..5
159
+ should_not_validate_length_of :size, :within => 2..5
160
+ should_not_validate_length_of :size, :within => 4..5
161
+ should_not_validate_length_of :size, :within => 3..4
162
+ should_not_validate_length_of :size, :within => 3..6
163
+ should_not_validate_length_of :category, :in => 3..5
164
+ end
165
+ end
@@ -0,0 +1,180 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_numericality_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :product, :price => :integer, :size => :integer do
9
+ validates_numericality_of :price, options
10
+ end
11
+
12
+ validate_numericality_of(:price)
13
+ end
14
+
15
+ describe 'messages' do
16
+ before(:each){ @matcher = define_and_validate }
17
+
18
+ # To tests descriptions, we don't need to use mocks neither stubs.
19
+ # We just need to define a matcher.
20
+ #
21
+ it 'should contain a description' do
22
+ matcher = validate_numericality_of(:age)
23
+ matcher.description.should == 'ensure numericality of age'
24
+
25
+ matcher.allow_nil(false)
26
+ matcher.description.should == 'ensure numericality of age not allowing nil values'
27
+
28
+ matcher.allow_blank
29
+ matcher.description.should == 'ensure numericality of age not allowing nil values and allowing blank values'
30
+
31
+ matcher = validate_numericality_of(:age, :only_integer => true)
32
+ matcher.description.should == 'ensure numericality of age allowing only integer values'
33
+
34
+ matcher = validate_numericality_of(:age, :even => true)
35
+ matcher.description.should == 'ensure numericality of age allowing only even values'
36
+
37
+ matcher = validate_numericality_of(:age, :odd => true)
38
+ matcher.description.should == 'ensure numericality of age allowing only odd values'
39
+
40
+ matcher = validate_numericality_of(:age, :equal_to => 10)
41
+ matcher.description.should == 'ensure numericality of age is equal to 10'
42
+
43
+ matcher = validate_numericality_of(:age, :less_than_or_equal_to => 10)
44
+ matcher.description.should == 'ensure numericality of age is less than or equal to 10'
45
+
46
+ matcher = validate_numericality_of(:age, :greater_than_or_equal_to => 10)
47
+ matcher.description.should == 'ensure numericality of age is greater than or equal to 10'
48
+
49
+ matcher = validate_numericality_of(:age, :less_than => 10)
50
+ matcher.description.should == 'ensure numericality of age is less than 10'
51
+
52
+ matcher = validate_numericality_of(:age, :greater_than => 10)
53
+ matcher.description.should == 'ensure numericality of age is greater than 10'
54
+
55
+ matcher = validate_numericality_of(:age, :greater_than => 10, :less_than => 20)
56
+ matcher.description.should == 'ensure numericality of age is greater than 10 and is less than 20'
57
+ end
58
+
59
+ it 'should set only_numeric_values? message' do
60
+ @matcher.should_receive(:only_numeric_values?).and_return(false)
61
+ @matcher.matches?(@model)
62
+ @matcher.failure_message.should == 'Expected Product to allow only numeric values for price'
63
+ @matcher.negative_failure_message.should == 'Did not expect Product to allow only numeric values for price'
64
+ end
65
+
66
+ it 'should set only_integer_values? message' do
67
+ @matcher.should_receive(:only_integer?).and_return([false, { :not => '' }])
68
+ @matcher.matches?(@model)
69
+ @matcher.failure_message.should == 'Expected Product to allow only integer values for price'
70
+ end
71
+
72
+ it 'should set only_odd_values? message' do
73
+ @matcher.should_receive(:only_odd?).and_return(false)
74
+ @matcher.matches?(@model)
75
+ @matcher.failure_message.should == 'Expected Product to allow only odd values for price'
76
+ end
77
+
78
+ it 'should set only_even_values? message' do
79
+ @matcher.should_receive(:only_even?).and_return(false)
80
+ @matcher.matches?(@model)
81
+ @matcher.failure_message.should == 'Expected Product to allow only even values for price'
82
+ end
83
+
84
+ it 'should set equals_to? message' do
85
+ @matcher.should_receive(:equals_to?).and_return([false, { :count => 10 }])
86
+ @matcher.matches?(@model)
87
+ @matcher.failure_message.should == 'Expected Product to be valid only when price is equal to 10'
88
+ end
89
+
90
+ it 'should set less_than_minimum? message' do
91
+ @matcher.should_receive(:less_than_minimum?).and_return([false, { :count => 10 }])
92
+ @matcher.matches?(@model)
93
+ @matcher.failure_message.should == 'Expected Product to be invalid when price is less than 10'
94
+ end
95
+
96
+ it 'should set more_than_maximum? message' do
97
+ @matcher.should_receive(:more_than_maximum?).and_return([false, { :count => 10 }])
98
+ @matcher.matches?(@model)
99
+ @matcher.failure_message.should == 'Expected Product to be invalid when price is greater than 10'
100
+ end
101
+ end
102
+
103
+ describe 'matcher' do
104
+ # Wrap specs without options. Usually a couple specs.
105
+ describe 'without options' do
106
+ before(:each){ define_and_validate }
107
+
108
+ it { should validate_numericality_of(:price) }
109
+ it { should_not validate_numericality_of(:size) }
110
+ end
111
+
112
+ # Wrap each option inside a describe group.
113
+ describe 'with equal_to option' do
114
+ it { should define_and_validate(:equal_to => 100).equal_to(100) }
115
+ it { should_not define_and_validate(:equal_to => 100).equal_to(99) }
116
+ it { should_not define_and_validate(:equal_to => 100).equal_to(101) }
117
+ end
118
+
119
+ describe 'with less_than option' do
120
+ it { should define_and_validate(:less_than => 100).less_than(100) }
121
+ it { should_not define_and_validate(:less_than => 100).less_than(99) }
122
+ it { should_not define_and_validate(:less_than => 100).less_than(101) }
123
+ end
124
+
125
+ describe 'with greater_than option' do
126
+ it { should define_and_validate(:greater_than => 100).greater_than(100) }
127
+ it { should_not define_and_validate(:greater_than => 100).greater_than(99) }
128
+ it { should_not define_and_validate(:greater_than => 100).greater_than(101) }
129
+ end
130
+
131
+ describe 'with less_than_or_equal_to option' do
132
+ it { should define_and_validate(:less_than_or_equal_to => 100).less_than_or_equal_to(100) }
133
+ it { should_not define_and_validate(:less_than_or_equal_to => 100).less_than_or_equal_to(99) }
134
+ it { should_not define_and_validate(:less_than_or_equal_to => 100).less_than_or_equal_to(101) }
135
+ end
136
+
137
+ describe 'with greater_than_or_equal_to option' do
138
+ it { should define_and_validate(:greater_than_or_equal_to => 100).greater_than_or_equal_to(100) }
139
+ it { should_not define_and_validate(:greater_than_or_equal_to => 100).greater_than_or_equal_to(99) }
140
+ it { should_not define_and_validate(:greater_than_or_equal_to => 100).greater_than_or_equal_to(101) }
141
+ end
142
+
143
+ describe "with even option" do
144
+ it { should define_and_validate(:even => true).even }
145
+ it { should_not define_and_validate.even(true) }
146
+ end
147
+
148
+ describe "with odd option" do
149
+ it { should define_and_validate(:odd => true).odd }
150
+ it { should_not define_and_validate.odd(true) }
151
+ end
152
+
153
+ describe "with several options" do
154
+ it { should define_and_validate(:less_than => 100, :greater_than => 10).less_than(100).greater_than(10) }
155
+ it { should define_and_validate(:less_than => 100, :message => 'not valid').less_than(100).message('not valid') }
156
+ it { should define_and_validate(:less_than_or_equal_to => 100, :greater_than => 1).less_than_or_equal_to(100).greater_than(1) }
157
+ end
158
+
159
+ # A macro to spec messages
160
+ create_message_specs(self)
161
+
162
+ # Those are macros to test optionals which accept only boolean values
163
+ create_optional_boolean_specs(:allow_nil, self)
164
+ create_optional_boolean_specs(:allow_blank, self)
165
+ create_optional_boolean_specs(:only_integer, self)
166
+ end
167
+
168
+ # In macros we include just a few tests to assure that everything works properly
169
+ describe 'macros' do
170
+ before(:each) { define_and_validate(:less_than => 100000, :greater_than => 9999) }
171
+
172
+ should_validate_numericality_of :price
173
+ should_validate_numericality_of :price, :less_than => 100000
174
+ should_validate_numericality_of :price, :greater_than => 9999
175
+ should_validate_numericality_of :price, :less_than => 100000, :greater_than => 999
176
+ should_not_validate_numericality_of :size
177
+ should_not_validate_numericality_of :price, :less_than => 55555
178
+ should_not_validate_numericality_of :price, :greater_than => 55555
179
+ end
180
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_presence_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :product, :title => :string, :size => :string, :category => :string do
9
+ validates_presence_of :title, :size, options
10
+ end
11
+
12
+ validate_presence_of(:title, :size)
13
+ end
14
+
15
+ describe 'messages' do
16
+ before(:each){ @matcher = define_and_validate }
17
+
18
+ it 'should contain a description' do
19
+ @matcher.description.should == 'require title and size to be set'
20
+ end
21
+
22
+ it 'should set allow_nil? message' do
23
+ @matcher = validate_presence_of(:category)
24
+ @matcher.matches?(@model)
25
+ @matcher.failure_message.should == 'Expected Product to require category to be set'
26
+ @matcher.negative_failure_message.should == 'Did not expect Product to require category to be set'
27
+ end
28
+ end
29
+
30
+ describe 'matchers' do
31
+ describe 'without options' do
32
+ before(:each){ define_and_validate }
33
+
34
+ it { should validate_presence_of(:size) }
35
+ it { should validate_presence_of(:title) }
36
+ it { should validate_presence_of(:title, :size) }
37
+ it { should_not validate_presence_of(:category) }
38
+ end
39
+
40
+ create_message_specs(self)
41
+ end
42
+
43
+ describe 'macros' do
44
+ before(:each){ define_and_validate }
45
+
46
+ should_validate_presence_of(:size)
47
+ should_validate_presence_of(:title)
48
+ should_validate_presence_of(:size, :title)
49
+ should_not_validate_presence_of(:category)
50
+ end
51
+ end
52
+
@@ -0,0 +1,150 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_uniqueness_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :user, :username => :string, :email => :integer, :access_code => :string do
9
+ validates_uniqueness_of :username, options
10
+ end
11
+
12
+ # Create a model
13
+ User.create(:username => 'jose')
14
+
15
+ validate_uniqueness_of(:username)
16
+ end
17
+
18
+ describe 'messages' do
19
+ before(:each){ @matcher = define_and_validate }
20
+
21
+ it 'should contain a description' do
22
+ @matcher.description.should == 'require unique values for username'
23
+
24
+ @matcher.scope(:email)
25
+ @matcher.description.should == 'require unique values for username scoped to [:email]'
26
+
27
+ @matcher.scope(:email, :access_code)
28
+ @matcher.description.should == 'require unique values for username scoped to [:email, :access_code]'
29
+
30
+ @matcher.case_sensitive
31
+ @matcher.description.should == 'require unique values for username scoped to [:email, :access_code] and case sensitive'
32
+
33
+ @matcher.case_sensitive(false)
34
+ @matcher.description.should == 'require unique values for username scoped to [:email, :access_code] and case insensitive'
35
+
36
+ @matcher.allow_nil
37
+ @matcher.description.should == 'require unique values for username scoped to [:email, :access_code], case insensitive, and allowing nil values'
38
+
39
+ @matcher.allow_blank(false)
40
+ @matcher.description.should == 'require unique values for username scoped to [:email, :access_code], case insensitive, allowing nil values, and not allowing blank values'
41
+ end
42
+
43
+ it 'should set responds_to_scope? message' do
44
+ @matcher.scope(:title).matches?(@model)
45
+ @matcher.failure_message.should == 'Expected User instance responds to title='
46
+ end
47
+
48
+ it 'should set is_unique? message' do
49
+ @matcher = validate_uniqueness_of(:email)
50
+ @matcher.matches?(@model)
51
+ @matcher.failure_message.should == 'Expected User to require unique values for email'
52
+ end
53
+
54
+ it 'should set case_sensitive? message' do
55
+ @matcher.case_sensitive(false).matches?(@model)
56
+ @matcher.failure_message.should == 'Expected User to not be case sensitive on username validation'
57
+ end
58
+
59
+ it 'should valid with new scope' do
60
+ @matcher.scope(:email).matches?(@model)
61
+ @matcher.failure_message.should == 'Expected User to be valid when username scope (email) change'
62
+ end
63
+ end
64
+
65
+ describe 'matcher' do
66
+
67
+ describe 'without options' do
68
+ before(:each){ define_and_validate }
69
+
70
+ it { should validate_uniqueness_of(:username) }
71
+ it { should_not validate_uniqueness_of(:email) }
72
+ end
73
+
74
+ describe 'scoped to' do
75
+ it { should define_and_validate(:scope => :email).scope(:email) }
76
+ it { should define_and_validate(:scope => [:email, :access_code]).scope(:email, :access_code) }
77
+ it { should_not define_and_validate(:scope => :email).scope(:title) }
78
+ it { should_not define_and_validate(:scope => :email).scope(:access_code) }
79
+ end
80
+
81
+ create_message_specs(self)
82
+
83
+ # Those are macros to test optionals which accept only boolean values
84
+ create_optional_boolean_specs(:allow_nil, self)
85
+ create_optional_boolean_specs(:allow_blank, self)
86
+ create_optional_boolean_specs(:case_sensitive, self)
87
+ end
88
+
89
+ describe 'errors' do
90
+ it 'should raise an error if no object is found' do
91
+ @matcher = define_and_validate
92
+ User.destroy_all
93
+
94
+ proc { @matcher.matches?(@model) }.should raise_error(ScriptError)
95
+ end
96
+
97
+ it 'should raise an error if no object with not nil attribute is found' do
98
+ @matcher = define_and_validate.allow_nil
99
+ User.destroy_all
100
+
101
+ User.create(:username => nil)
102
+ proc { @matcher.matches?(@model) }.should raise_error(ScriptError)
103
+
104
+ User.create(:username => 'jose')
105
+ proc { @matcher.matches?(@model) }.should_not raise_error(ScriptError)
106
+ end
107
+
108
+ it 'should raise an error if no object with not nil attribute is found' do
109
+ @matcher = define_and_validate.allow_blank
110
+ User.destroy_all
111
+
112
+ User.create(:username => '')
113
+ proc { @matcher.matches?(@model) }.should raise_error(ScriptError)
114
+
115
+ User.create(:username => 'jose')
116
+ proc { @matcher.matches?(@model) }.should_not raise_error(ScriptError)
117
+ end
118
+
119
+ it 'should raise an error if cannot find a new scope value' do
120
+ @matcher = define_and_validate(:scope => :email).scope(:email)
121
+
122
+ User.stub!(:find).and_return do |many, conditions|
123
+ if many == :all
124
+ 1000.upto(1100).map{|i| User.new(:email => i) }
125
+ else
126
+ User.new(:username => 'jose')
127
+ end
128
+ end
129
+ proc { @matcher.matches?(@model) }.should raise_error(ScriptError)
130
+
131
+ User.stub!(:find).and_return do |many, conditions|
132
+ if many == :all
133
+ 1000.upto(1099).map{|i| User.new(:email => i) }
134
+ else
135
+ User.new(:username => 'jose')
136
+ end
137
+ end
138
+ proc { @matcher.matches?(@model) }.should_not raise_error(ScriptError)
139
+ end
140
+ end
141
+
142
+ describe 'macros' do
143
+ before(:each){ define_and_validate(:scope => :email) }
144
+
145
+ should_validate_uniqueness_of :username
146
+ should_validate_uniqueness_of :username, :scope => :email
147
+ should_not_validate_uniqueness_of :email
148
+ should_not_validate_uniqueness_of :username, :scope => :access_code
149
+ end
150
+ end