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.
- 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
|