shoulda-matchers 4.4.1 → 4.5.0
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 +4 -4
- data/LICENSE +22 -0
- data/README.md +7 -9
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +4 -2
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +3 -2
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +26 -21
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +6 -8
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +6 -8
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +16 -13
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +2 -1
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +5 -6
- data/lib/shoulda/matchers/action_controller/route_params.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +18 -16
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +29 -27
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -5
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +51 -25
- data/lib/shoulda/matchers/active_model/helpers.rb +1 -1
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -34
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +9 -1
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +8 -7
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +26 -25
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +6 -6
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +39 -26
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -6
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +2 -4
- data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
- data/lib/shoulda/matchers/active_model/validator.rb +3 -3
- data/lib/shoulda/matchers/active_record.rb +26 -26
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +6 -3
- data/lib/shoulda/matchers/active_record/association_matcher.rb +80 -40
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
- data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +2 -9
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +12 -7
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +23 -5
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +8 -8
- data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +46 -8
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +39 -17
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +7 -7
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +12 -10
- data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +11 -7
- data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +2 -0
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +13 -9
- data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
- data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
- data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +78 -71
- data/lib/shoulda/matchers/doublespeak.rb +2 -1
- data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
- data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
- data/lib/shoulda/matchers/doublespeak/object_double.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
- data/lib/shoulda/matchers/error.rb +1 -1
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +14 -13
- data/lib/shoulda/matchers/integrations/configuration.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
- data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
- data/lib/shoulda/matchers/rails_shim.rb +5 -3
- data/lib/shoulda/matchers/util.rb +7 -2
- data/lib/shoulda/matchers/util/word_wrap.rb +7 -7
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers/warn.rb +3 -3
- data/shoulda-matchers.gemspec +10 -7
- metadata +11 -10
@@ -84,6 +84,8 @@ module Shoulda
|
|
84
84
|
|
85
85
|
# @private
|
86
86
|
class HaveDbColumnMatcher
|
87
|
+
OPTIONS = %i(precision limit default null scale primary).freeze
|
88
|
+
|
87
89
|
def initialize(column)
|
88
90
|
@column = column
|
89
91
|
@options = {}
|
@@ -95,7 +97,8 @@ module Shoulda
|
|
95
97
|
end
|
96
98
|
|
97
99
|
def with_options(opts = {})
|
98
|
-
|
100
|
+
validate_options(opts)
|
101
|
+
OPTIONS.each do |attribute|
|
99
102
|
if opts.key?(attribute.to_sym)
|
100
103
|
@options[attribute.to_sym] = opts[attribute.to_sym]
|
101
104
|
end
|
@@ -125,23 +128,38 @@ module Shoulda
|
|
125
128
|
|
126
129
|
def description
|
127
130
|
desc = "have db column named #{@column}"
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
desc << " of
|
131
|
+
if @options.key?(:column_type)
|
132
|
+
desc << " of type #{@options[:column_type]}"
|
133
|
+
end
|
134
|
+
if @options.key?(:precision)
|
135
|
+
desc << " of precision #{@options[:precision]}"
|
136
|
+
end
|
137
|
+
desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
|
138
|
+
desc << " of default #{@options[:default]}" if @options.key?(:default)
|
139
|
+
desc << " of null #{@options[:null]}" if @options.key?(:null)
|
140
|
+
desc << " of primary #{@options[:primary]}" if @options.key?(:primary)
|
141
|
+
desc << " of scale #{@options[:scale]}" if @options.key?(:scale)
|
135
142
|
desc
|
136
143
|
end
|
137
144
|
|
138
145
|
protected
|
139
146
|
|
147
|
+
def validate_options(opts)
|
148
|
+
invalid_options = opts.keys.map(&:to_sym) - OPTIONS
|
149
|
+
if invalid_options.any?
|
150
|
+
raise(
|
151
|
+
ArgumentError,
|
152
|
+
"Unknown option(s): #{invalid_options.map(&:inspect).join(', ')}",
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
140
157
|
def column_exists?
|
141
158
|
if model_class.column_names.include?(@column.to_s)
|
142
159
|
true
|
143
160
|
else
|
144
|
-
@missing =
|
161
|
+
@missing =
|
162
|
+
"#{model_class} does not have a db column named #{@column}."
|
145
163
|
false
|
146
164
|
end
|
147
165
|
end
|
@@ -152,8 +170,9 @@ module Shoulda
|
|
152
170
|
if matched_column.type.to_s == @options[:column_type].to_s
|
153
171
|
true
|
154
172
|
else
|
155
|
-
@missing =
|
156
|
-
|
173
|
+
@missing =
|
174
|
+
"#{model_class} has a db column named #{@column} " <<
|
175
|
+
"of type #{matched_column.type}, not #{@options[:column_type]}."
|
157
176
|
false
|
158
177
|
end
|
159
178
|
end
|
@@ -229,18 +248,21 @@ module Shoulda
|
|
229
248
|
true
|
230
249
|
else
|
231
250
|
@missing = "#{model_class} has a db column named #{@column} "
|
232
|
-
|
233
|
-
@
|
234
|
-
|
235
|
-
|
236
|
-
|
251
|
+
@missing <<
|
252
|
+
if @options[:primary]
|
253
|
+
'that is not primary, but should be'
|
254
|
+
else
|
255
|
+
'that is primary, but should not be'
|
256
|
+
end
|
237
257
|
false
|
238
258
|
end
|
239
259
|
end
|
240
260
|
|
241
261
|
def matched_column
|
242
262
|
@_matched_column ||= begin
|
243
|
-
column = model_class.columns.detect
|
263
|
+
column = model_class.columns.detect do |each|
|
264
|
+
each.name == @column.to_s
|
265
|
+
end
|
244
266
|
DecoratedColumn.new(model_class, column)
|
245
267
|
end
|
246
268
|
end
|
@@ -39,22 +39,22 @@ module Shoulda
|
|
39
39
|
check_column_exists!
|
40
40
|
check_implicit_order_column_matches!
|
41
41
|
true
|
42
|
-
rescue SecondaryCheckFailedError =>
|
42
|
+
rescue SecondaryCheckFailedError => e
|
43
43
|
@failure_message = Shoulda::Matchers.word_wrap(
|
44
44
|
"Expected #{model.name} to #{expectation}, " +
|
45
|
-
"but that could not be proved: #{
|
45
|
+
"but that could not be proved: #{e.message}.",
|
46
46
|
)
|
47
47
|
false
|
48
|
-
rescue PrimaryCheckFailedError =>
|
48
|
+
rescue PrimaryCheckFailedError => e
|
49
49
|
@failure_message = Shoulda::Matchers.word_wrap(
|
50
|
-
"Expected #{model.name} to #{expectation}, but #{
|
50
|
+
"Expected #{model.name} to #{expectation}, but #{e.message}.",
|
51
51
|
)
|
52
52
|
false
|
53
53
|
end
|
54
54
|
|
55
55
|
def failure_message_when_negated
|
56
56
|
Shoulda::Matchers.word_wrap(
|
57
|
-
"Expected #{model.name} not to #{expectation}, but it did."
|
57
|
+
"Expected #{model.name} not to #{expectation}, but it did.",
|
58
58
|
)
|
59
59
|
end
|
60
60
|
|
@@ -72,7 +72,7 @@ module Shoulda
|
|
72
72
|
if !matcher.matches?(@subject)
|
73
73
|
raise SecondaryCheckFailedError.new(
|
74
74
|
"The :#{model.table_name} table does not have a " +
|
75
|
-
":#{column_name} column"
|
75
|
+
":#{column_name} column",
|
76
76
|
)
|
77
77
|
end
|
78
78
|
end
|
@@ -81,7 +81,7 @@ module Shoulda
|
|
81
81
|
if model.implicit_order_column.to_s != column_name.to_s
|
82
82
|
message =
|
83
83
|
if model.implicit_order_column.nil?
|
84
|
-
|
84
|
+
'implicit_order_column is not set'
|
85
85
|
else
|
86
86
|
"it is :#{model.implicit_order_column}"
|
87
87
|
end
|
@@ -35,17 +35,19 @@ module Shoulda
|
|
35
35
|
def matches?(subject)
|
36
36
|
@subject = subject
|
37
37
|
if readonly_attributes.include?(@attribute)
|
38
|
-
@failure_message_when_negated = "Did not expect #{@attribute}
|
38
|
+
@failure_message_when_negated = "Did not expect #{@attribute}"\
|
39
|
+
' to be read-only'
|
39
40
|
true
|
40
41
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
"#{
|
47
|
-
|
48
|
-
|
42
|
+
@failure_message =
|
43
|
+
if readonly_attributes.empty?
|
44
|
+
"#{class_name} attribute #{@attribute} " <<
|
45
|
+
'is not read-only'
|
46
|
+
else
|
47
|
+
"#{class_name} is making " <<
|
48
|
+
"#{readonly_attributes.to_a.to_sentence} " <<
|
49
|
+
"read-only, but not #{@attribute}."
|
50
|
+
end
|
49
51
|
false
|
50
52
|
end
|
51
53
|
end
|
@@ -57,7 +59,7 @@ module Shoulda
|
|
57
59
|
private
|
58
60
|
|
59
61
|
def readonly_attributes
|
60
|
-
@
|
62
|
+
@_readonly_attributes ||= (@subject.class.readonly_attributes || [])
|
61
63
|
end
|
62
64
|
|
63
65
|
def class_name
|
@@ -12,7 +12,7 @@ module Shoulda
|
|
12
12
|
#
|
13
13
|
# # RSpec
|
14
14
|
# RSpec.describe Post, type: :model do
|
15
|
-
# it {
|
15
|
+
# it { should have_rich_text(:content) }
|
16
16
|
# end
|
17
17
|
#
|
18
18
|
# # Minitest (Shoulda)
|
@@ -20,20 +20,21 @@ module Shoulda
|
|
20
20
|
# should have_rich_text(:content)
|
21
21
|
# end
|
22
22
|
#
|
23
|
-
# @return [
|
23
|
+
# @return [HaveRichTextMatcher]
|
24
24
|
#
|
25
25
|
def have_rich_text(rich_text_attribute)
|
26
|
-
|
26
|
+
HaveRichTextMatcher.new(rich_text_attribute)
|
27
27
|
end
|
28
28
|
|
29
29
|
# @private
|
30
|
-
class
|
30
|
+
class HaveRichTextMatcher
|
31
31
|
def initialize(rich_text_attribute)
|
32
32
|
@rich_text_attribute = rich_text_attribute
|
33
33
|
end
|
34
34
|
|
35
35
|
def description
|
36
|
-
"have configured :#{rich_text_attribute} as a
|
36
|
+
"have configured :#{rich_text_attribute} as a "\
|
37
|
+
'ActionText::RichText association'
|
37
38
|
end
|
38
39
|
|
39
40
|
def failure_message
|
@@ -41,7 +42,8 @@ module Shoulda
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def failure_message_when_negated
|
44
|
-
"Did not expect #{subject.class} to have ActionText::RichText
|
45
|
+
"Did not expect #{subject.class} to have ActionText::RichText"\
|
46
|
+
" :#{rich_text_attribute}"
|
45
47
|
end
|
46
48
|
|
47
49
|
def matches?(subject)
|
@@ -67,7 +69,9 @@ module Shoulda
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def has_expected_action_text?
|
70
|
-
|
72
|
+
defined?(ActionText::RichText) &&
|
73
|
+
@subject.send(rich_text_attribute).
|
74
|
+
instance_of?(ActionText::RichText)
|
71
75
|
end
|
72
76
|
|
73
77
|
def error_description
|
@@ -63,12 +63,14 @@ module Shoulda
|
|
63
63
|
|
64
64
|
def failure_message
|
65
65
|
return if !@errors
|
66
|
+
|
66
67
|
"Expected #{@subject.class} to #{description} but the following " \
|
67
68
|
"errors were found: #{@errors.join(', ')}"
|
68
69
|
end
|
69
70
|
|
70
71
|
def failure_message_when_negated
|
71
72
|
return if !@errors
|
73
|
+
|
72
74
|
"Did not expect #{@subject.class} to have secure token " \
|
73
75
|
":#{token_attribute}"
|
74
76
|
end
|
@@ -121,7 +121,9 @@ module Shoulda
|
|
121
121
|
|
122
122
|
def description
|
123
123
|
description = "serialize :#{@name}"
|
124
|
-
|
124
|
+
if @options.key?(:type)
|
125
|
+
description += " class_name => #{@options[:type]}"
|
126
|
+
end
|
125
127
|
description
|
126
128
|
end
|
127
129
|
|
@@ -141,13 +143,12 @@ module Shoulda
|
|
141
143
|
klass = serialization_coder
|
142
144
|
if klass == @options[:type]
|
143
145
|
true
|
146
|
+
elsif klass.respond_to?(:object_class) &&
|
147
|
+
klass.object_class == @options[:type]
|
148
|
+
true
|
144
149
|
else
|
145
|
-
|
146
|
-
|
147
|
-
else
|
148
|
-
@missing = ":#{@name} should be a type of #{@options[:type]}"
|
149
|
-
false
|
150
|
-
end
|
150
|
+
@missing = ":#{@name} should be a type of #{@options[:type]}"
|
151
|
+
false
|
151
152
|
end
|
152
153
|
else
|
153
154
|
true
|
@@ -176,9 +177,12 @@ module Shoulda
|
|
176
177
|
end
|
177
178
|
|
178
179
|
def expectation
|
179
|
-
expectation = "#{model_class.name} to serialize the attribute called
|
180
|
+
expectation = "#{model_class.name} to serialize the attribute called"\
|
181
|
+
" :#{@name}"
|
180
182
|
expectation += " with a type of #{@options[:type]}" if @options[:type]
|
181
|
-
|
183
|
+
if @options[:instance_type]
|
184
|
+
expectation += " with an instance of #{@options[:instance_type]}"
|
185
|
+
end
|
182
186
|
expectation
|
183
187
|
end
|
184
188
|
|
@@ -80,7 +80,7 @@ module Shoulda
|
|
80
80
|
#
|
81
81
|
# RSpec.describe Post, type: :model do
|
82
82
|
# describe "validations" do
|
83
|
-
# subject { Post.
|
83
|
+
# subject { Post.new(content: "Here is the content") }
|
84
84
|
# it { should validate_uniqueness_of(:title) }
|
85
85
|
# end
|
86
86
|
# end
|
@@ -92,7 +92,7 @@ module Shoulda
|
|
92
92
|
#
|
93
93
|
# RSpec.describe Post, type: :model do
|
94
94
|
# describe "validations" do
|
95
|
-
# subject { FactoryBot.
|
95
|
+
# subject { FactoryBot.build(:post) }
|
96
96
|
# it { should validate_uniqueness_of(:title) }
|
97
97
|
# end
|
98
98
|
# end
|
@@ -157,6 +157,12 @@ module Shoulda
|
|
157
157
|
# should validate_uniqueness_of(:slug).scoped_to(:journal_id)
|
158
158
|
# end
|
159
159
|
#
|
160
|
+
# NOTE: Support for testing uniqueness validation scoped to an array of
|
161
|
+
# associations is not available.
|
162
|
+
#
|
163
|
+
# For more information, please refer to
|
164
|
+
# https://github.com/thoughtbot/shoulda-matchers/issues/814
|
165
|
+
#
|
160
166
|
# ##### case_insensitive
|
161
167
|
#
|
162
168
|
# Use `case_insensitive` to test usage of the `:case_sensitive` option
|
@@ -264,14 +270,14 @@ module Shoulda
|
|
264
270
|
super(attribute)
|
265
271
|
@expected_message = :taken
|
266
272
|
@options = {
|
267
|
-
case_sensitivity_strategy: :sensitive
|
273
|
+
case_sensitivity_strategy: :sensitive,
|
268
274
|
}
|
269
275
|
@existing_record_created = false
|
270
276
|
@failure_reason = nil
|
271
277
|
@failure_reason_when_negated = nil
|
272
278
|
@attribute_setters = {
|
273
279
|
existing_record: AttributeSetters.new,
|
274
|
-
new_record: AttributeSetters.new
|
280
|
+
new_record: AttributeSetters.new,
|
275
281
|
}
|
276
282
|
end
|
277
283
|
|
@@ -407,11 +413,12 @@ module Shoulda
|
|
407
413
|
else
|
408
414
|
@failure_reason = 'Expected the validation '
|
409
415
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
416
|
+
@failure_reason <<
|
417
|
+
if expected_scopes.empty?
|
418
|
+
'not to be scoped to anything, '
|
419
|
+
else
|
420
|
+
"to be scoped to #{inspected_expected_scopes}, "
|
421
|
+
end
|
415
422
|
|
416
423
|
if actual_sets_of_scopes.any?
|
417
424
|
@failure_reason << 'but it was scoped to '
|
@@ -455,7 +462,7 @@ module Shoulda
|
|
455
462
|
def inspected_actual_scopes
|
456
463
|
inspected_actual_sets_of_scopes.to_sentence(
|
457
464
|
words_connector: ' and ',
|
458
|
-
last_word_connector: ', and'
|
465
|
+
last_word_connector: ', and',
|
459
466
|
)
|
460
467
|
end
|
461
468
|
|
@@ -491,7 +498,9 @@ module Shoulda
|
|
491
498
|
def does_not_match_allow_nil?
|
492
499
|
expects_to_allow_nil? && (
|
493
500
|
update_existing_record!(nil) &&
|
494
|
-
(@failure_reason = nil ||
|
501
|
+
(@failure_reason = nil ||
|
502
|
+
disallows_value_of(nil, @expected_message)
|
503
|
+
)
|
495
504
|
)
|
496
505
|
end
|
497
506
|
|
@@ -527,21 +536,15 @@ module Shoulda
|
|
527
536
|
end
|
528
537
|
|
529
538
|
def find_existing_record
|
530
|
-
|
531
|
-
|
532
|
-
if record.present?
|
533
|
-
record
|
534
|
-
else
|
535
|
-
nil
|
536
|
-
end
|
539
|
+
model.first.presence
|
537
540
|
end
|
538
541
|
|
539
542
|
def create_existing_record
|
540
543
|
@given_record.tap do |existing_record|
|
541
544
|
existing_record.save(validate: false)
|
542
545
|
end
|
543
|
-
rescue ::ActiveRecord::StatementInvalid =>
|
544
|
-
raise ExistingRecordInvalid.create(underlying_exception:
|
546
|
+
rescue ::ActiveRecord::StatementInvalid => e
|
547
|
+
raise ExistingRecordInvalid.create(underlying_exception: e)
|
545
548
|
end
|
546
549
|
|
547
550
|
def update_existing_record!(value)
|
@@ -577,7 +580,7 @@ module Shoulda
|
|
577
580
|
attribute_names_under_test.each do |attribute_name|
|
578
581
|
set_attribute_on_new_record!(
|
579
582
|
attribute_name,
|
580
|
-
existing_record.public_send(attribute_name)
|
583
|
+
existing_record.public_send(attribute_name),
|
581
584
|
)
|
582
585
|
end
|
583
586
|
|
@@ -619,11 +622,12 @@ module Shoulda
|
|
619
622
|
|
620
623
|
reason << inspected_scopes.to_sentence
|
621
624
|
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
625
|
+
reason <<
|
626
|
+
if inspected_scopes.many?
|
627
|
+
' do not seem to be attributes'
|
628
|
+
else
|
629
|
+
' does not seem to be an attribute'
|
630
|
+
end
|
627
631
|
|
628
632
|
reason << " on #{model.name}."
|
629
633
|
|
@@ -643,11 +647,12 @@ module Shoulda
|
|
643
647
|
|
644
648
|
reason << inspected_scopes.to_sentence
|
645
649
|
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
650
|
+
reason <<
|
651
|
+
if inspected_scopes.many?
|
652
|
+
' seem to be attributes'
|
653
|
+
else
|
654
|
+
' seems to be an attribute'
|
655
|
+
end
|
651
656
|
|
652
657
|
reason << " on #{model.name}."
|
653
658
|
|
@@ -658,14 +663,14 @@ module Shoulda
|
|
658
663
|
end
|
659
664
|
|
660
665
|
def scopes_present_on_model
|
661
|
-
@
|
666
|
+
@_scopes_present_on_model ||= expected_scopes.select do |scope|
|
662
667
|
model.method_defined?("#{scope}=")
|
663
668
|
end
|
664
669
|
end
|
665
670
|
|
666
671
|
def scopes_missing_on_model
|
667
|
-
@
|
668
|
-
|
672
|
+
@_scopes_missing_on_model ||= expected_scopes.reject do |scope|
|
673
|
+
model.method_defined?("#{scope}=")
|
669
674
|
end
|
670
675
|
end
|
671
676
|
|
@@ -697,7 +702,7 @@ module Shoulda
|
|
697
702
|
raise NonCaseSwappableValueError.create(
|
698
703
|
model: model,
|
699
704
|
attribute: @attribute,
|
700
|
-
value: value
|
705
|
+
value: value,
|
701
706
|
)
|
702
707
|
end
|
703
708
|
|
@@ -724,7 +729,7 @@ module Shoulda
|
|
724
729
|
raise NonCaseSwappableValueError.create(
|
725
730
|
model: model,
|
726
731
|
attribute: @attribute,
|
727
|
-
value: value
|
732
|
+
value: value,
|
728
733
|
)
|
729
734
|
end
|
730
735
|
|
@@ -789,7 +794,7 @@ module Shoulda
|
|
789
794
|
column = column_for(scope)
|
790
795
|
|
791
796
|
if column.respond_to?(:array) && column.array
|
792
|
-
[
|
797
|
+
[dummy_scalar_value_for(column)]
|
793
798
|
else
|
794
799
|
dummy_scalar_value_for(column)
|
795
800
|
end
|
@@ -801,7 +806,7 @@ module Shoulda
|
|
801
806
|
|
802
807
|
def next_value_for(scope, previous_value)
|
803
808
|
if previous_value.is_a?(Array)
|
804
|
-
[
|
809
|
+
[next_scalar_value_for(scope, previous_value[0])]
|
805
810
|
else
|
806
811
|
next_scalar_value_for(scope, previous_value)
|
807
812
|
end
|
@@ -857,7 +862,7 @@ module Shoulda
|
|
857
862
|
attribute_setter = build_attribute_setter(
|
858
863
|
record,
|
859
864
|
attribute_name,
|
860
|
-
value
|
865
|
+
value,
|
861
866
|
)
|
862
867
|
attribute_setter.set!
|
863
868
|
|
@@ -869,7 +874,7 @@ module Shoulda
|
|
869
874
|
:existing_record,
|
870
875
|
existing_record,
|
871
876
|
attribute_name,
|
872
|
-
value
|
877
|
+
value,
|
873
878
|
)
|
874
879
|
end
|
875
880
|
|
@@ -878,7 +883,7 @@ module Shoulda
|
|
878
883
|
:new_record,
|
879
884
|
new_record,
|
880
885
|
attribute_name,
|
881
|
-
value
|
886
|
+
value,
|
882
887
|
)
|
883
888
|
end
|
884
889
|
|
@@ -896,13 +901,14 @@ module Shoulda
|
|
896
901
|
end
|
897
902
|
|
898
903
|
def build_attribute_setter(record, attribute_name, value)
|
899
|
-
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeSetter.
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
904
|
+
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeSetter.
|
905
|
+
new(
|
906
|
+
matcher_name: :validate_uniqueness_of,
|
907
|
+
object: record,
|
908
|
+
attribute_name: attribute_name,
|
909
|
+
value: value,
|
910
|
+
ignore_interference_by_writer: ignore_interference_by_writer,
|
911
|
+
)
|
906
912
|
end
|
907
913
|
|
908
914
|
def existing_value_read
|
@@ -929,7 +935,7 @@ module Shoulda
|
|
929
935
|
@given_record.class
|
930
936
|
end
|
931
937
|
|
932
|
-
def failure_message_preface
|
938
|
+
def failure_message_preface # rubocop:disable Metrics/MethodLength
|
933
939
|
prefix = ''
|
934
940
|
|
935
941
|
if @existing_record_created
|
@@ -938,30 +944,28 @@ module Shoulda
|
|
938
944
|
if attribute_setter_for_existing_record
|
939
945
|
prefix << ', setting '
|
940
946
|
prefix << description_for_attribute_setter(
|
941
|
-
attribute_setter_for_existing_record
|
947
|
+
attribute_setter_for_existing_record,
|
942
948
|
)
|
943
949
|
else
|
944
950
|
prefix << ", whose :#{attribute} is "
|
945
951
|
prefix << "‹#{existing_value_read.inspect}›"
|
946
952
|
end
|
947
953
|
|
948
|
-
prefix <<
|
954
|
+
prefix << ', and saving it as the existing record, then'
|
955
|
+
elsif attribute_setter_for_existing_record
|
956
|
+
prefix << "Given an existing #{model.name},"
|
957
|
+
prefix << ' after setting '
|
958
|
+
prefix << description_for_attribute_setter(
|
959
|
+
attribute_setter_for_existing_record,
|
960
|
+
)
|
961
|
+
prefix << ', then'
|
949
962
|
else
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
prefix << ', then'
|
957
|
-
else
|
958
|
-
prefix << "Given an existing #{model.name} whose :#{attribute}"
|
959
|
-
prefix << ' is '
|
960
|
-
prefix << Shoulda::Matchers::Util.inspect_value(
|
961
|
-
existing_value_read
|
962
|
-
)
|
963
|
-
prefix << ', after'
|
964
|
-
end
|
963
|
+
prefix << "Given an existing #{model.name} whose :#{attribute}"
|
964
|
+
prefix << ' is '
|
965
|
+
prefix << Shoulda::Matchers::Util.inspect_value(
|
966
|
+
existing_value_read,
|
967
|
+
)
|
968
|
+
prefix << ', after'
|
965
969
|
end
|
966
970
|
|
967
971
|
prefix << " making a new #{model.name} and setting "
|
@@ -988,7 +992,10 @@ different altogether.
|
|
988
992
|
MESSAGE
|
989
993
|
end
|
990
994
|
|
991
|
-
def description_for_attribute_setter(
|
995
|
+
def description_for_attribute_setter(
|
996
|
+
attribute_setter,
|
997
|
+
same_as_existing: nil
|
998
|
+
)
|
992
999
|
description = "its :#{attribute_setter.attribute_name} to "
|
993
1000
|
|
994
1001
|
if same_as_existing == false
|
@@ -996,13 +1003,13 @@ different altogether.
|
|
996
1003
|
end
|
997
1004
|
|
998
1005
|
description << Shoulda::Matchers::Util.inspect_value(
|
999
|
-
attribute_setter.value_written
|
1006
|
+
attribute_setter.value_written,
|
1000
1007
|
)
|
1001
1008
|
|
1002
1009
|
if attribute_setter.attribute_changed_value?
|
1003
1010
|
description << ' (read back as '
|
1004
1011
|
description << Shoulda::Matchers::Util.inspect_value(
|
1005
|
-
attribute_setter.value_read
|
1012
|
+
attribute_setter.value_read,
|
1006
1013
|
)
|
1007
1014
|
description << ')'
|
1008
1015
|
end
|
@@ -1026,7 +1033,7 @@ different altogether.
|
|
1026
1033
|
)
|
1027
1034
|
description_for_attribute_setter(
|
1028
1035
|
attribute_setter,
|
1029
|
-
same_as_existing: same_as_existing
|
1036
|
+
same_as_existing: same_as_existing,
|
1030
1037
|
)
|
1031
1038
|
end
|
1032
1039
|
end
|
@@ -1109,7 +1116,7 @@ b) If you meant for the validation to be case-insensitive, then you need to
|
|
1109
1116
|
|
1110
1117
|
For more information, please see:
|
1111
1118
|
|
1112
|
-
|
1119
|
+
https://matchers.shoulda.io/docs/v#{Shoulda::Matchers::VERSION}/file.NonCaseSwappableValueError.html
|
1113
1120
|
MESSAGE
|
1114
1121
|
end
|
1115
1122
|
end
|