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.
- checksums.yaml +7 -0
- data/Appraisals +12 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +6 -11
- data/NEWS.md +6 -0
- data/features/rails_integration.feature +2 -3
- data/features/step_definitions/rails_steps.rb +40 -8
- data/gemfiles/3.0.gemfile +2 -1
- data/gemfiles/3.0.gemfile.lock +6 -6
- data/gemfiles/3.1.gemfile +2 -1
- data/gemfiles/3.1.gemfile.lock +6 -6
- data/gemfiles/3.2.gemfile +2 -1
- data/gemfiles/3.2.gemfile.lock +6 -6
- data/gemfiles/4.0.gemfile +17 -0
- data/gemfiles/4.0.gemfile.lock +152 -0
- data/lib/shoulda/matchers.rb +1 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +24 -10
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +13 -7
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +18 -6
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +4 -2
- data/lib/shoulda/matchers/active_record.rb +1 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +18 -37
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +66 -4
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +93 -0
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -2
- data/lib/shoulda/matchers/integrations/test_unit.rb +10 -22
- data/lib/shoulda/matchers/rails_shim.rb +41 -0
- data/lib/shoulda/matchers/version.rb +1 -1
- data/shoulda-matchers.gemspec +1 -2
- data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +7 -2
- data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +2 -2
- data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
- data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +24 -0
- data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +24 -0
- data/spec/shoulda/matchers/active_model/helpers_spec.rb +5 -7
- data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +46 -12
- data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +1 -1
- data/spec/support/active_model_versions.rb +6 -6
- metadata +26 -55
data/lib/shoulda/matchers.rb
CHANGED
@@ -90,20 +90,34 @@ module Shoulda # :nodoc:
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def flash
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
@flash
|
99
|
-
|
100
|
-
|
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
|
-
|
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.
|
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("#{
|
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, :
|
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 #{
|
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 =>
|
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,
|
175
|
+
message_finder_factory.new(instance, attribute_to_check_message_against, context)
|
170
176
|
end
|
171
177
|
end
|
172
178
|
end
|
@@ -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
|
-
@
|
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 #{@
|
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'
|
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'
|
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'
|
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 = :"#{@
|
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]
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
24
|
-
reflection.
|
23
|
+
def associated_class
|
24
|
+
reflection.klass
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
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
|