ultra-fast-box 0.0.1
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/shoulda-matchers-8.0.1/LICENSE +22 -0
- data/shoulda-matchers-8.0.1/README.md +567 -0
- data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
- data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
- data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
- data/ultra-fast-box.gemspec +12 -0
- metadata +179 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
module Shoulda
|
|
2
|
+
module Matchers
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# The `encrypt` matcher tests usage of the
|
|
5
|
+
# `encrypts` macro (Rails 7+ only).
|
|
6
|
+
#
|
|
7
|
+
# class Survey < ActiveRecord::Base
|
|
8
|
+
# encrypts :access_code
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# # RSpec
|
|
12
|
+
# RSpec.describe Survey, type: :model do
|
|
13
|
+
# it { should encrypt(:access_code) }
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # Minitest (Shoulda)
|
|
17
|
+
# class SurveyTest < ActiveSupport::TestCase
|
|
18
|
+
# should encrypt(:access_code)
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# #### Qualifiers
|
|
22
|
+
#
|
|
23
|
+
# ##### deterministic
|
|
24
|
+
#
|
|
25
|
+
# class Survey < ActiveRecord::Base
|
|
26
|
+
# encrypts :access_code, deterministic: true
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# # RSpec
|
|
30
|
+
# RSpec.describe Survey, type: :model do
|
|
31
|
+
# it { should encrypt(:access_code).deterministic(true) }
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# # Minitest (Shoulda)
|
|
35
|
+
# class SurveyTest < ActiveSupport::TestCase
|
|
36
|
+
# should encrypt(:access_code).deterministic(true)
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# ##### downcase
|
|
40
|
+
#
|
|
41
|
+
# class Survey < ActiveRecord::Base
|
|
42
|
+
# encrypts :access_code, downcase: true
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# # RSpec
|
|
46
|
+
# RSpec.describe Survey, type: :model do
|
|
47
|
+
# it { should encrypt(:access_code).downcase(true) }
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# # Minitest (Shoulda)
|
|
51
|
+
# class SurveyTest < ActiveSupport::TestCase
|
|
52
|
+
# should encrypt(:access_code).downcase(true)
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# ##### ignore_case
|
|
56
|
+
#
|
|
57
|
+
# class Survey < ActiveRecord::Base
|
|
58
|
+
# encrypts :access_code, deterministic: true, ignore_case: true
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# # RSpec
|
|
62
|
+
# RSpec.describe Survey, type: :model do
|
|
63
|
+
# it { should encrypt(:access_code).ignore_case(true) }
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
# # Minitest (Shoulda)
|
|
67
|
+
# class SurveyTest < ActiveSupport::TestCase
|
|
68
|
+
# should encrypt(:access_code).ignore_case(true)
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# @return [EncryptMatcher]
|
|
72
|
+
#
|
|
73
|
+
def encrypt(value)
|
|
74
|
+
EncryptMatcher.new(value)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @private
|
|
78
|
+
class EncryptMatcher
|
|
79
|
+
def initialize(attribute)
|
|
80
|
+
@attribute = attribute.to_sym
|
|
81
|
+
@options = {}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
attr_reader :failure_message, :failure_message_when_negated
|
|
85
|
+
|
|
86
|
+
def deterministic(deterministic)
|
|
87
|
+
with_option(:deterministic, deterministic)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def downcase(downcase)
|
|
91
|
+
with_option(:downcase, downcase)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def ignore_case(ignore_case)
|
|
95
|
+
with_option(:ignore_case, ignore_case)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def matches?(subject)
|
|
99
|
+
@subject = subject
|
|
100
|
+
result = encrypted_attributes_included? &&
|
|
101
|
+
options_correct?(
|
|
102
|
+
:deterministic,
|
|
103
|
+
:downcase,
|
|
104
|
+
:ignore_case,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if result
|
|
108
|
+
@failure_message_when_negated = "Did not expect to #{description} of #{class_name}"
|
|
109
|
+
if @options.present?
|
|
110
|
+
@failure_message_when_negated += "
|
|
111
|
+
using "
|
|
112
|
+
@failure_message_when_negated += @options.map { |opt, expected|
|
|
113
|
+
":#{opt} option as ‹#{expected}›"
|
|
114
|
+
}.join(' and
|
|
115
|
+
')
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
@failure_message_when_negated += ",
|
|
119
|
+
but it did"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def description
|
|
126
|
+
"encrypt :#{@attribute}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def encrypted_attributes_included?
|
|
132
|
+
if encrypted_attributes.include?(@attribute)
|
|
133
|
+
true
|
|
134
|
+
else
|
|
135
|
+
@failure_message = "Expected to #{description} of #{class_name}, but it did not"
|
|
136
|
+
false
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def with_option(option_name, value)
|
|
141
|
+
@options[option_name] = value
|
|
142
|
+
self
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def options_correct?(*opts)
|
|
146
|
+
opts.all? do |opt|
|
|
147
|
+
next true unless @options.key?(opt)
|
|
148
|
+
|
|
149
|
+
expected = @options[opt]
|
|
150
|
+
actual = encrypted_attribute_scheme.send("#{opt}?")
|
|
151
|
+
next true if expected == actual
|
|
152
|
+
|
|
153
|
+
@failure_message = "Expected to #{description} of #{class_name} using :#{opt} option
|
|
154
|
+
as ‹#{expected}›, but got ‹#{actual}›"
|
|
155
|
+
|
|
156
|
+
false
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def encrypted_attributes
|
|
161
|
+
@_encrypted_attributes ||= @subject.class.encrypted_attributes || []
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def encrypted_attribute_scheme
|
|
165
|
+
@subject.class.type_for_attribute(@attribute).scheme
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def class_name
|
|
169
|
+
@subject.class.name
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
module Shoulda
|
|
2
|
+
module Matchers
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# The `have_one_attached` matcher tests usage of the
|
|
5
|
+
# `has_one_attached` macro.
|
|
6
|
+
#
|
|
7
|
+
# #### Example
|
|
8
|
+
#
|
|
9
|
+
# class User < ApplicationRecord
|
|
10
|
+
# has_one_attached :avatar
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# # RSpec
|
|
14
|
+
# RSpec.describe User, type: :model do
|
|
15
|
+
# it { should have_one_attached(:avatar) }
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # Minitest (Shoulda)
|
|
19
|
+
# class UserTest < ActiveSupport::TestCase
|
|
20
|
+
# should have_one_attached(:avatar)
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# #### Qualifiers
|
|
24
|
+
#
|
|
25
|
+
# ##### service
|
|
26
|
+
#
|
|
27
|
+
# Use `service` to assert that the `:service` option was specified.
|
|
28
|
+
#
|
|
29
|
+
# class User < ApplicationRecord
|
|
30
|
+
# has_one_attached :avatar, service: :s3
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # RSpec
|
|
34
|
+
# RSpec.describe User, type: :model do
|
|
35
|
+
# it { should have_one_attached(:avatar).service(:s3) }
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# # Minitest (Shoulda)
|
|
39
|
+
# class UserTest < ActiveSupport::TestCase
|
|
40
|
+
# should have_one_attached(:avatar).service(:s3)
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# ##### dependent
|
|
44
|
+
#
|
|
45
|
+
# Use `dependent` to assert that the `:dependent` option was specified.
|
|
46
|
+
#
|
|
47
|
+
# class User < ApplicationRecord
|
|
48
|
+
# has_one_attached :avatar, dependent: :destroy
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
# # RSpec
|
|
52
|
+
# RSpec.describe User, type: :model do
|
|
53
|
+
# it { should have_one_attached(:avatar).dependent(:destroy) }
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# # Minitest (Shoulda)
|
|
57
|
+
# class UserTest < ActiveSupport::TestCase
|
|
58
|
+
# should have_one_attached(:avatar).dependent(:destroy)
|
|
59
|
+
# end
|
|
60
|
+
#
|
|
61
|
+
# ##### strict_loading
|
|
62
|
+
#
|
|
63
|
+
# Use `strict_loading` to assert that the `:strict_loading` option was specified.
|
|
64
|
+
#
|
|
65
|
+
# class User < ApplicationRecord
|
|
66
|
+
# has_one_attached :avatar, strict_loading: true
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# # RSpec
|
|
70
|
+
# RSpec.describe User, type: :model do
|
|
71
|
+
# it { should have_one_attached(:avatar).strict_loading(true) }
|
|
72
|
+
# end
|
|
73
|
+
#
|
|
74
|
+
# # Minitest (Shoulda)
|
|
75
|
+
# class UserTest < ActiveSupport::TestCase
|
|
76
|
+
# should have_one_attached(:avatar).strict_loading(true)
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# Default value is true when no argument is specified:
|
|
80
|
+
#
|
|
81
|
+
# # RSpec
|
|
82
|
+
# RSpec.describe User, type: :model do
|
|
83
|
+
# it { should have_one_attached(:avatar).strict_loading }
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# # Minitest (Shoulda)
|
|
87
|
+
# class UserTest < ActiveSupport::TestCase
|
|
88
|
+
# should have_one_attached(:avatar).strict_loading
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# #### Multiple attributes
|
|
92
|
+
#
|
|
93
|
+
# You can pass multiple attributes to assert that each one has the
|
|
94
|
+
# validation. Any qualifier chained on the matcher is applied to
|
|
95
|
+
# every attribute uniformly.
|
|
96
|
+
#
|
|
97
|
+
# class User < ActiveRecord::Base
|
|
98
|
+
# has_one_attached :avatar
|
|
99
|
+
# has_one_attached :cover
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# # RSpec
|
|
103
|
+
# RSpec.describe User, type: :model do
|
|
104
|
+
# it { should have_one_attached(:avatar, :cover) }
|
|
105
|
+
# end
|
|
106
|
+
#
|
|
107
|
+
# # Minitest (Shoulda)
|
|
108
|
+
# class UserTest < ActiveSupport::TestCase
|
|
109
|
+
# should have_one_attached(:avatar, :cover)
|
|
110
|
+
# end
|
|
111
|
+
#
|
|
112
|
+
# @return [HaveAttachedMatcher]
|
|
113
|
+
#
|
|
114
|
+
def have_one_attached(*names)
|
|
115
|
+
MatcherCollection.build(names) { |name| HaveAttachedMatcher.new(:one, name) }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The `have_many_attached` matcher tests usage of the
|
|
119
|
+
# `has_many_attached` macro.
|
|
120
|
+
#
|
|
121
|
+
# #### Example
|
|
122
|
+
#
|
|
123
|
+
# class Message < ApplicationRecord
|
|
124
|
+
# has_many_attached :images
|
|
125
|
+
# end
|
|
126
|
+
#
|
|
127
|
+
# # RSpec
|
|
128
|
+
# RSpec.describe Message, type: :model do
|
|
129
|
+
# it { should have_many_attached(:images) }
|
|
130
|
+
# end
|
|
131
|
+
#
|
|
132
|
+
# # Minitest (Shoulda)
|
|
133
|
+
# class MessageTest < ActiveSupport::TestCase
|
|
134
|
+
# should have_many_attached(:images)
|
|
135
|
+
# end
|
|
136
|
+
#
|
|
137
|
+
# @return [HaveAttachedMatcher]
|
|
138
|
+
#
|
|
139
|
+
def have_many_attached(name)
|
|
140
|
+
HaveAttachedMatcher.new(:many, name)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @private
|
|
144
|
+
class HaveAttachedMatcher
|
|
145
|
+
attr_reader :name, :options
|
|
146
|
+
|
|
147
|
+
def initialize(macro, name)
|
|
148
|
+
@macro = macro
|
|
149
|
+
@name = name
|
|
150
|
+
@options = {}
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def description
|
|
154
|
+
"have a has_#{macro}_attached called #{name}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def failure_message
|
|
158
|
+
<<-MESSAGE
|
|
159
|
+
Expected #{expectation}, but this could not be proved.
|
|
160
|
+
#{@failure}
|
|
161
|
+
MESSAGE
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def failure_reason
|
|
165
|
+
@failure
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def failure_message_when_negated
|
|
169
|
+
<<-MESSAGE
|
|
170
|
+
Did not expect #{expectation}, but it does.
|
|
171
|
+
MESSAGE
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def expectation
|
|
175
|
+
"#{model_class.name} to #{description}" + build_expectation_suffix
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def matches?(subject)
|
|
179
|
+
@subject = subject
|
|
180
|
+
reader_attribute_exists? &&
|
|
181
|
+
writer_attribute_exists? &&
|
|
182
|
+
attachments_association_exists? &&
|
|
183
|
+
blobs_association_exists? &&
|
|
184
|
+
eager_loading_scope_exists? &&
|
|
185
|
+
service_correct? &&
|
|
186
|
+
dependent_option_correct?
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
OPTION_METHODS = {
|
|
190
|
+
service: -> (value) { value },
|
|
191
|
+
strict_loading: -> (value = true) { value },
|
|
192
|
+
dependent: -> (value) { value },
|
|
193
|
+
}.freeze
|
|
194
|
+
|
|
195
|
+
OPTION_METHODS.each do |option_name, value_processor|
|
|
196
|
+
define_method(option_name) do |*args|
|
|
197
|
+
@options[option_name] = value_processor.call(*args)
|
|
198
|
+
self
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
|
|
204
|
+
attr_reader :subject, :macro
|
|
205
|
+
|
|
206
|
+
def build_expectation_suffix
|
|
207
|
+
String.new.tap do |suffix|
|
|
208
|
+
suffix << " with service :#{options[:service]}" if options.key?(:service)
|
|
209
|
+
|
|
210
|
+
if options.key?(:strict_loading)
|
|
211
|
+
suffix << " with strict_loading option set to #{options[:strict_loading]}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
if options.key?(:dependent)
|
|
215
|
+
suffix << " with dependent option set to :#{options[:dependent]}"
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def reader_attribute_exists?
|
|
221
|
+
if subject.respond_to?(name)
|
|
222
|
+
true
|
|
223
|
+
else
|
|
224
|
+
@failure = "#{model_class.name} does not have a :#{name} method."
|
|
225
|
+
false
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def writer_attribute_exists?
|
|
230
|
+
if subject.respond_to?("#{name}=")
|
|
231
|
+
true
|
|
232
|
+
else
|
|
233
|
+
@failure = "#{model_class.name} does not have a :#{name}= method."
|
|
234
|
+
false
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def attachments_association_exists?
|
|
239
|
+
if attachments_association_matcher.matches?(subject)
|
|
240
|
+
true
|
|
241
|
+
else
|
|
242
|
+
@failure = attachments_association_matcher.failure_message
|
|
243
|
+
false
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def attachments_association_matcher
|
|
248
|
+
@_attachments_association_matcher ||=
|
|
249
|
+
AssociationMatcher.new(
|
|
250
|
+
:"has_#{macro}",
|
|
251
|
+
attachments_association_name,
|
|
252
|
+
).
|
|
253
|
+
conditions(name:).
|
|
254
|
+
class_name('ActiveStorage::Attachment').
|
|
255
|
+
inverse_of(:record)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def attachments_association_name
|
|
259
|
+
case macro
|
|
260
|
+
when :one then "#{name}_attachment"
|
|
261
|
+
when :many then "#{name}_attachments"
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def blobs_association_exists?
|
|
266
|
+
if blobs_association_matcher.matches?(subject)
|
|
267
|
+
true
|
|
268
|
+
else
|
|
269
|
+
@failure = blobs_association_matcher.failure_message
|
|
270
|
+
false
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def blobs_association_matcher
|
|
275
|
+
@_blobs_association_matcher ||=
|
|
276
|
+
AssociationMatcher.new(
|
|
277
|
+
:"has_#{macro}",
|
|
278
|
+
blobs_association_name,
|
|
279
|
+
).
|
|
280
|
+
through(attachments_association_name).
|
|
281
|
+
class_name('ActiveStorage::Blob').
|
|
282
|
+
strict_loading(options[:strict_loading]).
|
|
283
|
+
source(:blob)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def blobs_association_name
|
|
287
|
+
case macro
|
|
288
|
+
when :one then "#{name}_blob"
|
|
289
|
+
when :many then "#{name}_blobs"
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def eager_loading_scope_exists?
|
|
294
|
+
if model_class.respond_to?("with_attached_#{name}")
|
|
295
|
+
true
|
|
296
|
+
else
|
|
297
|
+
@failure = "#{model_class.name} does not have a " \
|
|
298
|
+
":with_attached_#{name} scope."
|
|
299
|
+
false
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def model_class
|
|
304
|
+
subject.class
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def dependent_option_correct?
|
|
308
|
+
dependent = @options[:dependent]
|
|
309
|
+
return true if dependent.nil? || dependent_option == dependent
|
|
310
|
+
|
|
311
|
+
@failure = 'The dependent option for the association called ' \
|
|
312
|
+
"#{attachments_association_name} is incorrect " \
|
|
313
|
+
"(expected: :#{dependent}, " \
|
|
314
|
+
"actual: :#{dependent_option})"
|
|
315
|
+
false
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def service_correct?
|
|
319
|
+
service = @options[:service]
|
|
320
|
+
return true if service.nil? || service_name == service
|
|
321
|
+
|
|
322
|
+
@failure = 'The service for the association called ' \
|
|
323
|
+
"#{attachments_association_name} is incorrect " \
|
|
324
|
+
"(expected: :#{service}, " \
|
|
325
|
+
"actual: :#{service_name})"
|
|
326
|
+
false
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def attachment_reflection
|
|
330
|
+
model_class.attachment_reflections[name.to_s]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def service_name
|
|
334
|
+
attachment_reflection.options[:service_name]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def dependent_option
|
|
338
|
+
attachment_reflection.options[:dependent]
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
end
|