remarkable_activerecord 3.0.0

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 (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