shoulda-matchers 4.1.1 → 4.4.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 +4 -4
- data/README.md +163 -79
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +5 -1
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +2 -21
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +27 -3
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +32 -0
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +6 -9
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +25 -0
- data/lib/shoulda/matchers/active_record.rb +3 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +23 -7
- data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +2 -2
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +12 -6
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +20 -3
- data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +147 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
- data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +79 -0
- data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +28 -9
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +2 -6
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +2 -2
- data/lib/shoulda/matchers/independent.rb +0 -1
- data/lib/shoulda/matchers/rails_shim.rb +42 -44
- data/lib/shoulda/matchers/util.rb +9 -2
- data/lib/shoulda/matchers/util/word_wrap.rb +1 -1
- data/lib/shoulda/matchers/version.rb +1 -1
- metadata +9 -8
- data/MIT-LICENSE +0 -22
- data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
@@ -311,15 +311,6 @@ EOT
|
|
311
311
|
self
|
312
312
|
end
|
313
313
|
|
314
|
-
def allow_blank
|
315
|
-
@options[:allow_blank] = true
|
316
|
-
self
|
317
|
-
end
|
318
|
-
|
319
|
-
def expects_to_allow_blank?
|
320
|
-
@options[:allow_blank]
|
321
|
-
end
|
322
|
-
|
323
314
|
def allow_nil
|
324
315
|
@options[:allow_nil] = true
|
325
316
|
self
|
@@ -423,14 +414,14 @@ EOT
|
|
423
414
|
allows_all_values_in_array? &&
|
424
415
|
disallows_all_values_outside_of_array? &&
|
425
416
|
allows_nil_value? &&
|
426
|
-
|
417
|
+
allow_blank_matches?
|
427
418
|
end
|
428
419
|
|
429
420
|
def does_not_match_for_array?
|
430
421
|
disallows_any_values_in_array? ||
|
431
422
|
allows_any_value_outside_of_array? ||
|
432
423
|
disallows_nil_value? ||
|
433
|
-
|
424
|
+
allow_blank_does_not_match?
|
434
425
|
end
|
435
426
|
|
436
427
|
def allows_lower_value
|
@@ -616,16 +607,6 @@ EOT
|
|
616
607
|
@options[:allow_nil] && disallows_value_of(nil)
|
617
608
|
end
|
618
609
|
|
619
|
-
def allows_blank_value?
|
620
|
-
@options[:allow_blank] != true ||
|
621
|
-
BLANK_VALUES.all? { |value| allows_value_of(value) }
|
622
|
-
end
|
623
|
-
|
624
|
-
def disallows_blank_value?
|
625
|
-
@options[:allow_blank] &&
|
626
|
-
BLANK_VALUES.any? { |value| disallows_value_of(value) }
|
627
|
-
end
|
628
|
-
|
629
610
|
def inspected_array
|
630
611
|
Shoulda::Matchers::Util.inspect_values(@array).to_sentence(
|
631
612
|
two_words_connector: " or ",
|
@@ -232,13 +232,34 @@ module Shoulda
|
|
232
232
|
# it { should validate_length_of(:bio).is_at_least(15).allow_nil }
|
233
233
|
# end
|
234
234
|
#
|
235
|
-
# #
|
235
|
+
# # Minitest (Shoulda)
|
236
236
|
# class UserTest < ActiveSupport::TestCase
|
237
237
|
# should validate_length_of(:bio).is_at_least(15).allow_nil
|
238
238
|
# end
|
239
239
|
#
|
240
240
|
# @return [ValidateLengthOfMatcher]
|
241
241
|
#
|
242
|
+
# # ##### allow_blank
|
243
|
+
#
|
244
|
+
# Use `allow_blank` to assert that the attribute allows blank.
|
245
|
+
#
|
246
|
+
# class User
|
247
|
+
# include ActiveModel::Model
|
248
|
+
# attr_accessor :bio
|
249
|
+
#
|
250
|
+
# validates_length_of :bio, minimum: 15, allow_blank: true
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# # RSpec
|
254
|
+
# describe User do
|
255
|
+
# it { should validate_length_of(:bio).is_at_least(15).allow_blank }
|
256
|
+
# end
|
257
|
+
#
|
258
|
+
# # Minitest (Shoulda)
|
259
|
+
# class UserTest < ActiveSupport::TestCase
|
260
|
+
# should validate_length_of(:bio).is_at_least(15).allow_blank
|
261
|
+
# end
|
262
|
+
#
|
242
263
|
def validate_length_of(attr)
|
243
264
|
ValidateLengthOfMatcher.new(attr)
|
244
265
|
end
|
@@ -331,7 +352,8 @@ module Shoulda
|
|
331
352
|
|
332
353
|
lower_bound_matches? &&
|
333
354
|
upper_bound_matches? &&
|
334
|
-
allow_nil_matches?
|
355
|
+
allow_nil_matches? &&
|
356
|
+
allow_blank_matches?
|
335
357
|
end
|
336
358
|
|
337
359
|
def does_not_match?(subject)
|
@@ -339,7 +361,8 @@ module Shoulda
|
|
339
361
|
|
340
362
|
lower_bound_does_not_match? ||
|
341
363
|
upper_bound_does_not_match? ||
|
342
|
-
allow_nil_does_not_match?
|
364
|
+
allow_nil_does_not_match? ||
|
365
|
+
allow_blank_does_not_match?
|
343
366
|
end
|
344
367
|
|
345
368
|
private
|
@@ -376,6 +399,7 @@ module Shoulda
|
|
376
399
|
def disallows_lower_length?
|
377
400
|
!@options.key?(:minimum) ||
|
378
401
|
@options[:minimum] == 0 ||
|
402
|
+
(@options[:minimum] == 1 && expects_to_allow_blank?) ||
|
379
403
|
disallows_length_of?(
|
380
404
|
@options[:minimum] - 1,
|
381
405
|
translated_short_message
|
@@ -204,6 +204,33 @@ module Shoulda
|
|
204
204
|
# is_greater_than(21)
|
205
205
|
# end
|
206
206
|
#
|
207
|
+
# ##### is_other_than
|
208
|
+
#
|
209
|
+
# Use `is_other_than` to test usage of the `:other_than` option.
|
210
|
+
# This asserts that the attribute can take a number which is not equal to
|
211
|
+
# the given value.
|
212
|
+
#
|
213
|
+
# class Person
|
214
|
+
# include ActiveModel::Model
|
215
|
+
# attr_accessor :legal_age
|
216
|
+
#
|
217
|
+
# validates_numericality_of :legal_age, other_than: 21
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# # RSpec
|
221
|
+
# RSpec.describe Person, type: :model do
|
222
|
+
# it do
|
223
|
+
# should validate_numericality_of(:legal_age).
|
224
|
+
# is_other_than(21)
|
225
|
+
# end
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# # Minitest (Shoulda)
|
229
|
+
# class PersonTest < ActiveSupport::TestCase
|
230
|
+
# should validate_numericality_of(:legal_age).
|
231
|
+
# is_other_than(21)
|
232
|
+
# end
|
233
|
+
#
|
207
234
|
# ##### even
|
208
235
|
#
|
209
236
|
# Use `even` to test usage of the `:even` option. This asserts that the
|
@@ -395,6 +422,11 @@ module Shoulda
|
|
395
422
|
self
|
396
423
|
end
|
397
424
|
|
425
|
+
def is_other_than(value)
|
426
|
+
prepare_submatcher(comparison_matcher_for(value, :!=).for(@attribute))
|
427
|
+
self
|
428
|
+
end
|
429
|
+
|
398
430
|
def with_message(message)
|
399
431
|
@expects_custom_validation_message = true
|
400
432
|
@expected_message = message
|
@@ -314,10 +314,11 @@ validation for you? Instead of using `validate_presence_of`, try
|
|
314
314
|
def attribute_accepts_string_values?
|
315
315
|
if association?
|
316
316
|
false
|
317
|
-
elsif
|
318
|
-
|
317
|
+
elsif attribute_serialization_coder.respond_to?(:object_class)
|
318
|
+
attribute_serialization_coder.object_class == String
|
319
319
|
else
|
320
|
-
|
320
|
+
RailsShim.supports_full_attributes_api?(model) &&
|
321
|
+
attribute_type.try(:type) == :string
|
321
322
|
end
|
322
323
|
end
|
323
324
|
|
@@ -355,12 +356,8 @@ validation for you? Instead of using `validate_presence_of`, try
|
|
355
356
|
end
|
356
357
|
end
|
357
358
|
|
358
|
-
def
|
359
|
-
|
360
|
-
attribute_type.coder
|
361
|
-
else
|
362
|
-
nil
|
363
|
-
end
|
359
|
+
def attribute_serialization_coder
|
360
|
+
RailsShim.attribute_serialization_coder_for(model, @attribute)
|
364
361
|
end
|
365
362
|
|
366
363
|
def attribute_type
|
@@ -24,6 +24,11 @@ module Shoulda
|
|
24
24
|
self
|
25
25
|
end
|
26
26
|
|
27
|
+
def allow_blank
|
28
|
+
options[:allow_blank] = true
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
27
32
|
def strict
|
28
33
|
@expects_strict = true
|
29
34
|
self
|
@@ -116,8 +121,20 @@ module Shoulda
|
|
116
121
|
)
|
117
122
|
end
|
118
123
|
|
124
|
+
def allow_blank_matches?
|
125
|
+
!expects_to_allow_blank? ||
|
126
|
+
blank_values.all? { |value| allows_value_of(value) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def allow_blank_does_not_match?
|
130
|
+
expects_to_allow_blank? &&
|
131
|
+
blank_values.all? { |value| disallows_value_of(value) }
|
132
|
+
end
|
133
|
+
|
119
134
|
private
|
120
135
|
|
136
|
+
attr_reader :options
|
137
|
+
|
121
138
|
def overall_failure_message
|
122
139
|
Shoulda::Matchers.word_wrap(
|
123
140
|
"Expected #{model.name} to #{description}, but this could not be " +
|
@@ -161,6 +178,14 @@ module Shoulda
|
|
161
178
|
@last_submatcher_run = matcher
|
162
179
|
matcher.matches?(subject)
|
163
180
|
end
|
181
|
+
|
182
|
+
def expects_to_allow_blank?
|
183
|
+
options[:allow_blank]
|
184
|
+
end
|
185
|
+
|
186
|
+
def blank_values
|
187
|
+
['', ' ', "\n", "\r", "\t", "\f"]
|
188
|
+
end
|
164
189
|
end
|
165
190
|
end
|
166
191
|
end
|
@@ -14,13 +14,16 @@ require "shoulda/matchers/active_record/association_matchers/model_reflection"
|
|
14
14
|
require "shoulda/matchers/active_record/association_matchers/option_verifier"
|
15
15
|
require "shoulda/matchers/active_record/have_db_column_matcher"
|
16
16
|
require "shoulda/matchers/active_record/have_db_index_matcher"
|
17
|
+
require "shoulda/matchers/active_record/have_implicit_order_column"
|
17
18
|
require "shoulda/matchers/active_record/have_readonly_attribute_matcher"
|
19
|
+
require "shoulda/matchers/active_record/have_rich_text_matcher"
|
18
20
|
require "shoulda/matchers/active_record/have_secure_token_matcher"
|
19
21
|
require "shoulda/matchers/active_record/serialize_matcher"
|
20
22
|
require "shoulda/matchers/active_record/accept_nested_attributes_for_matcher"
|
21
23
|
require "shoulda/matchers/active_record/define_enum_for_matcher"
|
22
24
|
require "shoulda/matchers/active_record/uniqueness"
|
23
25
|
require "shoulda/matchers/active_record/validate_uniqueness_of_matcher"
|
26
|
+
require "shoulda/matchers/active_record/have_attached_matcher"
|
24
27
|
|
25
28
|
module Shoulda
|
26
29
|
module Matchers
|
@@ -920,21 +920,21 @@ module Shoulda
|
|
920
920
|
# asserts that the table you're referring to actually exists.
|
921
921
|
#
|
922
922
|
# class Person < ActiveRecord::Base
|
923
|
-
# has_and_belongs_to_many :issues, join_table:
|
923
|
+
# has_and_belongs_to_many :issues, join_table: :people_tickets
|
924
924
|
# end
|
925
925
|
#
|
926
926
|
# # RSpec
|
927
927
|
# RSpec.describe Person, type: :model do
|
928
928
|
# it do
|
929
929
|
# should have_and_belong_to_many(:issues).
|
930
|
-
# join_table(
|
930
|
+
# join_table(:people_tickets)
|
931
931
|
# end
|
932
932
|
# end
|
933
933
|
#
|
934
934
|
# # Minitest (Shoulda)
|
935
935
|
# class PersonTest < ActiveSupport::TestCase
|
936
936
|
# should have_and_belong_to_many(:issues).
|
937
|
-
# join_table(
|
937
|
+
# join_table(:people_tickets)
|
938
938
|
# end
|
939
939
|
#
|
940
940
|
# ##### validate
|
@@ -1096,12 +1096,12 @@ module Shoulda
|
|
1096
1096
|
self
|
1097
1097
|
end
|
1098
1098
|
|
1099
|
-
def optional
|
1099
|
+
def optional(optional = true)
|
1100
1100
|
remove_submatcher(AssociationMatchers::RequiredMatcher)
|
1101
1101
|
add_submatcher(
|
1102
1102
|
AssociationMatchers::OptionalMatcher,
|
1103
1103
|
name,
|
1104
|
-
|
1104
|
+
optional,
|
1105
1105
|
)
|
1106
1106
|
self
|
1107
1107
|
end
|
@@ -1144,6 +1144,7 @@ module Shoulda
|
|
1144
1144
|
@subject = subject
|
1145
1145
|
association_exists? &&
|
1146
1146
|
macro_correct? &&
|
1147
|
+
validate_inverse_of_through_association &&
|
1147
1148
|
(polymorphic? || class_exists?) &&
|
1148
1149
|
foreign_key_exists? &&
|
1149
1150
|
primary_key_exists? &&
|
@@ -1198,7 +1199,14 @@ module Shoulda
|
|
1198
1199
|
end
|
1199
1200
|
|
1200
1201
|
def expectation
|
1201
|
-
|
1202
|
+
expectation =
|
1203
|
+
"#{model_class.name} to have a #{macro} association called #{name}"
|
1204
|
+
|
1205
|
+
if through?
|
1206
|
+
expectation << " through #{reflector.has_and_belongs_to_many_name}"
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
expectation
|
1202
1210
|
end
|
1203
1211
|
|
1204
1212
|
def missing_options
|
@@ -1241,6 +1249,14 @@ module Shoulda
|
|
1241
1249
|
end
|
1242
1250
|
end
|
1243
1251
|
|
1252
|
+
def validate_inverse_of_through_association
|
1253
|
+
reflector.validate_inverse_of_through_association!
|
1254
|
+
true
|
1255
|
+
rescue ::ActiveRecord::ActiveRecordError => error
|
1256
|
+
@missing = error.message
|
1257
|
+
false
|
1258
|
+
end
|
1259
|
+
|
1244
1260
|
def macro_supports_primary_key?
|
1245
1261
|
macro == :belongs_to ||
|
1246
1262
|
([:has_many, :has_one].include?(macro) && !through?)
|
@@ -1394,7 +1410,7 @@ module Shoulda
|
|
1394
1410
|
end
|
1395
1411
|
|
1396
1412
|
def foreign_key_reflection
|
1397
|
-
if [:has_one, :has_many].include?(macro) && reflection.options.include?(:inverse_of)
|
1413
|
+
if [:has_one, :has_many].include?(macro) && reflection.options.include?(:inverse_of) && reflection.options[:inverse_of] != false
|
1398
1414
|
associated_class.reflect_on_association(reflection.options[:inverse_of])
|
1399
1415
|
else
|
1400
1416
|
reflection
|
@@ -29,7 +29,7 @@ module Shoulda
|
|
29
29
|
if option_verifier.correct_for_string?(:join_table, options[:join_table_name])
|
30
30
|
true
|
31
31
|
else
|
32
|
-
@failure_message = "#{name} should use
|
32
|
+
@failure_message = "#{name} should use #{options[:join_table_name].inspect} for :join_table option"
|
33
33
|
false
|
34
34
|
end
|
35
35
|
else
|
@@ -38,7 +38,7 @@ module Shoulda
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def join_table_exists?
|
41
|
-
if RailsShim.tables_and_views(connection).include?(join_table_name)
|
41
|
+
if RailsShim.tables_and_views(connection).include?(join_table_name.to_s)
|
42
42
|
true
|
43
43
|
else
|
44
44
|
@failure_message = missing_table_message
|
@@ -35,12 +35,12 @@ module Shoulda
|
|
35
35
|
join_table_name.to_s
|
36
36
|
end
|
37
37
|
|
38
|
-
def association_relation
|
38
|
+
def association_relation(related_instance)
|
39
39
|
relation = associated_class.all
|
40
40
|
|
41
41
|
if reflection.scope
|
42
42
|
# Source: AR::Associations::AssociationScope#eval_scope
|
43
|
-
relation.instance_exec(
|
43
|
+
relation.instance_exec(related_instance, &reflection.scope)
|
44
44
|
else
|
45
45
|
relation
|
46
46
|
end
|
@@ -65,16 +65,22 @@ module Shoulda
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
def validate_inverse_of_through_association!
|
69
|
+
if through?
|
70
|
+
reflection.check_validity!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_and_belongs_to_many_name
|
75
|
+
reflection.options[:through]
|
76
|
+
end
|
77
|
+
|
68
78
|
protected
|
69
79
|
|
70
80
|
attr_reader :reflection, :subject
|
71
81
|
|
72
82
|
private
|
73
83
|
|
74
|
-
def has_and_belongs_to_many_name
|
75
|
-
reflection.options[:through]
|
76
|
-
end
|
77
|
-
|
78
84
|
def has_and_belongs_to_many_name_table_name
|
79
85
|
if has_and_belongs_to_many_reflection
|
80
86
|
has_and_belongs_to_many_reflection.table_name
|
@@ -4,15 +4,32 @@ module Shoulda
|
|
4
4
|
module AssociationMatchers
|
5
5
|
# @private
|
6
6
|
class ModelReflector
|
7
|
-
delegate
|
8
|
-
:
|
9
|
-
:association_foreign_key,
|
7
|
+
delegate(
|
8
|
+
:associated_class,
|
9
|
+
:association_foreign_key,
|
10
|
+
:foreign_key,
|
11
|
+
:has_and_belongs_to_many_name,
|
12
|
+
:join_table_name,
|
13
|
+
:polymorphic?,
|
14
|
+
:validate_inverse_of_through_association!,
|
15
|
+
to: :reflection
|
16
|
+
)
|
17
|
+
|
18
|
+
delegate(
|
19
|
+
:through?,
|
20
|
+
to: :reflection,
|
21
|
+
allow_nil: true
|
22
|
+
)
|
10
23
|
|
11
24
|
def initialize(subject, name)
|
12
25
|
@subject = subject
|
13
26
|
@name = name
|
14
27
|
end
|
15
28
|
|
29
|
+
def association_relation
|
30
|
+
reflection.association_relation(subject)
|
31
|
+
end
|
32
|
+
|
16
33
|
def reflection
|
17
34
|
@reflection ||= reflect_on_association(name)
|
18
35
|
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord
|
4
|
+
def have_one_attached(name)
|
5
|
+
HaveAttachedMatcher.new(:one, name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def have_many_attached(name)
|
9
|
+
HaveAttachedMatcher.new(:many, name)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @private
|
13
|
+
class HaveAttachedMatcher
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
def initialize(macro, name)
|
17
|
+
@macro = macro
|
18
|
+
@name = name
|
19
|
+
end
|
20
|
+
|
21
|
+
def description
|
22
|
+
"have a has_#{macro}_attached called #{name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def failure_message
|
26
|
+
<<-MESSAGE
|
27
|
+
Expected #{expectation}, but this could not be proved.
|
28
|
+
#{@failure}
|
29
|
+
MESSAGE
|
30
|
+
end
|
31
|
+
|
32
|
+
def failure_message_when_negated
|
33
|
+
<<-MESSAGE
|
34
|
+
Did not expect #{expectation}, but it does.
|
35
|
+
MESSAGE
|
36
|
+
end
|
37
|
+
|
38
|
+
def expectation
|
39
|
+
"#{model_class.name} to #{description}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def matches?(subject)
|
43
|
+
@subject = subject
|
44
|
+
reader_attribute_exists? &&
|
45
|
+
writer_attribute_exists? &&
|
46
|
+
attachments_association_exists? &&
|
47
|
+
blobs_association_exists? &&
|
48
|
+
eager_loading_scope_exists?
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :subject, :macro
|
54
|
+
|
55
|
+
def reader_attribute_exists?
|
56
|
+
if subject.respond_to?(name)
|
57
|
+
true
|
58
|
+
else
|
59
|
+
@failure = "#{model_class.name} does not have a :#{name} method."
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def writer_attribute_exists?
|
65
|
+
if subject.respond_to?("#{name}=")
|
66
|
+
true
|
67
|
+
else
|
68
|
+
@failure = "#{model_class.name} does not have a :#{name}= method."
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def attachments_association_exists?
|
74
|
+
if attachments_association_matcher.matches?(subject)
|
75
|
+
true
|
76
|
+
else
|
77
|
+
@failure = attachments_association_matcher.failure_message
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def attachments_association_matcher
|
83
|
+
@_attachments_association_matcher ||=
|
84
|
+
AssociationMatcher.new(
|
85
|
+
:"has_#{macro}",
|
86
|
+
attachments_association_name,
|
87
|
+
).
|
88
|
+
conditions(name: name).
|
89
|
+
class_name('ActiveStorage::Attachment').
|
90
|
+
inverse_of(:record)
|
91
|
+
end
|
92
|
+
|
93
|
+
def attachments_association_name
|
94
|
+
case macro
|
95
|
+
when :one then
|
96
|
+
"#{name}_attachment"
|
97
|
+
when :many then
|
98
|
+
"#{name}_attachments"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def blobs_association_exists?
|
103
|
+
if blobs_association_matcher.matches?(subject)
|
104
|
+
true
|
105
|
+
else
|
106
|
+
@failure = blobs_association_matcher.failure_message
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def blobs_association_matcher
|
112
|
+
@_blobs_association_matcher ||=
|
113
|
+
AssociationMatcher.new(
|
114
|
+
:"has_#{macro}",
|
115
|
+
blobs_association_name,
|
116
|
+
).
|
117
|
+
through(attachments_association_name).
|
118
|
+
class_name('ActiveStorage::Blob').
|
119
|
+
source(:blob)
|
120
|
+
end
|
121
|
+
|
122
|
+
def blobs_association_name
|
123
|
+
case macro
|
124
|
+
when :one then
|
125
|
+
"#{name}_blob"
|
126
|
+
when :many then
|
127
|
+
"#{name}_blobs"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def eager_loading_scope_exists?
|
132
|
+
if model_class.respond_to?("with_attached_#{name}")
|
133
|
+
true
|
134
|
+
else
|
135
|
+
@failure = "#{model_class.name} does not have a " \
|
136
|
+
":with_attached_#{name} scope."
|
137
|
+
false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def model_class
|
142
|
+
subject.class
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|