shoulda-matchers 5.1.0 → 6.4.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 +1 -1
- data/README.md +41 -18
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +1 -1
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
- data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +3 -5
- data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
- data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
- data/lib/shoulda/matchers/active_model/qualifiers.rb +1 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -7
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +5 -5
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +29 -14
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +65 -10
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +76 -86
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +29 -4
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
- data/lib/shoulda/matchers/active_model/validator.rb +4 -0
- data/lib/shoulda/matchers/active_model.rb +4 -1
- data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +34 -4
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +9 -1
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -0
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +4 -0
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +23 -19
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +338 -30
- data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/lib/shoulda/matchers/active_record/uniqueness/model.rb +13 -1
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
- data/lib/shoulda/matchers/active_record.rb +2 -0
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
- data/lib/shoulda/matchers/doublespeak.rb +0 -1
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +7 -5
- data/lib/shoulda/matchers/integrations/libraries/routing.rb +5 -3
- data/lib/shoulda/matchers/rails_shim.rb +22 -6
- data/lib/shoulda/matchers/util/word_wrap.rb +2 -2
- data/lib/shoulda/matchers/util.rb +18 -20
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +2 -2
- data/shoulda-matchers.gemspec +2 -2
- metadata +15 -9
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -157
@@ -48,6 +48,30 @@ module Shoulda
|
|
48
48
|
# should have_db_column(:camera_aperture).of_type(:decimal)
|
49
49
|
# end
|
50
50
|
#
|
51
|
+
# ##### of_sql_type
|
52
|
+
#
|
53
|
+
# Use `of_sql_type` to assert that a column is defined as a certain sql_type.
|
54
|
+
#
|
55
|
+
# class CreatePhones < ActiveRecord::Migration
|
56
|
+
# def change
|
57
|
+
# create_table :phones do |t|
|
58
|
+
# t.string :camera_aperture, limit: 36
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# # RSpec
|
64
|
+
# RSpec.describe Phone, type: :model do
|
65
|
+
# it do
|
66
|
+
# should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# # Minitest (Shoulda)
|
71
|
+
# class PhoneTest < ActiveSupport::TestCase
|
72
|
+
# should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
|
73
|
+
# end
|
74
|
+
#
|
51
75
|
# ##### with_options
|
52
76
|
#
|
53
77
|
# Use `with_options` to assert that a column has been defined with
|
@@ -96,6 +120,11 @@ module Shoulda
|
|
96
120
|
self
|
97
121
|
end
|
98
122
|
|
123
|
+
def of_sql_type(sql_column_type)
|
124
|
+
@options[:sql_column_type] = sql_column_type
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
99
128
|
def with_options(opts = {})
|
100
129
|
validate_options(opts)
|
101
130
|
OPTIONS.each do |attribute|
|
@@ -110,6 +139,7 @@ module Shoulda
|
|
110
139
|
@subject = subject
|
111
140
|
column_exists? &&
|
112
141
|
correct_column_type? &&
|
142
|
+
correct_sql_column_type? &&
|
113
143
|
correct_precision? &&
|
114
144
|
correct_limit? &&
|
115
145
|
correct_default? &&
|
@@ -129,12 +159,9 @@ module Shoulda
|
|
129
159
|
|
130
160
|
def description
|
131
161
|
desc = "have db column named #{@column}"
|
132
|
-
if @options.key?(:column_type)
|
133
|
-
|
134
|
-
|
135
|
-
if @options.key?(:precision)
|
136
|
-
desc << " of precision #{@options[:precision]}"
|
137
|
-
end
|
162
|
+
desc << " of type #{@options[:column_type]}" if @options.key?(:column_type)
|
163
|
+
desc << " of sql_type #{@options[:sql_column_type]}" if @options.key?(:sql_column_type)
|
164
|
+
desc << " of precision #{@options[:precision]}" if @options.key?(:precision)
|
138
165
|
desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
|
139
166
|
desc << " of default #{@options[:default]}" if @options.key?(:default)
|
140
167
|
desc << " of null #{@options[:null]}" if @options.key?(:null)
|
@@ -178,6 +205,19 @@ module Shoulda
|
|
178
205
|
end
|
179
206
|
end
|
180
207
|
|
208
|
+
def correct_sql_column_type?
|
209
|
+
return true unless @options.key?(:sql_column_type)
|
210
|
+
|
211
|
+
if matched_column.sql_type.to_s == @options[:sql_column_type].to_s
|
212
|
+
true
|
213
|
+
else
|
214
|
+
@missing =
|
215
|
+
"#{model_class} has a db column named #{@column} " <<
|
216
|
+
"of sql type #{matched_column.sql_type}, not #{@options[:sql_column_type]}."
|
217
|
+
false
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
181
221
|
def correct_precision?
|
182
222
|
return true unless @options.key?(:precision)
|
183
223
|
|
@@ -152,18 +152,18 @@ module Shoulda
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def description
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end
|
155
|
+
String.new('have ').tap do |description|
|
156
|
+
description <<
|
157
|
+
if qualifiers.include?(:unique)
|
158
|
+
"#{Shoulda::Matchers::Util.a_or_an(index_type)} "
|
159
|
+
else
|
160
|
+
'an '
|
161
|
+
end
|
163
162
|
|
164
|
-
|
163
|
+
description << 'index on '
|
165
164
|
|
166
|
-
|
165
|
+
description << inspected_expected_columns
|
166
|
+
end
|
167
167
|
end
|
168
168
|
|
169
169
|
private
|
@@ -197,17 +197,28 @@ module Shoulda
|
|
197
197
|
def matched_index
|
198
198
|
@_matched_index ||=
|
199
199
|
if expected_columns.one?
|
200
|
-
|
200
|
+
sorted_indexes.detect do |index|
|
201
201
|
Array.wrap(index.columns) == expected_columns
|
202
202
|
end
|
203
203
|
else
|
204
|
-
|
204
|
+
sorted_indexes.detect do |index|
|
205
205
|
index.columns == expected_columns
|
206
206
|
end
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
210
|
-
def
|
210
|
+
def sorted_indexes
|
211
|
+
if qualifiers.include?(:unique)
|
212
|
+
# return indexes with unique matching the qualifier first
|
213
|
+
unsorted_indexes.sort_by do |index|
|
214
|
+
index.unique == qualifiers[:unique] ? 0 : 1
|
215
|
+
end
|
216
|
+
else
|
217
|
+
unsorted_indexes
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def unsorted_indexes
|
211
222
|
model.connection.indexes(table_name)
|
212
223
|
end
|
213
224
|
|
@@ -2,7 +2,7 @@ module Shoulda
|
|
2
2
|
module Matchers
|
3
3
|
module ActiveRecord
|
4
4
|
# The `have_implicit_order_column` matcher tests that the model has `implicit_order_column`
|
5
|
-
# assigned to one of the table columns.
|
5
|
+
# assigned to one of the table columns.
|
6
6
|
#
|
7
7
|
# class Product < ApplicationRecord
|
8
8
|
# self.implicit_order_column = :created_at
|
@@ -20,10 +20,8 @@ module Shoulda
|
|
20
20
|
#
|
21
21
|
# @return [HaveImplicitOrderColumnMatcher]
|
22
22
|
#
|
23
|
-
|
24
|
-
|
25
|
-
HaveImplicitOrderColumnMatcher.new(column_name)
|
26
|
-
end
|
23
|
+
def have_implicit_order_column(column_name)
|
24
|
+
HaveImplicitOrderColumnMatcher.new(column_name)
|
27
25
|
end
|
28
26
|
|
29
27
|
# @private
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord
|
4
|
+
# The `normalize` matcher is used to ensure attribute normalizations
|
5
|
+
# are transforming attribute values as expected.
|
6
|
+
#
|
7
|
+
# Take this model for example:
|
8
|
+
#
|
9
|
+
# class User < ActiveRecord::Base
|
10
|
+
# normalizes :email, with: -> email { email.strip.downcase }
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# You can use `normalize` providing an input and defining the expected
|
14
|
+
# normalization output:
|
15
|
+
#
|
16
|
+
# # RSpec
|
17
|
+
# RSpec.describe User, type: :model do
|
18
|
+
# it do
|
19
|
+
# should normalize(:email).from(" ME@XYZ.COM\n").to("me@xyz.com")
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # Minitest (Shoulda)
|
24
|
+
# class User < ActiveSupport::TestCase
|
25
|
+
# should normalize(:email).from(" ME@XYZ.COM\n").to("me@xyz.com")
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# You can use `normalize` to test multiple attributes at once:
|
29
|
+
#
|
30
|
+
# class User < ActiveRecord::Base
|
31
|
+
# normalizes :email, :handle, with: -> value { value.strip.downcase }
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # RSpec
|
35
|
+
# RSpec.describe User, type: :model do
|
36
|
+
# it do
|
37
|
+
# should normalize(:email, :handle).from(" Example\n").to("example")
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # Minitest (Shoulda)
|
42
|
+
# class User < ActiveSupport::TestCase
|
43
|
+
# should normalize(:email, :handle).from(" Example\n").to("example")
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# If the normalization accepts nil values with the `apply_to_nil` option,
|
47
|
+
# you just need to use `.from(nil).to("Your expected value here")`.
|
48
|
+
#
|
49
|
+
# class User < ActiveRecord::Base
|
50
|
+
# normalizes :name, with: -> name { name&.titleize || 'Untitled' },
|
51
|
+
# apply_to_nil: true
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # RSpec
|
55
|
+
# RSpec.describe User, type: :model do
|
56
|
+
# it { should normalize(:name).from("jane doe").to("Jane Doe") }
|
57
|
+
# it { should normalize(:name).from(nil).to("Untitled") }
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# # Minitest (Shoulda)
|
61
|
+
# class User < ActiveSupport::TestCase
|
62
|
+
# should normalize(:name).from("jane doe").to("Jane Doe")
|
63
|
+
# should normalize(:name).from(nil).to("Untitled")
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# @return [NormalizeMatcher]
|
67
|
+
#
|
68
|
+
def normalize(*attributes)
|
69
|
+
if attributes.empty?
|
70
|
+
raise ArgumentError, 'need at least one attribute'
|
71
|
+
else
|
72
|
+
NormalizeMatcher.new(*attributes)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @private
|
77
|
+
class NormalizeMatcher
|
78
|
+
attr_reader :attributes, :from_value, :to_value, :failure_message,
|
79
|
+
:failure_message_when_negated
|
80
|
+
|
81
|
+
def initialize(*attributes)
|
82
|
+
@attributes = attributes
|
83
|
+
end
|
84
|
+
|
85
|
+
def description
|
86
|
+
%(
|
87
|
+
normalize #{attributes.to_sentence(last_word_connector: ' and ')} from
|
88
|
+
‹#{from_value.inspect}› to ‹#{to_value.inspect}›
|
89
|
+
).squish
|
90
|
+
end
|
91
|
+
|
92
|
+
def from(value)
|
93
|
+
@from_value = value
|
94
|
+
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def to(value)
|
99
|
+
@to_value = value
|
100
|
+
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def matches?(subject)
|
105
|
+
attributes.all? { |attribute| attribute_matches?(subject, attribute) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def does_not_match?(subject)
|
109
|
+
attributes.all? { |attribute| attribute_does_not_match?(subject, attribute) }
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def attribute_matches?(subject, attribute)
|
115
|
+
return true if normalize_attribute?(subject, attribute)
|
116
|
+
|
117
|
+
@failure_message = build_failure_message(
|
118
|
+
attribute,
|
119
|
+
subject.class.normalize_value_for(attribute, from_value),
|
120
|
+
)
|
121
|
+
false
|
122
|
+
end
|
123
|
+
|
124
|
+
def attribute_does_not_match?(subject, attribute)
|
125
|
+
return true unless normalize_attribute?(subject, attribute)
|
126
|
+
|
127
|
+
@failure_message_when_negated = build_failure_message_when_negated(attribute)
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
131
|
+
def normalize_attribute?(subject, attribute)
|
132
|
+
subject.class.normalize_value_for(attribute, from_value) == to_value
|
133
|
+
end
|
134
|
+
|
135
|
+
def build_failure_message(attribute, attribute_value)
|
136
|
+
%(
|
137
|
+
Expected to normalize #{attribute.inspect} from ‹#{from_value.inspect}› to
|
138
|
+
‹#{to_value.inspect}› but it was normalized to ‹#{attribute_value.inspect}›
|
139
|
+
).squish
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_failure_message_when_negated(attribute)
|
143
|
+
%(
|
144
|
+
Expected to not normalize #{attribute.inspect} from ‹#{from_value.inspect}› to
|
145
|
+
‹#{to_value.inspect}› but it was normalized
|
146
|
+
).squish
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -29,7 +29,19 @@ module Shoulda
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def symlink_to(parent)
|
32
|
-
|
32
|
+
table_name = parent.table_name
|
33
|
+
|
34
|
+
new_class = Class.new(parent) do
|
35
|
+
define_singleton_method :table_name do
|
36
|
+
table_name
|
37
|
+
end
|
38
|
+
|
39
|
+
define_singleton_method :base_class do
|
40
|
+
self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
namespace.set(name, new_class)
|
33
45
|
end
|
34
46
|
|
35
47
|
def to_s
|
@@ -411,9 +411,29 @@ module Shoulda
|
|
411
411
|
if scopes_match?
|
412
412
|
true
|
413
413
|
else
|
414
|
-
@failure_reason = 'Expected the validation '
|
414
|
+
@failure_reason = String.new('Expected the validation ').tap do |failure_reason_string|
|
415
|
+
failure_reason_string <<
|
416
|
+
if expected_scopes.empty?
|
417
|
+
'not to be scoped to anything, '
|
418
|
+
else
|
419
|
+
"to be scoped to #{inspected_expected_scopes}, "
|
420
|
+
end
|
421
|
+
|
422
|
+
if actual_sets_of_scopes.any?
|
423
|
+
failure_reason_string << 'but it was scoped to '
|
424
|
+
failure_reason_string << "#{inspected_actual_scopes} instead."
|
425
|
+
else
|
426
|
+
failure_reason_string << 'but it was not scoped to anything.'
|
427
|
+
end
|
428
|
+
end
|
415
429
|
|
416
|
-
|
430
|
+
false
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
def build_failure_reason
|
435
|
+
String.new('Expected the validation ').tap do |failure_reason_string|
|
436
|
+
failure_reason_string <<
|
417
437
|
if expected_scopes.empty?
|
418
438
|
'not to be scoped to anything, '
|
419
439
|
else
|
@@ -421,27 +441,25 @@ module Shoulda
|
|
421
441
|
end
|
422
442
|
|
423
443
|
if actual_sets_of_scopes.any?
|
424
|
-
|
425
|
-
|
444
|
+
failure_reason_string << 'but it was scoped to '
|
445
|
+
failure_reason_string << "#{inspected_actual_scopes} instead."
|
426
446
|
else
|
427
|
-
|
447
|
+
failure_reason_string << 'but it was not scoped to anything.'
|
428
448
|
end
|
429
|
-
|
430
|
-
false
|
431
449
|
end
|
432
450
|
end
|
433
451
|
|
434
452
|
def does_not_match_scopes_configuration?
|
435
453
|
if scopes_match?
|
436
|
-
@failure_reason = 'Expected the validation '
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
454
|
+
@failure_reason = String.new('Expected the validation ').tap do |failure_reason|
|
455
|
+
if expected_scopes.empty?
|
456
|
+
failure_reason << 'to be scoped to nothing, '
|
457
|
+
failure_reason << 'but it was scoped to '
|
458
|
+
failure_reason << "#{inspected_actual_scopes} instead."
|
459
|
+
else
|
460
|
+
failure_reason << 'not to be scoped to '
|
461
|
+
failure_reason << inspected_expected_scopes
|
462
|
+
end
|
445
463
|
end
|
446
464
|
|
447
465
|
false
|
@@ -618,20 +636,18 @@ module Shoulda
|
|
618
636
|
else
|
619
637
|
inspected_scopes = scopes_missing_on_model.map(&:inspect)
|
620
638
|
|
621
|
-
|
622
|
-
|
623
|
-
reason << inspected_scopes.to_sentence
|
624
|
-
|
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
|
639
|
+
@failure_reason = String.new.tap do |reason|
|
640
|
+
reason << inspected_scopes.to_sentence
|
631
641
|
|
632
|
-
|
642
|
+
reason <<
|
643
|
+
if inspected_scopes.many?
|
644
|
+
' do not seem to be attributes'
|
645
|
+
else
|
646
|
+
' does not seem to be an attribute'
|
647
|
+
end
|
633
648
|
|
634
|
-
|
649
|
+
reason << " on #{model.name}."
|
650
|
+
end
|
635
651
|
|
636
652
|
false
|
637
653
|
end
|
@@ -643,20 +659,18 @@ module Shoulda
|
|
643
659
|
else
|
644
660
|
inspected_scopes = scopes_present_on_model.map(&:inspect)
|
645
661
|
|
646
|
-
|
647
|
-
|
648
|
-
reason << inspected_scopes.to_sentence
|
649
|
-
|
650
|
-
reason <<
|
651
|
-
if inspected_scopes.many?
|
652
|
-
' seem to be attributes'
|
653
|
-
else
|
654
|
-
' seems to be an attribute'
|
655
|
-
end
|
662
|
+
@failure_reason = String.new.tap do |reason|
|
663
|
+
reason << inspected_scopes.to_sentence
|
656
664
|
|
657
|
-
|
665
|
+
reason <<
|
666
|
+
if inspected_scopes.many?
|
667
|
+
' seem to be attributes'
|
668
|
+
else
|
669
|
+
' seems to be an attribute'
|
670
|
+
end
|
658
671
|
|
659
|
-
|
672
|
+
reason << " on #{model.name}."
|
673
|
+
end
|
660
674
|
|
661
675
|
false
|
662
676
|
end
|
@@ -936,45 +950,43 @@ module Shoulda
|
|
936
950
|
end
|
937
951
|
|
938
952
|
def failure_message_preface # rubocop:disable Metrics/MethodLength
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
953
|
+
String.new.tap do |prefix|
|
954
|
+
if @existing_record_created
|
955
|
+
prefix << "After taking the given #{model.name}"
|
956
|
+
|
957
|
+
if attribute_setter_for_existing_record
|
958
|
+
prefix << ', setting '
|
959
|
+
prefix << description_for_attribute_setter(
|
960
|
+
attribute_setter_for_existing_record,
|
961
|
+
)
|
962
|
+
else
|
963
|
+
prefix << ", whose :#{attribute} is "
|
964
|
+
prefix << "‹#{existing_value_read.inspect}›"
|
965
|
+
end
|
943
966
|
|
944
|
-
|
945
|
-
|
967
|
+
prefix << ', and saving it as the existing record, then'
|
968
|
+
elsif attribute_setter_for_existing_record
|
969
|
+
prefix << "Given an existing #{model.name},"
|
970
|
+
prefix << ' after setting '
|
946
971
|
prefix << description_for_attribute_setter(
|
947
972
|
attribute_setter_for_existing_record,
|
948
973
|
)
|
974
|
+
prefix << ', then'
|
949
975
|
else
|
950
|
-
prefix << "
|
951
|
-
prefix <<
|
976
|
+
prefix << "Given an existing #{model.name} whose :#{attribute}"
|
977
|
+
prefix << ' is '
|
978
|
+
prefix << Shoulda::Matchers::Util.inspect_value(
|
979
|
+
existing_value_read,
|
980
|
+
)
|
981
|
+
prefix << ', after'
|
952
982
|
end
|
953
983
|
|
954
|
-
prefix <<
|
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'
|
962
|
-
else
|
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'
|
969
|
-
end
|
970
|
-
|
971
|
-
prefix << " making a new #{model.name} and setting "
|
984
|
+
prefix << " making a new #{model.name} and setting "
|
972
985
|
|
973
|
-
|
986
|
+
prefix << descriptions_for_attribute_setters_for_new_record
|
974
987
|
|
975
|
-
|
976
|
-
|
977
|
-
prefix
|
988
|
+
prefix << ", the matcher expected the new #{model.name} to be"
|
989
|
+
end
|
978
990
|
end
|
979
991
|
|
980
992
|
def attribute_changed_value_message
|
@@ -24,6 +24,8 @@ require 'shoulda/matchers/active_record/define_enum_for_matcher'
|
|
24
24
|
require 'shoulda/matchers/active_record/uniqueness'
|
25
25
|
require 'shoulda/matchers/active_record/validate_uniqueness_of_matcher'
|
26
26
|
require 'shoulda/matchers/active_record/have_attached_matcher'
|
27
|
+
require 'shoulda/matchers/active_record/normalize_matcher'
|
28
|
+
require 'shoulda/matchers/active_record/encrypt_matcher'
|
27
29
|
|
28
30
|
module Shoulda
|
29
31
|
module Matchers
|
@@ -18,15 +18,11 @@ module Shoulda
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def activate
|
21
|
-
doubles_by_method_name.
|
22
|
-
double.activate
|
23
|
-
end
|
21
|
+
doubles_by_method_name.each_value(&:activate)
|
24
22
|
end
|
25
23
|
|
26
24
|
def deactivate
|
27
|
-
doubles_by_method_name.
|
28
|
-
double.deactivate
|
29
|
-
end
|
25
|
+
doubles_by_method_name.each_value(&:deactivate)
|
30
26
|
end
|
31
27
|
|
32
28
|
def calls_by_method_name
|
@@ -39,15 +39,11 @@ module Shoulda
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def activate
|
42
|
-
double_collections_by_class.
|
43
|
-
double_collection.activate
|
44
|
-
end
|
42
|
+
double_collections_by_class.each_value(&:activate)
|
45
43
|
end
|
46
44
|
|
47
45
|
def deactivate
|
48
|
-
double_collections_by_class.
|
49
|
-
double_collection.deactivate
|
50
|
-
end
|
46
|
+
double_collections_by_class.each_value(&:deactivate)
|
51
47
|
end
|
52
48
|
|
53
49
|
def double_collections_by_class
|
@@ -400,7 +400,7 @@ module Shoulda
|
|
400
400
|
false
|
401
401
|
rescue NoMethodError => e
|
402
402
|
if e.message =~
|
403
|
-
/undefined method `#{delegate_method}' for nil
|
403
|
+
/undefined method `#{delegate_method}' for nil/
|
404
404
|
false
|
405
405
|
else
|
406
406
|
raise e
|
@@ -440,22 +440,20 @@ module Shoulda
|
|
440
440
|
end
|
441
441
|
|
442
442
|
def formatted_calls_on_delegate_object
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
443
|
+
String.new.tap do |string|
|
444
|
+
if calls_on_delegate_object.any?
|
445
|
+
string << "\n\n"
|
446
|
+
calls_on_delegate_object.each_with_index do |call, i|
|
447
|
+
name = call.method_name
|
448
|
+
args = call.args.map(&:inspect).join(', ')
|
449
|
+
string << "#{i + 1}) #{name}(#{args})\n"
|
450
|
+
end
|
451
|
+
else
|
452
|
+
string << ' (none)'
|
451
453
|
end
|
452
|
-
else
|
453
|
-
string << ' (none)'
|
454
|
-
end
|
455
454
|
|
456
|
-
|
457
|
-
|
458
|
-
string
|
455
|
+
string.rstrip!
|
456
|
+
end
|
459
457
|
end
|
460
458
|
end
|
461
459
|
end
|