shoulda-matchers 2.3.0 → 2.4.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/Appraisals +12 -0
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +6 -11
  5. data/NEWS.md +6 -0
  6. data/features/rails_integration.feature +2 -3
  7. data/features/step_definitions/rails_steps.rb +40 -8
  8. data/gemfiles/3.0.gemfile +2 -1
  9. data/gemfiles/3.0.gemfile.lock +6 -6
  10. data/gemfiles/3.1.gemfile +2 -1
  11. data/gemfiles/3.1.gemfile.lock +6 -6
  12. data/gemfiles/3.2.gemfile +2 -1
  13. data/gemfiles/3.2.gemfile.lock +6 -6
  14. data/gemfiles/4.0.gemfile +17 -0
  15. data/gemfiles/4.0.gemfile.lock +152 -0
  16. data/lib/shoulda/matchers.rb +1 -0
  17. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
  18. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +24 -10
  19. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +13 -7
  20. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +2 -2
  21. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +18 -6
  22. data/lib/shoulda/matchers/active_model/validation_matcher.rb +4 -2
  23. data/lib/shoulda/matchers/active_record.rb +1 -0
  24. data/lib/shoulda/matchers/active_record/association_matcher.rb +18 -37
  25. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +8 -3
  26. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +8 -3
  27. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +66 -4
  28. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +93 -0
  29. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +8 -3
  30. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -2
  31. data/lib/shoulda/matchers/integrations/test_unit.rb +10 -22
  32. data/lib/shoulda/matchers/rails_shim.rb +41 -0
  33. data/lib/shoulda/matchers/version.rb +1 -1
  34. data/shoulda-matchers.gemspec +1 -2
  35. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +7 -2
  36. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +2 -2
  37. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
  38. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +24 -0
  39. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +24 -0
  40. data/spec/shoulda/matchers/active_model/helpers_spec.rb +5 -7
  41. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +46 -12
  42. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +1 -1
  43. data/spec/support/active_model_versions.rb +6 -6
  44. metadata +26 -55
@@ -1,5 +1,6 @@
1
1
  require 'shoulda/matchers/version'
2
2
  require 'shoulda/matchers/assertion_error'
3
+ require 'shoulda/matchers/rails_shim'
3
4
 
4
5
  if defined?(RSpec)
5
6
  require 'shoulda/matchers/integrations/rspec'
@@ -71,7 +71,7 @@ module Shoulda # :nodoc:
71
71
 
72
72
  def recorded_layouts
73
73
  if @context
74
- @context.instance_variable_get('@layouts')
74
+ @context.instance_variable_get(Shoulda::Matchers::RailsShim.layouts_ivar)
75
75
  else
76
76
  {}
77
77
  end
@@ -90,20 +90,34 @@ module Shoulda # :nodoc:
90
90
  end
91
91
 
92
92
  def flash
93
- if @flash
94
- @flash
95
- else
96
- @flash = @controller.flash.dup
97
- used = @controller.flash.instance_variable_get(:@used).dup
98
- @flash.instance_variable_set(:@used, used)
99
- sweep_flash_if_necessary
100
- @flash
93
+ @flash ||= copy_of_flash_from_controller
94
+ end
95
+
96
+ def copy_of_flash_from_controller
97
+ @controller.flash.dup.tap do |flash|
98
+ copy_flashes(@controller.flash, flash)
99
+ copy_discard_if_necessary(@controller.flash, flash)
100
+ sweep_flash_if_necessary(flash)
101
+ end
102
+ end
103
+
104
+ def copy_flashes(original_flash, new_flash)
105
+ flashes_ivar = Shoulda::Matchers::RailsShim.flashes_ivar
106
+ flashes = original_flash.instance_variable_get(flashes_ivar).dup
107
+ new_flash.instance_variable_set(flashes_ivar, flashes)
108
+ end
109
+
110
+ def copy_discard_if_necessary(original_flash, new_flash)
111
+ discard_ivar = :@discard
112
+ if original_flash.instance_variable_defined?(discard_ivar)
113
+ discard = original_flash.instance_variable_get(discard_ivar).dup
114
+ new_flash.instance_variable_set(discard_ivar, discard)
101
115
  end
102
116
  end
103
117
 
104
- def sweep_flash_if_necessary
118
+ def sweep_flash_if_necessary(flash)
105
119
  unless @options[:now]
106
- @flash.sweep
120
+ flash.sweep
107
121
  end
108
122
  end
109
123
 
@@ -30,6 +30,7 @@ module Shoulda # :nodoc:
30
30
  class AllowValueMatcher # :nodoc:
31
31
  include Helpers
32
32
 
33
+ attr_accessor :attribute_with_message
33
34
  attr_accessor :options
34
35
 
35
36
  def initialize(*values)
@@ -39,7 +40,8 @@ module Shoulda # :nodoc:
39
40
  end
40
41
 
41
42
  def for(attribute)
42
- self.attribute = attribute
43
+ self.attribute_to_set = attribute
44
+ self.attribute_to_check_message_against = attribute
43
45
  self
44
46
  end
45
47
 
@@ -48,8 +50,11 @@ module Shoulda # :nodoc:
48
50
  self
49
51
  end
50
52
 
51
- def with_message(message)
53
+ def with_message(message, options={})
52
54
  self.options[:expected_message] = message
55
+ if options.key?(:against)
56
+ self.attribute_to_check_message_against = options[:against]
57
+ end
53
58
  self
54
59
  end
55
60
 
@@ -63,7 +68,7 @@ module Shoulda # :nodoc:
63
68
 
64
69
  values_to_match.none? do |value|
65
70
  self.value = value
66
- instance.send("#{attribute}=", value)
71
+ instance.send("#{attribute_to_set}=", value)
67
72
  errors_match?
68
73
  end
69
74
  end
@@ -83,7 +88,8 @@ module Shoulda # :nodoc:
83
88
  private
84
89
 
85
90
  attr_accessor :values_to_match, :message_finder_factory,
86
- :instance, :attribute, :context, :value, :matched_error
91
+ :instance, :attribute_to_set, :attribute_to_check_message_against,
92
+ :context, :value, :matched_error
87
93
 
88
94
  def errors_match?
89
95
  has_messages? && errors_for_attribute_match?
@@ -119,7 +125,7 @@ module Shoulda # :nodoc:
119
125
 
120
126
  def expectation
121
127
  includes_expected_message = expected_message ? "to include #{expected_message.inspect}" : ''
122
- [error_source, includes_expected_message, "when #{attribute} is set to #{value.inspect}"].join(' ')
128
+ [error_source, includes_expected_message, "when #{attribute_to_set} is set to #{value.inspect}"].join(' ')
123
129
  end
124
130
 
125
131
  def error_source
@@ -157,7 +163,7 @@ module Shoulda # :nodoc:
157
163
  options[:expected_message],
158
164
  :model_name => model_name,
159
165
  :instance => instance,
160
- :attribute => attribute
166
+ :attribute => attribute_to_set
161
167
  )
162
168
  end
163
169
 
@@ -166,7 +172,7 @@ module Shoulda # :nodoc:
166
172
  end
167
173
 
168
174
  def message_finder
169
- message_finder_factory.new(instance, attribute, context)
175
+ message_finder_factory.new(instance, attribute_to_check_message_against, context)
170
176
  end
171
177
  end
172
178
  end
@@ -20,8 +20,8 @@ module Shoulda # :nodoc:
20
20
  self
21
21
  end
22
22
 
23
- def with_message(message)
24
- @allow_matcher.with_message(message)
23
+ def with_message(message, options={})
24
+ @allow_matcher.with_message(message, options)
25
25
  self
26
26
  end
27
27
 
@@ -13,9 +13,11 @@ module Shoulda # :nodoc:
13
13
  class ValidateConfirmationOfMatcher < ValidationMatcher # :nodoc:
14
14
  include Helpers
15
15
 
16
+ attr_reader :attribute, :confirmation_attribute
17
+
16
18
  def initialize(attribute)
17
19
  @attribute = attribute
18
- @confirmation = "#{attribute}_confirmation"
20
+ @confirmation_attribute = "#{attribute}_confirmation"
19
21
  end
20
22
 
21
23
  def with_message(message)
@@ -24,7 +26,7 @@ module Shoulda # :nodoc:
24
26
  end
25
27
 
26
28
  def description
27
- "require #{@confirmation} to match #{@attribute}"
29
+ "require #{@confirmation_attribute} to match #{@attribute}"
28
30
  end
29
31
 
30
32
  def matches?(subject)
@@ -40,25 +42,35 @@ module Shoulda # :nodoc:
40
42
 
41
43
  def disallows_different_value
42
44
  set_confirmation('some value')
43
- disallows_value_of('different value', @message)
45
+ disallows_value_of('different value') do |matcher|
46
+ matcher.with_message(@message, against: error_attribute)
47
+ end
44
48
  end
45
49
 
46
50
  def allows_same_value
47
51
  set_confirmation('same value')
48
- allows_value_of('same value', @message)
52
+ allows_value_of('same value') do |matcher|
53
+ matcher.with_message(@message, against: error_attribute)
54
+ end
49
55
  end
50
56
 
51
57
  def allows_missing_confirmation
52
58
  set_confirmation(nil)
53
- allows_value_of('any value', @message)
59
+ allows_value_of('any value') do |matcher|
60
+ matcher.with_message(@message, against: error_attribute)
61
+ end
54
62
  end
55
63
 
56
64
  def set_confirmation(val)
57
- setter = :"#{@confirmation}="
65
+ setter = :"#{@confirmation_attribute}="
58
66
  if @subject.respond_to?(setter)
59
67
  @subject.send(setter, val)
60
68
  end
61
69
  end
70
+
71
+ def error_attribute
72
+ RailsShim.validates_confirmation_of_error_attribute(self)
73
+ end
62
74
  end
63
75
  end
64
76
  end
@@ -30,8 +30,9 @@ module Shoulda # :nodoc:
30
30
 
31
31
  private
32
32
 
33
- def allows_value_of(value, message = nil)
33
+ def allows_value_of(value, message = nil, &block)
34
34
  allow = allow_value_matcher(value, message)
35
+ yield allow if block_given?
35
36
 
36
37
  if allow.matches?(@subject)
37
38
  @failure_message_for_should_not = allow.failure_message_for_should_not
@@ -42,8 +43,9 @@ module Shoulda # :nodoc:
42
43
  end
43
44
  end
44
45
 
45
- def disallows_value_of(value, message = nil)
46
+ def disallows_value_of(value, message = nil, &block)
46
47
  disallow = disallow_value_matcher(value, message)
48
+ yield disallow if block_given?
47
49
 
48
50
  if disallow.matches?(@subject)
49
51
  @failure_message_for_should_not = disallow.failure_message_for_should_not
@@ -4,6 +4,7 @@ require 'shoulda/matchers/active_record/association_matchers/order_matcher'
4
4
  require 'shoulda/matchers/active_record/association_matchers/through_matcher'
5
5
  require 'shoulda/matchers/active_record/association_matchers/dependent_matcher'
6
6
  require 'shoulda/matchers/active_record/association_matchers/model_reflector'
7
+ require 'shoulda/matchers/active_record/association_matchers/option_verifier'
7
8
  require 'shoulda/matchers/active_record/have_db_column_matcher'
8
9
  require 'shoulda/matchers/active_record/have_db_index_matcher'
9
10
  require 'shoulda/matchers/active_record/have_readonly_attribute_matcher'
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module Shoulda # :nodoc:
2
4
  module Matchers
3
5
  module ActiveRecord # :nodoc:
@@ -72,6 +74,9 @@ module Shoulda # :nodoc:
72
74
  end
73
75
 
74
76
  class AssociationMatcher # :nodoc:
77
+ delegate :reflection, :model_class, :associated_class, :through?,
78
+ :join_table, to: :reflector
79
+
75
80
  def initialize(macro, name)
76
81
  @macro = macro
77
82
  @name = name
@@ -160,6 +165,14 @@ module Shoulda # :nodoc:
160
165
 
161
166
  attr_reader :submatchers, :missing, :subject, :macro, :name, :options
162
167
 
168
+ def reflector
169
+ @reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
170
+ end
171
+
172
+ def option_verifier
173
+ @option_verifier ||= AssociationMatchers::OptionVerifier.new(reflector)
174
+ end
175
+
163
176
  def add_submatcher(matcher)
164
177
  @submatchers << matcher
165
178
  end
@@ -200,10 +213,6 @@ module Shoulda # :nodoc:
200
213
  end
201
214
  end
202
215
 
203
- def reflection
204
- @reflection ||= model_class.reflect_on_association(name)
205
- end
206
-
207
216
  def macro_correct?
208
217
  if reflection.macro == macro
209
218
  true
@@ -221,27 +230,15 @@ module Shoulda # :nodoc:
221
230
  macro == :belongs_to && !class_has_foreign_key?(model_class)
222
231
  end
223
232
 
224
- def model_class
225
- subject.class
226
- end
227
-
228
233
  def has_foreign_key_missing?
229
234
  [:has_many, :has_one].include?(macro) &&
230
235
  !through? &&
231
236
  !class_has_foreign_key?(associated_class)
232
237
  end
233
238
 
234
- def through?
235
- reflection.options[:through]
236
- end
237
-
238
- def associated_class
239
- reflection.klass
240
- end
241
-
242
239
  def class_name_correct?
243
240
  if options.key?(:class_name)
244
- if options[:class_name].to_s == reflection.klass.to_s
241
+ if option_verifier.correct_for_string?(:class_name, options[:class_name])
245
242
  true
246
243
  else
247
244
  @missing = "#{name} should resolve to #{options[:class_name]} for class_name"
@@ -254,7 +251,7 @@ module Shoulda # :nodoc:
254
251
 
255
252
  def conditions_correct?
256
253
  if options.key?(:conditions)
257
- if options[:conditions].to_s == reflection.options[:conditions].to_s
254
+ if option_verifier.correct_for_relation_clause?(:conditions, options[:conditions])
258
255
  true
259
256
  else
260
257
  @missing = "#{name} should have the following conditions: #{options[:conditions]}"
@@ -276,7 +273,7 @@ module Shoulda # :nodoc:
276
273
  end
277
274
 
278
275
  def validate_correct?
279
- if option_correct?(:validate)
276
+ if option_verifier.correct_for_boolean?(:validate, options[:validate])
280
277
  true
281
278
  else
282
279
  @missing = "#{name} should have :validate => #{options[:validate]}"
@@ -285,7 +282,7 @@ module Shoulda # :nodoc:
285
282
  end
286
283
 
287
284
  def touch_correct?
288
- if option_correct?(:touch)
285
+ if option_verifier.correct_for_boolean?(:touch, options[:touch])
289
286
  true
290
287
  else
291
288
  @missing = "#{name} should have :touch => #{options[:touch]}"
@@ -293,17 +290,9 @@ module Shoulda # :nodoc:
293
290
  end
294
291
  end
295
292
 
296
- def option_correct?(key)
297
- !options.key?(key) || reflection_set_properly_for?(key)
298
- end
299
-
300
- def reflection_set_properly_for?(key)
301
- options[key] == !!reflection.options[key]
302
- end
303
-
304
293
  def class_has_foreign_key?(klass)
305
294
  if options.key?(:foreign_key)
306
- reflection.options[:foreign_key] == options[:foreign_key]
295
+ option_verifier.correct_for_string?(:foreign_key, options[:foreign_key])
307
296
  else
308
297
  if klass.column_names.include?(foreign_key)
309
298
  true
@@ -314,14 +303,6 @@ module Shoulda # :nodoc:
314
303
  end
315
304
  end
316
305
 
317
- def join_table
318
- if reflection.respond_to? :join_table
319
- reflection.join_table.to_s
320
- else
321
- reflection.options[:join_table].to_s
322
- end
323
- end
324
-
325
306
  def foreign_key
326
307
  if foreign_key_reflection
327
308
  if foreign_key_reflection.respond_to?(:foreign_key)
@@ -16,9 +16,9 @@ module Shoulda # :nodoc:
16
16
  end
17
17
 
18
18
  def matches?(subject)
19
- subject = ModelReflector.new(subject, name)
19
+ self.subject = ModelReflector.new(subject, name)
20
20
 
21
- if subject.option_set_properly?(counter_cache, :counter_cache)
21
+ if option_verifier.correct_for_string?(:counter_cache, counter_cache)
22
22
  true
23
23
  else
24
24
  self.missing_option = "#{name} should have #{description}"
@@ -27,7 +27,12 @@ module Shoulda # :nodoc:
27
27
  end
28
28
 
29
29
  private
30
- attr_accessor :counter_cache, :name
30
+
31
+ attr_accessor :subject, :counter_cache, :name
32
+
33
+ def option_verifier
34
+ @option_verifier ||= OptionVerifier.new(subject)
35
+ end
31
36
  end
32
37
  end
33
38
  end
@@ -16,9 +16,9 @@ module Shoulda # :nodoc:
16
16
  end
17
17
 
18
18
  def matches?(subject)
19
- subject = ModelReflector.new(subject, name)
19
+ self.subject = ModelReflector.new(subject, name)
20
20
 
21
- if dependent.nil? || subject.option_set_properly?(dependent, :dependent)
21
+ if option_verifier.correct_for_string?(:dependent, dependent)
22
22
  true
23
23
  else
24
24
  self.missing_option = "#{name} should have #{dependent} dependency"
@@ -27,7 +27,12 @@ module Shoulda # :nodoc:
27
27
  end
28
28
 
29
29
  private
30
- attr_accessor :dependent, :name
30
+
31
+ attr_accessor :subject, :dependent, :name
32
+
33
+ def option_verifier
34
+ @option_verifier ||= OptionVerifier.new(subject)
35
+ end
31
36
  end
32
37
  end
33
38
  end
@@ -20,15 +20,77 @@ module Shoulda # :nodoc:
20
20
  subject.class
21
21
  end
22
22
 
23
- def option_string(key)
24
- reflection.options[key].to_s
23
+ def associated_class
24
+ reflection.klass
25
25
  end
26
26
 
27
- def option_set_properly?(option, option_key)
28
- option.to_s == option_string(option_key)
27
+ def through?
28
+ reflection.options[:through]
29
+ end
30
+
31
+ def join_table
32
+ if reflection.respond_to? :join_table
33
+ reflection.join_table.to_s
34
+ else
35
+ reflection.options[:join_table].to_s
36
+ end
37
+ end
38
+
39
+ def association_relation
40
+ if reflection.respond_to?(:scope) && reflection.scope
41
+ relation_from_scope(reflection.scope)
42
+ else
43
+ options = reflection.options
44
+ relation = RailsShim.clean_scope(reflection.klass)
45
+ if options[:conditions]
46
+ relation = relation.where(options[:conditions])
47
+ end
48
+ if options[:include]
49
+ relation = relation.include(options[:include])
50
+ end
51
+ if options[:order]
52
+ relation = relation.order(options[:order])
53
+ end
54
+ if options[:group]
55
+ relation = relation.group(options[:group])
56
+ end
57
+ if options[:having]
58
+ relation = relation.having(options[:having])
59
+ end
60
+ if options[:limit]
61
+ relation = relation.limit(options[:limit])
62
+ end
63
+ relation
64
+ end
65
+ end
66
+
67
+ def build_relation_with_clause(name, value)
68
+ case name
69
+ when :conditions then associated_class.where(value)
70
+ when :order then associated_class.order(value)
71
+ else raise ArgumentError, "Unknown clause '#{name}'"
72
+ end
73
+ end
74
+
75
+ def extract_relation_clause_from(relation, name)
76
+ case name
77
+ when :conditions then relation.where_values_hash
78
+ when :order then relation.order_values.join(', ')
79
+ else raise ArgumentError, "Unknown clause '#{name}'"
80
+ end
29
81
  end
30
82
 
31
83
  private
84
+
85
+ def relation_from_scope(scope)
86
+ # Source: AR::Associations::AssociationScope#eval_scope
87
+ if scope.is_a?(::Proc)
88
+ associated_class.all.instance_exec(subject, &scope)
89
+ else
90
+ scope
91
+ end
92
+ end
93
+
32
94
  attr_reader :subject, :name
33
95
  end
34
96
  end