shoulda-matchers 2.8.0 → 3.0.0.rc1

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/.hound_config/ruby.yml +7 -0
  3. data/.travis.yml +11 -54
  4. data/Appraisals +45 -100
  5. data/CONTRIBUTING.md +51 -7
  6. data/Gemfile +7 -19
  7. data/Gemfile.lock +60 -134
  8. data/Guardfile +5 -0
  9. data/NEWS.md +203 -0
  10. data/README.md +95 -50
  11. data/Rakefile +1 -0
  12. data/doc_config/yard/templates/default/layout/html/setup.rb +1 -1
  13. data/gemfiles/4.0.0.gemfile +10 -7
  14. data/gemfiles/4.0.0.gemfile.lock +103 -79
  15. data/gemfiles/4.0.1.gemfile +10 -7
  16. data/gemfiles/4.0.1.gemfile.lock +109 -83
  17. data/gemfiles/4.1.gemfile +10 -7
  18. data/gemfiles/4.1.gemfile.lock +109 -85
  19. data/gemfiles/4.2.gemfile +10 -9
  20. data/gemfiles/4.2.gemfile.lock +86 -78
  21. data/lib/shoulda/matchers.rb +13 -18
  22. data/lib/shoulda/matchers/action_controller.rb +4 -1
  23. data/lib/shoulda/matchers/action_controller/flash_store.rb +95 -0
  24. data/lib/shoulda/matchers/action_controller/{strong_parameters_matcher.rb → permit_matcher.rb} +147 -30
  25. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  26. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +1 -1
  27. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  28. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  29. data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -1
  30. data/lib/shoulda/matchers/action_controller/route_params.rb +15 -6
  31. data/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  32. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +30 -136
  33. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +28 -109
  34. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +103 -0
  35. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +1 -12
  36. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +79 -10
  37. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +10 -0
  38. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +21 -0
  39. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +24 -0
  40. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -5
  41. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +29 -10
  42. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +27 -10
  43. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -12
  44. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +56 -20
  45. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +3 -11
  46. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +65 -0
  47. data/lib/shoulda/matchers/active_record/association_matcher.rb +40 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +21 -7
  49. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +11 -40
  50. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +2 -6
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +137 -22
  53. data/lib/shoulda/matchers/configuration.rb +20 -0
  54. data/lib/shoulda/matchers/doublespeak.rb +11 -1
  55. data/lib/shoulda/matchers/doublespeak/double.rb +29 -11
  56. data/lib/shoulda/matchers/doublespeak/double_collection.rb +4 -3
  57. data/lib/shoulda/matchers/doublespeak/method_call.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/object_double.rb +7 -2
  59. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +4 -3
  60. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +3 -3
  61. data/lib/shoulda/matchers/doublespeak/world.rb +21 -1
  62. data/lib/shoulda/matchers/integrations.rb +43 -0
  63. data/lib/shoulda/matchers/integrations/configuration.rb +68 -0
  64. data/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  65. data/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  66. data/lib/shoulda/matchers/integrations/libraries.rb +15 -0
  67. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +31 -0
  68. data/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  69. data/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  70. data/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  71. data/lib/shoulda/matchers/integrations/libraries/rails.rb +30 -0
  72. data/lib/shoulda/matchers/integrations/rails.rb +12 -0
  73. data/lib/shoulda/matchers/integrations/registry.rb +28 -0
  74. data/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  75. data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  76. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  77. data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  78. data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  79. data/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  80. data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  81. data/lib/shoulda/matchers/rails_shim.rb +0 -40
  82. data/lib/shoulda/matchers/version.rb +1 -1
  83. data/script/SUPPORTED_VERSIONS +1 -1
  84. data/script/update_gems_in_all_appraisals +14 -0
  85. data/shoulda-matchers.gemspec +2 -2
  86. data/spec/acceptance/active_model_integration_spec.rb +4 -1
  87. data/spec/acceptance/independent_matchers_spec.rb +6 -6
  88. data/spec/acceptance/multiple_libraries_integration_spec.rb +52 -0
  89. data/spec/acceptance/rails_integration_spec.rb +15 -5
  90. data/spec/acceptance_spec_helper.rb +8 -0
  91. data/spec/doublespeak_spec_helper.rb +14 -0
  92. data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +110 -0
  93. data/spec/support/acceptance/helpers.rb +2 -0
  94. data/spec/support/acceptance/helpers/base_helpers.rb +6 -1
  95. data/spec/support/acceptance/helpers/command_helpers.rb +6 -2
  96. data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -8
  97. data/spec/support/acceptance/helpers/n_unit_helpers.rb +25 -0
  98. data/spec/support/acceptance/helpers/rspec_helpers.rb +2 -0
  99. data/spec/support/acceptance/helpers/step_helpers.rb +13 -19
  100. data/spec/support/acceptance/matchers/have_output.rb +1 -1
  101. data/spec/support/tests/bundle.rb +1 -1
  102. data/spec/support/tests/command_runner.rb +25 -13
  103. data/spec/support/tests/current_bundle.rb +47 -0
  104. data/spec/support/tests/database.rb +28 -0
  105. data/spec/support/tests/database_adapters/postgresql.rb +25 -0
  106. data/spec/support/tests/database_adapters/sqlite3.rb +26 -0
  107. data/spec/support/tests/database_configuration.rb +33 -0
  108. data/spec/support/tests/database_configuration_registry.rb +28 -0
  109. data/spec/support/tests/filesystem.rb +25 -2
  110. data/spec/support/unit/helpers/active_record_versions.rb +12 -0
  111. data/spec/support/unit/helpers/class_builder.rb +6 -2
  112. data/spec/support/unit/helpers/column_type_helpers.rb +26 -0
  113. data/spec/support/unit/helpers/controller_builder.rb +0 -28
  114. data/spec/support/unit/helpers/database_helpers.rb +18 -0
  115. data/spec/support/unit/helpers/model_builder.rb +38 -6
  116. data/spec/support/unit/helpers/rails_versions.rb +2 -2
  117. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +9 -8
  118. data/spec/support/unit/matchers/fail_with_message_matcher.rb +1 -1
  119. data/spec/support/unit/rails_application.rb +29 -13
  120. data/spec/support/unit/record_validating_confirmation_builder.rb +1 -2
  121. data/spec/support/unit/shared_examples/set_session_or_flash.rb +355 -0
  122. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +433 -0
  123. data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +1 -5
  124. data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +37 -0
  125. data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +23 -147
  126. data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +8 -285
  127. data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +562 -0
  128. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +81 -14
  129. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +16 -8
  130. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +101 -9
  131. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +39 -1
  132. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +39 -1
  133. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +39 -0
  134. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -17
  135. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -17
  136. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -17
  137. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +838 -271
  138. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -19
  139. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +93 -0
  140. data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +3 -3
  141. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +25 -0
  142. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +905 -0
  143. data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +17 -11
  144. data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +1 -1
  145. data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +144 -43
  146. data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +1 -1
  147. data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +36 -11
  148. data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +29 -16
  149. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +8 -5
  150. data/spec/unit/shoulda/matchers/doublespeak_spec.rb +1 -1
  151. data/spec/unit_spec_helper.rb +15 -14
  152. data/spec/warnings_spy.rb +1 -1
  153. metadata +68 -29
  154. data/docs.watchr +0 -5
  155. data/gemfiles/3.0.gemfile +0 -26
  156. data/gemfiles/3.0.gemfile.lock +0 -173
  157. data/gemfiles/3.1.gemfile +0 -32
  158. data/gemfiles/3.1.gemfile.lock +0 -212
  159. data/gemfiles/3.1_1.9.2.gemfile +0 -32
  160. data/gemfiles/3.1_1.9.2.gemfile.lock +0 -212
  161. data/gemfiles/3.2.gemfile +0 -33
  162. data/gemfiles/3.2.gemfile.lock +0 -212
  163. data/gemfiles/3.2_1.9.2.gemfile +0 -31
  164. data/gemfiles/3.2_1.9.2.gemfile.lock +0 -207
  165. data/lib/shoulda/matchers/assertion_error.rb +0 -27
  166. data/lib/shoulda/matchers/doublespeak/structs.rb +0 -10
  167. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +0 -39
  168. data/lib/shoulda/matchers/integrations/rspec.rb +0 -19
  169. data/lib/shoulda/matchers/integrations/test_unit.rb +0 -34
  170. data/spec/unit/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +0 -331
  171. data/spec/unit/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +0 -564
@@ -1,564 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher, type: :model do
4
- context 'a model without a a uniqueness validation' do
5
- it 'rejects' do
6
- model = define_model(:example, attr: :string) { attr_accessible :attr } .new
7
- Example.create!(attr: 'value')
8
- expect(model).not_to matcher
9
- end
10
- end
11
-
12
- context 'a model with a uniqueness validation' do
13
- context 'where the subject has a character limit' do
14
- it 'tests with values within the character limit' do
15
- model = define_model(:example, attr: { type: :string, options: { limit: 1 } }) do
16
- attr_accessible :attr
17
- validates_uniqueness_of :attr
18
- end.new
19
- expect(model).to matcher
20
- end
21
- end
22
-
23
- context 'with an existing record' do
24
- it 'requires a unique value for that attribute' do
25
- create_existing
26
- expect(validating_uniqueness_with_other).to matcher
27
- end
28
-
29
- it 'accepts when the subject is an existing record' do
30
- expect(create_existing).to matcher
31
- end
32
-
33
- it 'rejects when a scope is specified' do
34
- create_existing
35
- expect(validating_uniqueness_with_other).not_to matcher.scoped_to(:other)
36
- end
37
-
38
- def create_existing
39
- define_model_with_other
40
- Example.create!(attr: 'value', other: 1)
41
- end
42
- end
43
-
44
- context 'without an existing record' do
45
- it 'does not require a created instance' do
46
- define_model_with_other
47
- expect(Example.count).to eq 0
48
- expect(validating_uniqueness_with_other).to matcher
49
- end
50
-
51
- context "and the table uses non-nullable columns, set beforehand" do
52
- it "does not require the record to be persisted" do
53
- model = define_model_with_non_nullable_column
54
- record = model.new(required_attribute_name => "some value")
55
- expect(record).to validate_uniqueness_of(unique_attribute_name)
56
- end
57
-
58
- def define_model_with_non_nullable_column
59
- model = define_model(:example,
60
- unique_attribute_name => :string,
61
- required_attribute_name => {
62
- type: :string,
63
- options: { null: false }
64
- }
65
- )
66
-
67
- model.tap do
68
- model.attr_accessible(
69
- required_attribute_name,
70
- unique_attribute_name
71
- )
72
- model.validates_presence_of(required_attribute_name)
73
- model.validates_uniqueness_of(unique_attribute_name)
74
- end
75
- end
76
-
77
- def required_attribute_name
78
- :required_attribute_name
79
- end
80
-
81
- def unique_attribute_name
82
- :unique_attribute_name
83
- end
84
- end
85
- end
86
-
87
- def define_model_with_other(options = {})
88
- @model ||= define_model(:example, attr: :string, other: :integer) do
89
- attr_accessible :attr, :other
90
- validates_uniqueness_of :attr, options
91
- end
92
- end
93
-
94
- def validating_uniqueness_with_other(options = {})
95
- define_model_with_other.new
96
- end
97
- end
98
-
99
- context 'a model with a uniqueness validation, a custom error, and an existing record' do
100
- it 'rejects when the actual message does not match the default message' do
101
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
102
- not_to matcher
103
- end
104
-
105
- it 'rejects when the messages do not match' do
106
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
107
- not_to matcher.with_message(/abc/)
108
- end
109
-
110
- it 'accepts when the messages match' do
111
- expect(validating_uniqueness_with_existing_record(message: 'Bad value')).
112
- to matcher.with_message(/Bad/)
113
- end
114
-
115
- def validating_uniqueness_with_existing_record(options = {})
116
- model = define_model(:example, attr: :string) do
117
- attr_accessible :attr
118
- validates_uniqueness_of :attr, options
119
- end.new
120
- Example.create!(attr: 'value')
121
- model
122
- end
123
- end
124
-
125
- context 'a model with a scoped uniqueness validation with an existing value' do
126
- it 'accepts when the correct scope is specified' do
127
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
128
- to matcher.scoped_to(:scope1, :scope2)
129
- end
130
-
131
- it 'accepts when the subject is an existing record' do
132
- define_scoped_model([:scope1, :scope2])
133
- expect(create_existing_record).to matcher.scoped_to(:scope1, :scope2)
134
- end
135
-
136
- it 'rejects when too narrow of a scope is specified' do
137
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
138
- not_to matcher.scoped_to(:scope1, :scope2, :other)
139
- end
140
-
141
- it 'rejects when too broad of a scope is specified' do
142
- expect(validating_scoped_uniqueness([:scope1, :scope2])).
143
- not_to matcher.scoped_to(:scope1)
144
- end
145
-
146
- it 'rejects when a different scope is specified' do
147
- expect(validating_scoped_uniqueness([:scope1])).
148
- not_to matcher.scoped_to(:other)
149
- end
150
-
151
- it 'rejects when no scope is specified' do
152
- expect(validating_scoped_uniqueness([:scope1])).not_to matcher
153
- end
154
-
155
- it 'rejects when a non-existent attribute is specified as a scope' do
156
- expect(validating_scoped_uniqueness([:scope1])).
157
- not_to matcher.scoped_to(:fake)
158
- end
159
-
160
- if rails_gte_4_1?
161
- context 'when the scoped attribute is an enum' do
162
- it 'accepts' do
163
- expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: 0)).
164
- to matcher.scoped_to(:scope1)
165
- end
166
-
167
- context 'with a nil value' do
168
- it 'accepts' do
169
- expect(validating_scoped_uniqueness_with_enum([:scope1], scope1: nil)).
170
- to matcher.scoped_to(:scope1)
171
- end
172
- end
173
-
174
- context 'when too narrow of a scope is specified' do
175
- it 'rejects' do
176
- expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
177
- not_to matcher.scoped_to(:scope1, :scope2, :other)
178
- end
179
- end
180
-
181
- context 'when too broad of a scope is specified' do
182
- it 'rejects' do
183
- expect(validating_scoped_uniqueness_with_enum_with_two_scopes).
184
- not_to matcher.scoped_to(:scope1)
185
- end
186
- end
187
-
188
- def validating_scoped_uniqueness_with_enum_with_two_scopes
189
- validating_scoped_uniqueness_with_enum([:scope1, :scope2], scope1: 0, scope2: 0)
190
- end
191
- end
192
- end
193
-
194
- context 'when the scoped attribute is a date' do
195
- it "accepts" do
196
- expect(validating_scoped_uniqueness([:scope1], :date, scope1: Date.today)).
197
- to matcher.scoped_to(:scope1)
198
- end
199
-
200
- context 'with an existing record that conflicts with scope.next' do
201
- it 'accepts' do
202
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :date, scope1: Date.today)).
203
- to matcher.scoped_to(:scope1)
204
- end
205
- end
206
-
207
- context 'when too narrow of a scope is specified' do
208
- it 'rejects' do
209
- expect(validating_scoped_uniqueness([:scope1, :scope2], :date, scope1: Date.today, scope2: Date.today)).
210
- not_to matcher.scoped_to(:scope1, :scope2, :other)
211
- end
212
- end
213
-
214
- context 'when too broad of a scope is specified' do
215
- it 'rejects' do
216
- expect(validating_scoped_uniqueness([:scope1, :scope2], :date, scope1: Date.today, scope2: Date.today)).
217
- not_to matcher.scoped_to(:scope1)
218
- end
219
- end
220
- end
221
-
222
- context 'when the scoped attribute is a datetime' do
223
- it 'accepts' do
224
- expect(validating_scoped_uniqueness([:scope1], :datetime, scope1: DateTime.now)).
225
- to matcher.scoped_to(:scope1)
226
- end
227
-
228
- context 'with an existing record that conflicts with scope.next' do
229
- it 'accepts' do
230
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :datetime, scope1: DateTime.now)).
231
- to matcher.scoped_to(:scope1)
232
- end
233
- end
234
-
235
- context 'with a nil value' do
236
- it 'accepts' do
237
- expect(validating_scoped_uniqueness([:scope1], :datetime, scope1: nil)).
238
- to matcher.scoped_to(:scope1)
239
- end
240
- end
241
-
242
- context 'when too narrow of a scope is specified' do
243
- it 'rejects' do
244
- expect(validating_scoped_uniqueness([:scope1, :scope2], :datetime, scope1: DateTime.now, scope2: DateTime.now)).
245
- not_to matcher.scoped_to(:scope1, :scope2, :other)
246
- end
247
- end
248
-
249
- context 'when too broad of a scope is specified' do
250
- it 'rejects' do
251
- expect(validating_scoped_uniqueness([:scope1, :scope2], :datetime, scope1: DateTime.now, scope2: DateTime.now)).
252
- not_to matcher.scoped_to(:scope1)
253
- end
254
- end
255
- end
256
-
257
- context 'when the scoped attribute is a uuid' do
258
- it 'accepts' do
259
- expect(validating_scoped_uniqueness([:scope1], :uuid, scope1: SecureRandom.uuid)).
260
- to matcher.scoped_to(:scope1)
261
- end
262
-
263
- context 'with an existing record that conflicts with scope.next' do
264
- it 'accepts' do
265
- expect(validating_scoped_uniqueness_with_conflicting_next(:scope1, :uuid, scope1: SecureRandom.uuid)).
266
- to matcher.scoped_to(:scope1)
267
- end
268
- end
269
-
270
- context 'with a nil value' do
271
- it 'accepts' do
272
- expect(validating_scoped_uniqueness([:scope1], :uuid, scope1: nil)).
273
- to matcher.scoped_to(:scope1)
274
- end
275
- end
276
-
277
- context 'when too narrow of a scope is specified' do
278
- it 'rejects' do
279
- record = validating_scoped_uniqueness([:scope1, :scope2], :uuid,
280
- scope1: SecureRandom.uuid,
281
- scope2: SecureRandom.uuid
282
- )
283
- expect(record).not_to matcher.scoped_to(:scope1, :scope2, :other)
284
- end
285
- end
286
-
287
- context 'when too broad of a scope is specified' do
288
- it 'rejects' do
289
- record = validating_scoped_uniqueness([:scope1, :scope2], :uuid,
290
- scope1: SecureRandom.uuid,
291
- scope2: SecureRandom.uuid
292
- )
293
- expect(record).not_to matcher.scoped_to(:scope1)
294
- end
295
- end
296
- end
297
-
298
- def create_existing_record(attributes = {})
299
- @existing ||= create_record(attributes)
300
- end
301
-
302
- def create_record(attributes = {})
303
- default_attributes = {attr: 'value', scope1: 1, scope2: 2, other: 3}
304
- Example.create!(default_attributes.merge(attributes))
305
- end
306
-
307
- def define_scoped_model(scope, scope_attr_type = :integer)
308
- define_model(:example, attr: :string, scope1: scope_attr_type,
309
- scope2: scope_attr_type, other: :integer) do
310
- attr_accessible :attr, :scope1, :scope2, :other
311
- validates_uniqueness_of :attr, scope: scope
312
- end
313
- end
314
-
315
- def validating_scoped_uniqueness(*args)
316
- attributes = args.extract_options!
317
- model = define_scoped_model(*args).new
318
- create_existing_record(attributes)
319
- model
320
- end
321
-
322
- def validating_scoped_uniqueness_with_enum(*args)
323
- attributes = args.extract_options!
324
- model = define_scoped_model(*args)
325
- model.enum scope1: [:foo, :bar]
326
- create_existing_record(attributes)
327
- model.new
328
- end
329
-
330
- def validating_scoped_uniqueness_with_conflicting_next(*args)
331
- attributes = args.extract_options!
332
- model = define_scoped_model(*args).new
333
- 2.times do
334
- attributes[:scope1] = attributes[:scope1].next
335
- create_record(attributes)
336
- end
337
- model
338
- end
339
- end
340
-
341
- context 'a model with a case-sensitive uniqueness validation on a string attribute and an existing record' do
342
- it 'accepts a case-sensitive value for that attribute' do
343
- expect(case_sensitive_validation_with_existing_value(:string)).
344
- to matcher
345
- end
346
-
347
- it 'rejects a case-insensitive value for that attribute' do
348
- expect(case_sensitive_validation_with_existing_value(:string)).
349
- not_to matcher.case_insensitive
350
- end
351
- end
352
-
353
- context 'a model with a case-sensitive uniqueness validation on an integer attribute with an existing value' do
354
- it 'accepts a case-insensitive value for that attribute' do
355
- expect(case_sensitive_validation_with_existing_value(:integer)).
356
- to matcher.case_insensitive
357
- end
358
-
359
- it 'accepts a case-sensitive value for that attribute' do
360
- expect(case_sensitive_validation_with_existing_value(:integer)).to matcher
361
- end
362
- end
363
-
364
- context "when the validation allows nil" do
365
- context "when there is an existing entry with a nil" do
366
- it "should allow_nil" do
367
- model = define_model_with_allow_nil
368
- Example.create!(attr: nil)
369
- expect(model).to matcher.allow_nil
370
- end
371
- end
372
-
373
- if active_model_3_1?
374
- context 'when the subject has a secure password' do
375
- it 'allows nil on the attribute' do
376
- model = define_model(:example, attr: :string, password_digest: :string) do |m|
377
- validates_uniqueness_of :attr, allow_nil: true
378
- has_secure_password
379
- end.new
380
- expect(model).to matcher.allow_nil
381
- end
382
- end
383
- end
384
-
385
- it "should create a nil and verify that it is allowed" do
386
- model = define_model_with_allow_nil
387
- expect(model).to matcher.allow_nil
388
- Example.all.any?{ |instance| instance.attr.nil? }
389
- end
390
-
391
- def define_model_with_allow_nil
392
- define_model(:example, attr: :integer) do
393
- attr_accessible :attr
394
- validates_uniqueness_of :attr, allow_nil: true
395
- end.new
396
- end
397
- end
398
-
399
- context "when the validation does not allow a nil" do
400
- context "when there is an existing entry with a nil" do
401
- it "should not allow_nil" do
402
- model = define_model_without_allow_nil
403
- Example.create!(attr: nil)
404
- expect(model).not_to matcher.allow_nil
405
- end
406
- end
407
-
408
- it "should not allow_nil" do
409
- model = define_model_without_allow_nil
410
- expect(model).not_to matcher.allow_nil
411
- end
412
-
413
- def define_model_without_allow_nil
414
- define_model(:example, attr: :integer) do
415
- attr_accessible :attr
416
- validates_uniqueness_of :attr
417
- end.new
418
- end
419
- end
420
-
421
- context 'when the validation allows blank' do
422
- context 'when there is an existing record with a blank value' do
423
- it 'accepts' do
424
- model = model_allowing_blank
425
- model.create!(attribute_name => '')
426
- expect(model.new).to matcher.allow_blank
427
- end
428
- end
429
-
430
- context 'when there is not an an existing record with a blank value' do
431
- it 'still accepts' do
432
- expect(record_allowing_blank).to matcher.allow_blank
433
- end
434
-
435
- it 'automatically creates a record' do
436
- model = model_allowing_blank
437
- matcher.allow_blank.matches?(model.new)
438
-
439
- record_created = model.all.any? do |instance|
440
- instance.__send__(attribute_name).blank?
441
- end
442
-
443
- expect(record_created).to be true
444
- end
445
- end
446
-
447
- def attribute_name
448
- :attr
449
- end
450
-
451
- def model_allowing_blank
452
- _attribute_name = attribute_name
453
-
454
- define_model(:example, attribute_name => :string) do
455
- attr_accessible _attribute_name
456
- validates_uniqueness_of _attribute_name, allow_blank: true
457
- end
458
- end
459
-
460
- def record_allowing_blank
461
- model_allowing_blank.new
462
- end
463
- end
464
-
465
- context 'when the validation does not allow blank' do
466
- context 'when there is an existing entry with a blank value' do
467
- it 'rejects' do
468
- model = model_disallowing_blank
469
- model.create!(attribute_name => '')
470
- expect(model.new).not_to matcher.allow_blank
471
- end
472
- end
473
-
474
- it 'should not allow_blank' do
475
- expect(record_disallowing_blank).not_to matcher.allow_blank
476
- end
477
-
478
- def attribute_name
479
- :attr
480
- end
481
-
482
- def model_disallowing_blank
483
- _attribute_name = attribute_name
484
-
485
- define_model(:example, attribute_name => :string) do
486
- attr_accessible _attribute_name
487
- validates_uniqueness_of _attribute_name, allow_blank: false
488
- end
489
- end
490
-
491
- def record_disallowing_blank
492
- model_disallowing_blank.new
493
- end
494
- end
495
-
496
- context "when testing that a polymorphic *_type column is one of the validation scopes" do
497
- it "sets that column to a meaningful value that works with other validations on the same column" do
498
- user_model = define_model :user
499
- favorite_columns = {
500
- favoriteable_id: { type: :integer, options: { null: false } },
501
- favoriteable_type: { type: :string, options: { null: false } }
502
- }
503
- favorite_model = define_model :favorite, favorite_columns do
504
- attr_accessible :favoriteable
505
- belongs_to :favoriteable, polymorphic: true
506
- validates :favoriteable, presence: true
507
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
508
- end
509
-
510
- user = user_model.create!
511
- favorite_model.create!(favoriteable: user)
512
- new_favorite = favorite_model.new
513
-
514
- expect(new_favorite).
515
- to validate_uniqueness_of(:favoriteable_id).
516
- scoped_to(:favoriteable_type)
517
- end
518
-
519
- context "if the model the *_type column refers to is namespaced, and shares the last part of its name with an existing model" do
520
- it "still works" do
521
- define_class 'User'
522
- define_module 'Models'
523
- user_model = define_model 'Models::User'
524
- favorite_columns = {
525
- favoriteable_id: { type: :integer, options: { null: false } },
526
- favoriteable_type: { type: :string, options: { null: false } }
527
- }
528
- favorite_model = define_model 'Models::Favorite', favorite_columns do
529
- attr_accessible :favoriteable
530
- belongs_to :favoriteable, polymorphic: true
531
- validates :favoriteable, presence: true
532
- validates :favoriteable_id, uniqueness: { scope: :favoriteable_type }
533
- end
534
-
535
- user = user_model.create!
536
- favorite_model.create!(favoriteable: user)
537
- new_favorite = favorite_model.new
538
-
539
- expect(new_favorite).
540
- to validate_uniqueness_of(:favoriteable_id).
541
- scoped_to(:favoriteable_type)
542
- end
543
- end
544
- end
545
-
546
- def case_sensitive_validation_with_existing_value(attr_type)
547
- model = define_model(:example, attr: attr_type) do
548
- attr_accessible :attr
549
- validates_uniqueness_of :attr, case_sensitive: true
550
- end.new
551
- if attr_type == :string
552
- Example.create!(attr: 'value')
553
- elsif attr_type == :integer
554
- Example.create!(attr: 1)
555
- else
556
- raise 'Must be :string or :integer'
557
- end
558
- model
559
- end
560
-
561
- def matcher
562
- validate_uniqueness_of(:attr)
563
- end
564
- end