shoulda-matchers 2.3.0 → 2.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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