mcmire-shoulda-matchers 2.5.0 → 2.6.1.docs.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +2 -1
  5. data/Appraisals +62 -22
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +3 -3
  8. data/NEWS.md +87 -4
  9. data/README.md +2 -2
  10. data/Rakefile +18 -0
  11. data/features/activemodel_integration.feature +15 -0
  12. data/features/rails_integration.feature +1 -1
  13. data/features/step_definitions/activemodel_steps.rb +21 -0
  14. data/features/step_definitions/rails_steps.rb +5 -4
  15. data/gemfiles/3.0.gemfile +6 -3
  16. data/gemfiles/3.0.gemfile.lock +14 -4
  17. data/gemfiles/3.1.gemfile +10 -4
  18. data/gemfiles/3.1.gemfile.lock +32 -5
  19. data/gemfiles/3.1_1.9.2.gemfile +21 -0
  20. data/gemfiles/3.1_1.9.2.gemfile.lock +191 -0
  21. data/gemfiles/3.2.gemfile +9 -4
  22. data/gemfiles/3.2.gemfile.lock +28 -5
  23. data/gemfiles/4.0.0.gemfile +11 -3
  24. data/gemfiles/4.0.0.gemfile.lock +42 -5
  25. data/gemfiles/4.0.1.gemfile +11 -3
  26. data/gemfiles/4.0.1.gemfile.lock +42 -5
  27. data/gemfiles/4.1.gemfile +37 -0
  28. data/gemfiles/4.1.gemfile.lock +216 -0
  29. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +202 -0
  30. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +2 -1
  32. data/lib/shoulda/matchers/action_controller/strong_parameters_matcher.rb +165 -0
  33. data/lib/shoulda/matchers/action_controller.rb +2 -0
  34. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +26 -4
  35. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -0
  36. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +2 -0
  37. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +60 -18
  38. data/lib/shoulda/matchers/active_model/errors.rb +43 -1
  39. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +14 -3
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
  41. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +100 -45
  42. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +38 -5
  43. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -20
  44. data/lib/shoulda/matchers/active_record/association_matcher.rb +12 -2
  45. data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  46. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +24 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -1
  48. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  49. data/lib/shoulda/matchers/active_record.rb +1 -0
  50. data/lib/shoulda/matchers/assertion_error.rb +8 -3
  51. data/lib/shoulda/matchers/doublespeak/double.rb +75 -0
  52. data/lib/shoulda/matchers/doublespeak/double_collection.rb +55 -0
  53. data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +28 -0
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +33 -0
  55. data/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +31 -0
  56. data/lib/shoulda/matchers/doublespeak/structs.rb +10 -0
  57. data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +35 -0
  58. data/lib/shoulda/matchers/doublespeak/world.rb +39 -0
  59. data/lib/shoulda/matchers/doublespeak.rb +28 -0
  60. data/lib/shoulda/matchers/error.rb +20 -1
  61. data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +35 -0
  62. data/lib/shoulda/matchers/independent/delegate_matcher.rb +293 -0
  63. data/lib/shoulda/matchers/independent.rb +10 -0
  64. data/lib/shoulda/matchers/integrations/nunit_test_case_detection.rb +38 -0
  65. data/lib/shoulda/matchers/integrations/rspec.rb +13 -14
  66. data/lib/shoulda/matchers/integrations/test_unit.rb +19 -15
  67. data/lib/shoulda/matchers/integrations.rb +13 -0
  68. data/lib/shoulda/matchers/rails_shim.rb +16 -0
  69. data/lib/shoulda/matchers/version.rb +1 -1
  70. data/lib/shoulda/matchers.rb +15 -3
  71. data/spec/shoulda/matchers/action_controller/callback_matcher_spec.rb +82 -0
  72. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +2 -2
  73. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +5 -5
  74. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +4 -4
  75. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +38 -11
  76. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +1 -1
  77. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +1 -1
  78. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +6 -6
  79. data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +314 -0
  80. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +32 -0
  81. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +553 -211
  82. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +22 -0
  83. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +38 -0
  84. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +42 -36
  85. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +15 -1
  86. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +4 -0
  87. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +4 -0
  88. data/spec/shoulda/matchers/doublespeak/double_collection_spec.rb +102 -0
  89. data/spec/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +21 -0
  90. data/spec/shoulda/matchers/doublespeak/double_spec.rb +144 -0
  91. data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +77 -0
  92. data/spec/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +40 -0
  93. data/spec/shoulda/matchers/doublespeak/stub_implementation_spec.rb +88 -0
  94. data/spec/shoulda/matchers/doublespeak/world_spec.rb +88 -0
  95. data/spec/shoulda/matchers/doublespeak_spec.rb +19 -0
  96. data/spec/shoulda/matchers/independent/delegate_matcher/stubbed_target_spec.rb +43 -0
  97. data/spec/shoulda/matchers/independent/delegate_matcher_spec.rb +250 -0
  98. data/spec/spec_helper.rb +15 -0
  99. data/spec/support/activemodel_helpers.rb +6 -2
  100. data/spec/support/controller_builder.rb +29 -1
  101. data/spec/support/rails_versions.rb +4 -0
  102. data/spec/support/test_application.rb +1 -1
  103. metadata +59 -10
@@ -4,7 +4,9 @@ module Shoulda
4
4
  # The `validate_numericality_of` matcher tests usage of the
5
5
  # `validates_numericality_of` validation.
6
6
  #
7
- # class Person < ActiveRecord::Base
7
+ # class Person
8
+ # include ActiveModel::Model
9
+ #
8
10
  # validates_numericality_of :gpa
9
11
  # end
10
12
  #
@@ -57,7 +59,10 @@ module Shoulda
57
59
  #
58
60
  # # RSpec
59
61
  # describe Person do
60
- # it { should validate_numericality_of(:number_of_cars).is_less_than(2) }
62
+ # it do
63
+ # should validate_numericality_of(:number_of_cars).
64
+ # is_less_than(2)
65
+ # end
61
66
  # end
62
67
  #
63
68
  # # Test::Unit
@@ -80,12 +85,16 @@ module Shoulda
80
85
  #
81
86
  # # RSpec
82
87
  # describe Person do
83
- # it { should validate_numericality_of(:birth_year).is_less_than_or_equal_to(1987) }
88
+ # it do
89
+ # should validate_numericality_of(:birth_year).
90
+ # is_less_than_or_equal_to(1987)
91
+ # end
84
92
  # end
85
93
  #
86
94
  # # Test::Unit
87
95
  # class PersonTest < ActiveSupport::TestCase
88
- # should validate_numericality_of(:birth_year).is_less_than_or_equal_to(1987)
96
+ # should validate_numericality_of(:birth_year).
97
+ # is_less_than_or_equal_to(1987)
89
98
  # end
90
99
  #
91
100
  # ##### is_equal_to
@@ -125,12 +134,16 @@ module Shoulda
125
134
  #
126
135
  # # RSpec
127
136
  # describe Person do
128
- # it { should validate_numericality_of(:height).is_greater_than_or_equal_to(55) }
137
+ # it do
138
+ # should validate_numericality_of(:height).
139
+ # is_greater_than_or_equal_to(55)
140
+ # end
129
141
  # end
130
142
  #
131
143
  # # Test::Unit
132
144
  # class PersonTest < ActiveSupport::TestCase
133
- # should validate_numericality_of(:height).is_greater_than_or_equal_to(55)
145
+ # should validate_numericality_of(:height).
146
+ # is_greater_than_or_equal_to(55)
134
147
  # end
135
148
  #
136
149
  # ##### is_greater_than
@@ -147,12 +160,16 @@ module Shoulda
147
160
  #
148
161
  # # RSpec
149
162
  # describe Person do
150
- # it { should validate_numericality_of(:legal_age).is_greater_than(21) }
163
+ # it do
164
+ # should validate_numericality_of(:legal_age).
165
+ # is_greater_than(21)
166
+ # end
151
167
  # end
152
168
  #
153
169
  # # Test::Unit
154
170
  # class PersonTest < ActiveSupport::TestCase
155
- # should validate_numericality_of(:legal_age).is_greater_than(21)
171
+ # should validate_numericality_of(:legal_age).
172
+ # is_greater_than(21)
156
173
  # end
157
174
  #
158
175
  # ##### even
@@ -198,49 +215,49 @@ module Shoulda
198
215
  # should validate_numericality_of(:birth_day).odd
199
216
  # end
200
217
  #
201
- # ##### allow_nil
218
+ # ##### with_message
202
219
  #
203
- # Use `allow_nil` to assert that the attribute allows nil.
220
+ # Use `with_message` if you are using a custom validation message.
204
221
  #
205
222
  # class Person
206
223
  # include ActiveModel::Model
207
224
  #
208
- # validate_numericality_of :rank, allow_nil: true
225
+ # validates_numericality_of :number_of_dependents,
226
+ # message: 'Number of dependents must be a number'
209
227
  # end
210
228
  #
211
229
  # # RSpec
212
230
  # describe Person do
213
- # it { should validate_numericality_of(:rank).allow_nil }
231
+ # it do
232
+ # should validate_numericality_of(:number_of_dependents).
233
+ # with_message('Number of dependents must be a number')
234
+ # end
214
235
  # end
215
236
  #
216
237
  # # Test::Unit
217
238
  # class PersonTest < ActiveSupport::TestCase
218
- # should validate_uniqueness_of(:rank).allow_nil
239
+ # should validate_numericality_of(:number_of_dependents).
240
+ # with_message('Number of dependents must be a number')
219
241
  # end
220
242
  #
221
- # ##### with_message
243
+ # ##### allow_nil
222
244
  #
223
- # Use `with_message` if you are using a custom validation message.
245
+ # Use `allow_nil` to assert that the attribute allows nil.
224
246
  #
225
- # class Person
247
+ # class Post
226
248
  # include ActiveModel::Model
227
249
  #
228
- # validates_numericality_of :number_of_dependents,
229
- # message: 'Number of dependents must be a number'
250
+ # validates_uniqueness_of :author_id, allow_nil: true
230
251
  # end
231
252
  #
232
253
  # # RSpec
233
- # describe Person do
234
- # it do
235
- # should validate_numericality_of(:number_of_dependents).
236
- # with_message('Number of dependents must be a number')
237
- # end
254
+ # describe Post do
255
+ # it { should validate_uniqueness_of(:author_id).allow_nil }
238
256
  # end
239
257
  #
240
258
  # # Test::Unit
241
- # class PersonTest < ActiveSupport::TestCase
242
- # should validate_numericality_of(:number_of_dependents).
243
- # with_message('Number of dependents must be a number')
259
+ # class PostTest < ActiveSupport::TestCase
260
+ # should validate_uniqueness_of(:author_id).allow_nil
244
261
  # end
245
262
  #
246
263
  # @return [ValidateNumericalityOfMatcher]
@@ -253,53 +270,69 @@ module Shoulda
253
270
  class ValidateNumericalityOfMatcher
254
271
  NUMERIC_NAME = 'numbers'
255
272
  NON_NUMERIC_VALUE = 'abcd'
273
+ DEFAULT_DIFF_TO_COMPARE = 1
274
+
275
+ attr_reader :diff_to_compare
256
276
 
257
277
  def initialize(attribute)
258
278
  @attribute = attribute
259
279
  @submatchers = []
260
-
280
+ @diff_to_compare = DEFAULT_DIFF_TO_COMPARE
261
281
  add_disallow_value_matcher
262
282
  end
263
283
 
264
284
  def only_integer
265
- add_submatcher(NumericalityMatchers::OnlyIntegerMatcher.new(@attribute))
285
+ prepare_submatcher(
286
+ NumericalityMatchers::OnlyIntegerMatcher.new(@attribute)
287
+ )
266
288
  self
267
289
  end
268
290
 
269
- def is_greater_than(value)
270
- add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>).for(@attribute))
291
+ def allow_nil
292
+ prepare_submatcher(
293
+ AllowValueMatcher.new(nil)
294
+ .for(@attribute)
295
+ .with_message(:not_a_number)
296
+ )
271
297
  self
272
298
  end
273
299
 
274
- def is_greater_than_or_equal_to(value)
275
- add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>=).for(@attribute))
300
+ def odd
301
+ prepare_submatcher(
302
+ NumericalityMatchers::OddNumberMatcher.new(@attribute)
303
+ )
276
304
  self
277
305
  end
278
306
 
279
- def is_equal_to(value)
280
- add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :==).for(@attribute))
307
+ def even
308
+ prepare_submatcher(
309
+ NumericalityMatchers::EvenNumberMatcher.new(@attribute)
310
+ )
281
311
  self
282
312
  end
283
313
 
284
- def is_less_than(value)
285
- add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<).for(@attribute))
314
+ def is_greater_than(value)
315
+ prepare_submatcher(comparison_matcher_for(value, :>).for(@attribute))
286
316
  self
287
317
  end
288
318
 
289
- def is_less_than_or_equal_to(value)
290
- add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<=).for(@attribute))
319
+ def is_greater_than_or_equal_to(value)
320
+ prepare_submatcher(comparison_matcher_for(value, :>=).for(@attribute))
291
321
  self
292
322
  end
293
323
 
294
- def odd
295
- odd_number_matcher = NumericalityMatchers::OddNumberMatcher.new(@attribute)
296
- add_submatcher(odd_number_matcher)
324
+ def is_equal_to(value)
325
+ prepare_submatcher(comparison_matcher_for(value, :==).for(@attribute))
297
326
  self
298
327
  end
299
328
 
300
- def even
301
- even_number_matcher = NumericalityMatchers::EvenNumberMatcher.new(@attribute)
302
- add_submatcher(even_number_matcher)
329
+ def is_less_than(value)
330
+ prepare_submatcher(comparison_matcher_for(value, :<).for(@attribute))
331
+ self
332
+ end
333
+
334
+ def is_less_than_or_equal_to(value)
335
+ prepare_submatcher(comparison_matcher_for(value, :<=).for(@attribute))
303
336
  self
304
337
  end
305
338
 
@@ -337,10 +370,27 @@ module Shoulda
337
370
  add_submatcher(disallow_value_matcher)
338
371
  end
339
372
 
373
+ def prepare_submatcher(submatcher)
374
+ add_submatcher(submatcher)
375
+ if submatcher.respond_to?(:diff_to_compare)
376
+ update_diff_to_compare(submatcher)
377
+ end
378
+ end
379
+
380
+ def comparison_matcher_for(value, operator)
381
+ NumericalityMatchers::ComparisonMatcher
382
+ .new(self, value, operator)
383
+ .for(@attribute)
384
+ end
385
+
340
386
  def add_submatcher(submatcher)
341
387
  @submatchers << submatcher
342
388
  end
343
389
 
390
+ def update_diff_to_compare(matcher)
391
+ @diff_to_compare = [@diff_to_compare, matcher.diff_to_compare].max
392
+ end
393
+
344
394
  def submatchers_match?
345
395
  failing_submatchers.empty?
346
396
  end
@@ -372,7 +422,12 @@ module Shoulda
372
422
  end
373
423
 
374
424
  def submatcher_comparison_descriptions
375
- @submatchers.inject([]){|m, s| m << s.comparison_description if s.respond_to?(:comparison_description); m }
425
+ @submatchers.inject([]) do |arr, submatcher|
426
+ if submatcher.respond_to? :comparison_description
427
+ arr << submatcher.comparison_description
428
+ end
429
+ arr
430
+ end
376
431
  end
377
432
  end
378
433
  end
@@ -4,7 +4,9 @@ module Shoulda
4
4
  # The `validate_presence_of` matcher tests usage of the
5
5
  # `validates_presence_of` validation.
6
6
  #
7
- # class Robot < ActiveRecord::Base
7
+ # class Robot
8
+ # include ActiveModel::Model
9
+ #
8
10
  # validates_presence_of :arms
9
11
  # end
10
12
  #
@@ -24,18 +26,24 @@ module Shoulda
24
26
  #
25
27
  # Use `with_message` if you are using a custom validation message.
26
28
  #
27
- # class Robot < ActiveRecord::Base
29
+ # class Robot
30
+ # include ActiveModel::Model
31
+ #
28
32
  # validates_presence_of :legs, message: 'Robot has no legs'
29
33
  # end
30
34
  #
31
35
  # # RSpec
32
36
  # describe Robot do
33
- # it { should validate_presence_of(:legs).with_message('Robot has no legs') }
37
+ # it do
38
+ # should validate_presence_of(:legs).
39
+ # with_message('Robot has no legs')
40
+ # end
34
41
  # end
35
42
  #
36
43
  # # Test::Unit
37
44
  # class RobotTest < ActiveSupport::TestCase
38
- # should validate_presence_of(:legs).with_message('Robot has no legs')
45
+ # should validate_presence_of(:legs).
46
+ # with_message('Robot has no legs')
39
47
  # end
40
48
  #
41
49
  # @return [ValidatePresenceOfMatcher]
@@ -54,7 +62,12 @@ module Shoulda
54
62
  def matches?(subject)
55
63
  super(subject)
56
64
  @expected_message ||= :blank
57
- disallows_value_of(blank_value, @expected_message)
65
+
66
+ if secure_password_being_validated?
67
+ disallows_and_double_checks_value_of!(blank_value, @expected_message)
68
+ else
69
+ disallows_value_of(blank_value, @expected_message)
70
+ end
58
71
  end
59
72
 
60
73
  def description
@@ -63,6 +76,26 @@ module Shoulda
63
76
 
64
77
  private
65
78
 
79
+ def secure_password_being_validated?
80
+ defined?(::ActiveModel::SecurePassword) &&
81
+ @subject.class.ancestors.include?(::ActiveModel::SecurePassword::InstanceMethodsOnActivation) &&
82
+ @attribute == :password
83
+ end
84
+
85
+ def disallows_and_double_checks_value_of!(value, message)
86
+ error_class = Shoulda::Matchers::ActiveModel::CouldNotSetPasswordError
87
+
88
+ disallows_value_of(value, message) do |matcher|
89
+ matcher._after_setting_value do
90
+ actual_value = @subject.__send__(@attribute)
91
+
92
+ if !actual_value.nil?
93
+ raise error_class.create(@subject.class)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
66
99
  def blank_value
67
100
  if collection?
68
101
  []
@@ -9,7 +9,9 @@ module Shoulda
9
9
  # values which are the same as those of the pre-existing record (thereby
10
10
  # failing the uniqueness check).
11
11
  #
12
- # class Post < ActiveRecord::Base
12
+ # class Post
13
+ # include ActiveModel::Model
14
+ #
13
15
  # validates_uniqueness_of :permalink
14
16
  # end
15
17
  #
@@ -43,7 +45,9 @@ module Shoulda
43
45
  # end
44
46
  # end
45
47
  #
46
- # class Post < ActiveRecord::Base
48
+ # class Post
49
+ # include ActiveModel::Model
50
+ #
47
51
  # validates :title, uniqueness: true
48
52
  # end
49
53
  #
@@ -88,8 +92,11 @@ module Shoulda
88
92
  #
89
93
  # Use `with_message` if you are using a custom validation message.
90
94
  #
91
- # class Post < ActiveRecord::Base
92
- # validates_uniqueness_of :title, message: 'Please choose another title'
95
+ # class Post
96
+ # include ActiveModel::Model
97
+ #
98
+ # validates_uniqueness_of :title,
99
+ # message: 'Please choose another title'
93
100
  # end
94
101
  #
95
102
  # # RSpec
@@ -112,7 +119,9 @@ module Shoulda
112
119
  # a new record fails validation if not only the primary attribute is not
113
120
  # unique, but the scoped attributes are not unique either.
114
121
  #
115
- # class Post < ActiveRecord::Base
122
+ # class Post
123
+ # include ActiveModel::Model
124
+ #
116
125
  # validates_uniqueness_of :slug, scope: :user_id
117
126
  # end
118
127
  #
@@ -133,7 +142,9 @@ module Shoulda
133
142
  # validation even if their values are a different case than corresponding
134
143
  # attributes in the pre-existing record.
135
144
  #
136
- # class Post < ActiveRecord::Base
145
+ # class Post
146
+ # include ActiveModel::Model
147
+ #
137
148
  # validates_uniqueness_of :key, case_sensitive: false
138
149
  # end
139
150
  #
@@ -151,7 +162,9 @@ module Shoulda
151
162
  #
152
163
  # Use `allow_nil` to assert that the attribute allows nil.
153
164
  #
154
- # class Post < ActiveRecord::Base
165
+ # class Post
166
+ # include ActiveModel::Model
167
+ #
155
168
  # validates_uniqueness_of :author_id, allow_nil: true
156
169
  # end
157
170
  #
@@ -255,11 +268,6 @@ module Shoulda
255
268
 
256
269
  @subject.class.new.tap do |instance|
257
270
  instance.__send__("#{@attribute}=", value)
258
-
259
- other_non_nullable_columns.each do |non_nullable_column|
260
- instance.__send__("#{non_nullable_column.name}=", correct_type_for_column(non_nullable_column))
261
- end
262
-
263
271
  if has_secure_password?
264
272
  instance.password = 'password'
265
273
  instance.password_confirmation = 'password'
@@ -313,7 +321,12 @@ module Shoulda
313
321
  previous_value ||= correct_type_for_column(@subject.class.columns_hash[scope.to_s])
314
322
 
315
323
  next_value =
316
- if previous_value.respond_to?(:next)
324
+ if @subject.class.respond_to?(:defined_enums) && @subject.defined_enums[scope.to_s]
325
+ available_values = @subject.defined_enums[scope.to_s].reject do |key, _|
326
+ key == previous_value
327
+ end
328
+ available_values.keys.last
329
+ elsif previous_value.respond_to?(:next)
317
330
  previous_value.next
318
331
  elsif previous_value.respond_to?(:to_datetime)
319
332
  previous_value.to_datetime.next
@@ -338,7 +351,7 @@ module Shoulda
338
351
  end
339
352
 
340
353
  def correct_type_for_column(column)
341
- if column.type == :string || column.type == :binary
354
+ if column.type == :string
342
355
  '0'
343
356
  elsif column.type == :datetime
344
357
  DateTime.now
@@ -360,12 +373,6 @@ module Shoulda
360
373
  end
361
374
  value
362
375
  end
363
-
364
- def other_non_nullable_columns
365
- @subject.class.columns.select do |column|
366
- column.name != @attribute && !column.null && !column.primary
367
- end
368
- end
369
376
  end
370
377
  end
371
378
  end
@@ -610,7 +610,7 @@ module Shoulda
610
610
  # @private
611
611
  class AssociationMatcher
612
612
  delegate :reflection, :model_class, :associated_class, :through?,
613
- :join_table, to: :reflector
613
+ :join_table, :polymorphic?, to: :reflector
614
614
 
615
615
  def initialize(macro, name)
616
616
  @macro = macro
@@ -644,6 +644,13 @@ module Shoulda
644
644
  self
645
645
  end
646
646
 
647
+ def inverse_of(inverse_of)
648
+ inverse_of_matcher =
649
+ AssociationMatchers::InverseOfMatcher.new(inverse_of, name)
650
+ add_submatcher(inverse_of_matcher)
651
+ self
652
+ end
653
+
647
654
  def source(source)
648
655
  source_matcher = AssociationMatchers::SourceMatcher.new(source, name)
649
656
  add_submatcher(source_matcher)
@@ -700,7 +707,7 @@ module Shoulda
700
707
  @subject = subject
701
708
  association_exists? &&
702
709
  macro_correct? &&
703
- class_exists? &&
710
+ (polymorphic? || class_exists?) &&
704
711
  foreign_key_exists? &&
705
712
  class_name_correct? &&
706
713
  autosave_correct? &&
@@ -766,6 +773,9 @@ module Shoulda
766
773
  def macro_correct?
767
774
  if reflection.macro == macro
768
775
  true
776
+ elsif reflection.macro == :has_many
777
+ macro == :has_and_belongs_to_many &&
778
+ reflection.name == @name
769
779
  else
770
780
  @missing = "actual association type was #{reflection.macro}"
771
781
  false
@@ -0,0 +1,41 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ module AssociationMatchers
5
+ # @private
6
+ class InverseOfMatcher
7
+ attr_accessor :missing_option
8
+
9
+ def initialize(inverse_of, name)
10
+ @inverse_of = inverse_of
11
+ @name = name
12
+ @missing_option = ''
13
+ end
14
+
15
+ def description
16
+ "inverse_of => #{inverse_of}"
17
+ end
18
+
19
+ def matches?(subject)
20
+ self.subject = ModelReflector.new(subject, name)
21
+
22
+ if option_verifier.correct_for_string?(:inverse_of, inverse_of)
23
+ true
24
+ else
25
+ self.missing_option = "#{name} should have #{description}"
26
+ false
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_accessor :subject, :inverse_of, :name
33
+
34
+ def option_verifier
35
+ @option_verifier ||= OptionVerifier.new(subject)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -16,13 +16,19 @@ module Shoulda
16
16
  reflection.klass
17
17
  end
18
18
 
19
+ def polymorphic?
20
+ reflection.options[:polymorphic]
21
+ end
22
+
19
23
  def through?
20
24
  reflection.options[:through]
21
25
  end
22
26
 
23
27
  def join_table
24
28
  join_table =
25
- if reflection.respond_to?(:join_table)
29
+ if has_and_belongs_to_many_name_table_name
30
+ has_and_belongs_to_many_name_table_name
31
+ elsif reflection.respond_to?(:join_table)
26
32
  reflection.join_table
27
33
  else
28
34
  reflection.options[:join_table]
@@ -74,6 +80,23 @@ module Shoulda
74
80
  relation
75
81
  end
76
82
  end
83
+
84
+ def has_and_belongs_to_many_name
85
+ reflection.options[:through]
86
+ end
87
+
88
+ def has_and_belongs_to_many_name_table_name
89
+ if has_and_belongs_to_many_reflection
90
+ has_and_belongs_to_many_reflection.table_name
91
+ end
92
+ end
93
+
94
+ def has_and_belongs_to_many_reflection
95
+ @_has_and_belongs_to_many_reflection ||=
96
+ if has_and_belongs_to_many_name
97
+ @subject.reflect_on_association(has_and_belongs_to_many_name)
98
+ end
99
+ end
77
100
  end
78
101
  end
79
102
  end
@@ -5,7 +5,7 @@ module Shoulda
5
5
  # @private
6
6
  class ModelReflector
7
7
  delegate :associated_class, :through?, :join_table,
8
- :association_relation, to: :reflection
8
+ :association_relation, :polymorphic?, to: :reflection
9
9
 
10
10
  def initialize(subject, name)
11
11
  @subject = subject
@@ -63,7 +63,7 @@ module Shoulda
63
63
  @options = {}
64
64
  end
65
65
 
66
- def unique(unique)
66
+ def unique(unique = true)
67
67
  @options[:unique] = unique
68
68
  self
69
69
  end
@@ -1,6 +1,7 @@
1
1
  require 'shoulda/matchers/active_record/association_matcher'
2
2
  require 'shoulda/matchers/active_record/association_matchers'
3
3
  require 'shoulda/matchers/active_record/association_matchers/counter_cache_matcher'
4
+ require 'shoulda/matchers/active_record/association_matchers/inverse_of_matcher'
4
5
  require 'shoulda/matchers/active_record/association_matchers/order_matcher'
5
6
  require 'shoulda/matchers/active_record/association_matchers/through_matcher'
6
7
  require 'shoulda/matchers/active_record/association_matchers/dependent_matcher'
@@ -9,9 +9,14 @@ module Shoulda
9
9
  # @private
10
10
  AssertionError = Test::Unit::AssertionFailedError
11
11
  elsif Gem.ruby_version >= Gem::Version.new("1.9")
12
- require 'minitest/unit'
13
- # @private
14
- AssertionError = MiniTest::Assertion
12
+ begin
13
+ require 'minitest'
14
+ rescue LoadError
15
+ require 'minitest/unit'
16
+ ensure
17
+ # @private
18
+ AssertionError = MiniTest::Assertion
19
+ end
15
20
  else
16
21
  raise 'No unit test library available'
17
22
  end