shrine 2.19.4 → 3.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -11
- data/README.md +9 -3
- data/doc/advantages.md +1 -1
- data/doc/carrierwave.md +4 -4
- data/doc/creating_persistence_plugins.md +172 -0
- data/doc/creating_plugins.md +1 -1
- data/doc/creating_storages.md +3 -1
- data/doc/design.md +2 -2
- data/doc/direct_s3.md +0 -22
- data/doc/paperclip.md +3 -3
- data/doc/plugins/activerecord.md +211 -42
- data/doc/plugins/atomic_helpers.md +153 -0
- data/doc/plugins/column.md +90 -0
- data/doc/plugins/derivation_endpoint.md +54 -62
- data/doc/plugins/derivatives.md +752 -0
- data/doc/plugins/entity.md +204 -0
- data/doc/plugins/infer_extension.md +8 -8
- data/doc/plugins/instrumentation.md +33 -13
- data/doc/plugins/keep_files.md +5 -15
- data/doc/plugins/model.md +157 -0
- data/doc/plugins/presign_endpoint.md +2 -1
- data/doc/plugins/refresh_metadata.md +44 -7
- data/doc/plugins/sequel.md +190 -33
- data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
- data/doc/processing.md +1 -1
- data/doc/release_notes/1.1.0.md +2 -2
- data/doc/release_notes/2.15.0.md +1 -1
- data/doc/storage/s3.md +2 -2
- data/doc/testing.md +1 -1
- data/lib/shrine.rb +72 -138
- data/lib/shrine/attacher.rb +272 -176
- data/lib/shrine/attachment.rb +2 -42
- data/lib/shrine/plugins/activerecord.rb +103 -26
- data/lib/shrine/plugins/add_metadata.rb +9 -10
- data/lib/shrine/plugins/atomic_helpers.rb +111 -0
- data/lib/shrine/plugins/attacher_options.rb +55 -0
- data/lib/shrine/plugins/backgrounding.rb +147 -115
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
- data/lib/shrine/plugins/column.rb +104 -0
- data/lib/shrine/plugins/data_uri.rb +35 -38
- data/lib/shrine/plugins/default_storage.rb +18 -12
- data/lib/shrine/plugins/default_url.rb +11 -21
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +9 -13
- data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
- data/lib/shrine/plugins/derivatives.rb +576 -0
- data/lib/shrine/plugins/determine_mime_type.rb +3 -15
- data/lib/shrine/plugins/download_endpoint.rb +83 -131
- data/lib/shrine/plugins/dynamic_storage.rb +4 -8
- data/lib/shrine/plugins/entity.rb +128 -0
- data/lib/shrine/plugins/form_assign.rb +107 -0
- data/lib/shrine/plugins/included.rb +4 -3
- data/lib/shrine/plugins/infer_extension.rb +10 -17
- data/lib/shrine/plugins/instrumentation.rb +45 -25
- data/lib/shrine/plugins/keep_files.rb +2 -12
- data/lib/shrine/plugins/metadata_attributes.rb +15 -14
- data/lib/shrine/plugins/model.rb +137 -0
- data/lib/shrine/plugins/module_include.rb +2 -0
- data/lib/shrine/plugins/presign_endpoint.rb +1 -15
- data/lib/shrine/plugins/pretty_location.rb +5 -5
- data/lib/shrine/plugins/processing.rb +21 -6
- data/lib/shrine/plugins/rack_file.rb +1 -39
- data/lib/shrine/plugins/rack_response.rb +14 -7
- data/lib/shrine/plugins/recache.rb +5 -2
- data/lib/shrine/plugins/refresh_metadata.rb +12 -8
- data/lib/shrine/plugins/remote_url.rb +44 -53
- data/lib/shrine/plugins/remove_attachment.rb +7 -2
- data/lib/shrine/plugins/remove_invalid.rb +8 -4
- data/lib/shrine/plugins/restore_cached_data.rb +12 -4
- data/lib/shrine/plugins/sequel.rb +115 -27
- data/lib/shrine/plugins/signature.rb +2 -7
- data/lib/shrine/plugins/store_dimensions.rb +13 -27
- data/lib/shrine/plugins/upload_endpoint.rb +14 -15
- data/lib/shrine/plugins/upload_options.rb +9 -8
- data/lib/shrine/plugins/url_options.rb +33 -0
- data/lib/shrine/plugins/validation.rb +87 -0
- data/lib/shrine/plugins/validation_helpers.rb +33 -54
- data/lib/shrine/plugins/versions.rb +106 -84
- data/lib/shrine/storage/file_system.rb +32 -57
- data/lib/shrine/storage/linter.rb +9 -1
- data/lib/shrine/storage/memory.rb +42 -0
- data/lib/shrine/storage/s3.rb +38 -146
- data/lib/shrine/uploaded_file.rb +22 -29
- data/lib/shrine/version.rb +4 -4
- data/shrine.gemspec +2 -3
- metadata +27 -54
- data/doc/plugins/backup.md +0 -31
- data/doc/plugins/copy.md +0 -24
- data/doc/plugins/delete_promoted.md +0 -12
- data/doc/plugins/direct_upload.md +0 -172
- data/doc/plugins/hooks.md +0 -58
- data/doc/plugins/logging.md +0 -42
- data/doc/plugins/migration_helpers.md +0 -60
- data/doc/plugins/moving.md +0 -19
- data/doc/plugins/multi_delete.md +0 -20
- data/doc/plugins/parallelize.md +0 -16
- data/doc/plugins/parsed_json.md +0 -23
- data/lib/shrine/plugins/background_helpers.rb +0 -5
- data/lib/shrine/plugins/backup.rb +0 -90
- data/lib/shrine/plugins/copy.rb +0 -50
- data/lib/shrine/plugins/delete_promoted.rb +0 -20
- data/lib/shrine/plugins/direct_upload.rb +0 -217
- data/lib/shrine/plugins/hooks.rb +0 -90
- data/lib/shrine/plugins/logging.rb +0 -142
- data/lib/shrine/plugins/migration_helpers.rb +0 -70
- data/lib/shrine/plugins/moving.rb +0 -57
- data/lib/shrine/plugins/multi_delete.rb +0 -32
- data/lib/shrine/plugins/parallelize.rb +0 -78
- data/lib/shrine/plugins/parsed_json.rb +0 -29
data/lib/shrine/attachment.rb
CHANGED
@@ -27,40 +27,6 @@ class Shrine
|
|
27
27
|
def initialize(name, **options)
|
28
28
|
@name = name.to_sym
|
29
29
|
@options = options
|
30
|
-
|
31
|
-
define_attachment_methods!
|
32
|
-
end
|
33
|
-
|
34
|
-
# Defines attachment methods for the specified attachment name. These
|
35
|
-
# methods will be added to any model that includes this module.
|
36
|
-
def define_attachment_methods!
|
37
|
-
attachment = self
|
38
|
-
name = attachment_name
|
39
|
-
|
40
|
-
define_method :"#{name}_attacher" do |**options|
|
41
|
-
if !instance_variable_get(:"@#{name}_attacher") || options.any?
|
42
|
-
instance_variable_set(:"@#{name}_attacher", attachment.build_attacher(self, options))
|
43
|
-
else
|
44
|
-
instance_variable_get(:"@#{name}_attacher")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
define_method :"#{name}=" do |value|
|
49
|
-
send(:"#{name}_attacher").assign(value)
|
50
|
-
end
|
51
|
-
|
52
|
-
define_method :"#{name}" do
|
53
|
-
send(:"#{name}_attacher").get
|
54
|
-
end
|
55
|
-
|
56
|
-
define_method :"#{name}_url" do |*args|
|
57
|
-
send(:"#{name}_attacher").url(*args)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Creates an instance of the corresponding Attacher subclass.
|
62
|
-
def build_attacher(object, options)
|
63
|
-
shrine_class::Attacher.new(object, @name, @options.merge(options))
|
64
30
|
end
|
65
31
|
|
66
32
|
# Returns name of the attachment this module provides.
|
@@ -75,17 +41,11 @@ class Shrine
|
|
75
41
|
|
76
42
|
# Returns class name with attachment name included.
|
77
43
|
#
|
78
|
-
# Shrine
|
79
|
-
def to_s
|
80
|
-
"#<#{self.class.inspect}(#{attachment_name})>"
|
81
|
-
end
|
82
|
-
|
83
|
-
# Returns class name with attachment name included.
|
84
|
-
#
|
85
|
-
# Shrine[:image].inspect #=> "#<Shrine::Attachment(image)>"
|
44
|
+
# Shrine::Attachment.new(:image).to_s #=> "#<Shrine::Attachment(image)>"
|
86
45
|
def inspect
|
87
46
|
"#<#{self.class.inspect}(#{attachment_name})>"
|
88
47
|
end
|
48
|
+
alias to_s inspect
|
89
49
|
|
90
50
|
# Returns the Shrine class that this attachment's class is namespaced
|
91
51
|
# under.
|
@@ -8,9 +8,14 @@ class Shrine
|
|
8
8
|
#
|
9
9
|
# [doc/plugins/activerecord.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/activerecord.md
|
10
10
|
module Activerecord
|
11
|
-
def self.
|
12
|
-
uploader.
|
13
|
-
uploader.
|
11
|
+
def self.load_dependencies(uploader, **)
|
12
|
+
uploader.plugin :model
|
13
|
+
uploader.plugin :atomic_helpers
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure(uploader, **opts)
|
17
|
+
uploader.opts[:activerecord] ||= { callbacks: true, validations: true }
|
18
|
+
uploader.opts[:activerecord].merge!(opts)
|
14
19
|
end
|
15
20
|
|
16
21
|
module AttachmentMethods
|
@@ -21,60 +26,132 @@ class Shrine
|
|
21
26
|
|
22
27
|
name = attachment_name
|
23
28
|
|
24
|
-
if shrine_class.opts[:
|
29
|
+
if shrine_class.opts[:activerecord][:validations]
|
25
30
|
model.validate do
|
26
|
-
|
27
|
-
|
31
|
+
# validation plugin integration
|
32
|
+
if send(:"#{name}_attacher").respond_to?(:errors)
|
33
|
+
send(:"#{name}_attacher").errors.each do |message|
|
34
|
+
errors.add(name, *message)
|
35
|
+
end
|
28
36
|
end
|
29
37
|
end
|
30
38
|
end
|
31
39
|
|
32
|
-
if shrine_class.opts[:
|
40
|
+
if shrine_class.opts[:activerecord][:callbacks]
|
33
41
|
model.before_save do
|
34
|
-
|
35
|
-
|
42
|
+
if send(:"#{name}_attacher").changed?
|
43
|
+
send(:"#{name}_attacher").save
|
44
|
+
end
|
36
45
|
end
|
37
46
|
|
38
47
|
[:create, :update].each do |action|
|
39
48
|
model.after_commit on: action do
|
40
|
-
|
41
|
-
|
49
|
+
if send(:"#{name}_attacher").changed?
|
50
|
+
send(:"#{name}_attacher").finalize
|
51
|
+
send(:"#{name}_attacher").activerecord_persist
|
52
|
+
end
|
42
53
|
end
|
43
54
|
end
|
44
55
|
|
45
56
|
model.after_commit on: :destroy do
|
46
|
-
send("#{name}_attacher").
|
57
|
+
send(:"#{name}_attacher").destroy_attached
|
47
58
|
end
|
48
59
|
end
|
49
|
-
end
|
50
|
-
end
|
51
60
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
# reload the attacher on record reload
|
62
|
+
define_method :reload do |*args|
|
63
|
+
result = super(*args)
|
64
|
+
instance_variable_set(:"@#{name}_attacher", nil)
|
65
|
+
result
|
66
|
+
end
|
56
67
|
end
|
57
68
|
end
|
58
69
|
|
59
70
|
module AttacherMethods
|
71
|
+
# Promotes cached file to permanent storage in an atomic way. It's
|
72
|
+
# intended to be called from a background job.
|
73
|
+
#
|
74
|
+
# attacher.assign(file)
|
75
|
+
# attacher.cached? #=> true
|
76
|
+
#
|
77
|
+
# # ... in background job ...
|
78
|
+
#
|
79
|
+
# attacher.atomic_promote
|
80
|
+
# attacher.stored? #=> true
|
81
|
+
#
|
82
|
+
# It accepts `:reload` and `:persist` strategies:
|
83
|
+
#
|
84
|
+
# attacher.atomic_promote(reload: :lock) # uses database locking (default)
|
85
|
+
# attacher.atomic_promote(reload: :fetch) # reloads with no locking
|
86
|
+
# attacher.atomic_promote(reload: ->(&b){}) # custom reloader
|
87
|
+
# attacher.atomic_promote(reload: false) # skips reloading
|
88
|
+
#
|
89
|
+
# attacher.atomic_promote(persist: :save) # persists stored file (default)
|
90
|
+
# attacher.atomic_promote(persist: ->{}) # custom persister
|
91
|
+
# attacher.atomic_promote(persist: false) # skips persistence
|
92
|
+
def activerecord_atomic_promote(**options, &block)
|
93
|
+
abstract_atomic_promote(activerecord_strategies(**options), &block)
|
94
|
+
end
|
95
|
+
alias atomic_promote activerecord_atomic_promote
|
96
|
+
|
97
|
+
# Persist the the record only if the attachment hasn't changed.
|
98
|
+
# Optionally yields reloaded attacher to the block before persisting.
|
99
|
+
# It's intended to be called from a background job.
|
100
|
+
#
|
101
|
+
# # ... in background job ...
|
102
|
+
#
|
103
|
+
# attacher.file.metadata["foo"] = "bar"
|
104
|
+
# attacher.write
|
105
|
+
#
|
106
|
+
# attacher.atomic_persist
|
107
|
+
def activerecord_atomic_persist(*args, **options, &block)
|
108
|
+
abstract_atomic_persist(*args, activerecord_strategies(**options), &block)
|
109
|
+
end
|
110
|
+
alias atomic_persist activerecord_atomic_persist
|
111
|
+
|
112
|
+
# Called in the `after_commit` callback after finalization.
|
113
|
+
def activerecord_persist
|
114
|
+
activerecord_save
|
115
|
+
end
|
116
|
+
alias persist activerecord_persist
|
117
|
+
|
60
118
|
private
|
61
119
|
|
62
|
-
#
|
63
|
-
def
|
64
|
-
|
120
|
+
# Resolves strategies for atomic promotion and persistence.
|
121
|
+
def activerecord_strategies(reload: :lock, persist: :save, **options)
|
122
|
+
reload = method(:"activerecord_#{reload}") if reload.is_a?(Symbol)
|
123
|
+
persist = method(:"activerecord_#{persist}") if persist.is_a?(Symbol)
|
124
|
+
|
125
|
+
{ reload: reload, persist: persist, **options }
|
126
|
+
end
|
127
|
+
|
128
|
+
# Implements the "fetch" reload strategy for #atomic_promote and
|
129
|
+
# #atomic_persist.
|
130
|
+
def activerecord_fetch
|
131
|
+
yield record.clone.reload
|
132
|
+
end
|
133
|
+
|
134
|
+
# Implements the "lock" reload strategy for #atomic_promote and
|
135
|
+
# #atomic_persist.
|
136
|
+
def activerecord_lock
|
137
|
+
record.transaction { yield record.clone.reload(lock: true) }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Implements the "save" persist strategy for #atomic_promote and
|
141
|
+
# #atomic_persist.
|
142
|
+
def activerecord_save
|
65
143
|
record.save(validate: false)
|
66
144
|
end
|
67
145
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
activerecord_json_column? ? value : super
|
146
|
+
# ActiveRecord JSON column attribute needs to be assigned with a Hash.
|
147
|
+
def serialize_column(data)
|
148
|
+
activerecord_json_column? ? data : super
|
72
149
|
end
|
73
150
|
|
74
151
|
# Returns true if the data attribute represents a JSON or JSONB column.
|
75
152
|
def activerecord_json_column?
|
76
153
|
return false unless record.is_a?(ActiveRecord::Base)
|
77
|
-
return false unless column = record.class.columns_hash[
|
154
|
+
return false unless column = record.class.columns_hash[attribute.to_s]
|
78
155
|
|
79
156
|
[:json, :jsonb].include?(column.type)
|
80
157
|
end
|
@@ -7,12 +7,12 @@ class Shrine
|
|
7
7
|
# [doc/plugins/add_metadata.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/add_metadata.md
|
8
8
|
module AddMetadata
|
9
9
|
def self.configure(uploader)
|
10
|
-
uploader.opts[:
|
10
|
+
uploader.opts[:add_metadata] ||= { definitions: [] }
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
14
|
def add_metadata(name = nil, &block)
|
15
|
-
opts[:
|
15
|
+
opts[:add_metadata][:definitions] << [name, block]
|
16
16
|
|
17
17
|
metadata_method(name) if name
|
18
18
|
end
|
@@ -31,25 +31,24 @@ class Shrine
|
|
31
31
|
end
|
32
32
|
|
33
33
|
module InstanceMethods
|
34
|
-
def extract_metadata(io,
|
34
|
+
def extract_metadata(io, **options)
|
35
35
|
metadata = super
|
36
|
-
context = context.merge(metadata: metadata)
|
37
36
|
|
38
|
-
extract_custom_metadata(io,
|
37
|
+
extract_custom_metadata(io, **options, metadata: metadata)
|
39
38
|
|
40
39
|
metadata
|
41
40
|
end
|
42
41
|
|
43
42
|
private
|
44
43
|
|
45
|
-
def extract_custom_metadata(io,
|
46
|
-
opts[:
|
47
|
-
result = instance_exec(io,
|
44
|
+
def extract_custom_metadata(io, **options)
|
45
|
+
opts[:add_metadata][:definitions].each do |name, block|
|
46
|
+
result = instance_exec(io, options, &block)
|
48
47
|
|
49
48
|
if name
|
50
|
-
|
49
|
+
options[:metadata].merge! name.to_s => result
|
51
50
|
else
|
52
|
-
|
51
|
+
options[:metadata].merge! result.transform_keys(&:to_s) if result
|
53
52
|
end
|
54
53
|
|
55
54
|
# rewind between metadata blocks
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
class AttachmentChanged < Error
|
5
|
+
end
|
6
|
+
|
7
|
+
module Plugins
|
8
|
+
# Documentation lives in [doc/plugins/atomic_helpers.md] on GitHub.
|
9
|
+
#
|
10
|
+
# [doc/plugins/atomic_helpers.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/atomic_helpers.md
|
11
|
+
class AtomicHelpers
|
12
|
+
module AttacherClassMethods
|
13
|
+
# Retrieves the attacher from the given entity/model and verifies that
|
14
|
+
# the attachment hasn't changed. It raises `Shrine::AttachmentChanged`
|
15
|
+
# exception if the attached file doesn't match.
|
16
|
+
#
|
17
|
+
# Shrine::Attacher.retrieve(model: photo, name: :image, data: data)
|
18
|
+
# #=> #<ImageUploader::Attacher>
|
19
|
+
def retrieve(model: nil, entity: nil, name:, data:, **options)
|
20
|
+
fail ArgumentError, "either :model or :entity is required" unless model || entity
|
21
|
+
|
22
|
+
record = model || entity
|
23
|
+
|
24
|
+
attacher = record.send(:"#{name}_attacher", **options) if record.respond_to?(:"#{name}_attacher")
|
25
|
+
attacher ||= from_model(record, name, **options) if model
|
26
|
+
attacher ||= from_entity(record, name, **options) if entity
|
27
|
+
|
28
|
+
if attacher.file != attacher.class.from_data(data).file
|
29
|
+
fail Shrine::AttachmentChanged, "attachment has changed"
|
30
|
+
end
|
31
|
+
|
32
|
+
attacher
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module AttacherMethods
|
37
|
+
# Like #promote, but additionally persists the promoted file
|
38
|
+
# atomically. You need to specify `:reload` and `:persist` strategies
|
39
|
+
# when calling the method:
|
40
|
+
#
|
41
|
+
# attacher.abstract_atomic_promote(
|
42
|
+
# reload: reload_strategy,
|
43
|
+
# persist: persist_strategy,
|
44
|
+
# )
|
45
|
+
#
|
46
|
+
# This more convenient to use with ORM plugins, which provide defaults
|
47
|
+
# for reloading and persistence.
|
48
|
+
def abstract_atomic_promote(reload:, persist:, **options, &block)
|
49
|
+
original_file = file
|
50
|
+
|
51
|
+
result = promote(**options)
|
52
|
+
|
53
|
+
begin
|
54
|
+
abstract_atomic_persist(original_file, reload: reload, persist: persist, &block)
|
55
|
+
result
|
56
|
+
rescue Shrine::AttachmentChanged
|
57
|
+
destroy(background: true)
|
58
|
+
raise
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Reloads the record to check whether the attachment has changed. If it
|
63
|
+
# hasn't, it persists the record. Otherwise it raises
|
64
|
+
# `Shrine::AttachmentChanged` exception.
|
65
|
+
#
|
66
|
+
# attacher.abstract_atomic_persist(
|
67
|
+
# reload: reload_strategy,
|
68
|
+
# persist: persist_strategy,
|
69
|
+
# )
|
70
|
+
#
|
71
|
+
# This more convenient to use with ORM plugins, which provide defaults
|
72
|
+
# for reloading and persistence.
|
73
|
+
def abstract_atomic_persist(original_file = file, reload:, persist:)
|
74
|
+
abstract_reload(reload) do |attacher|
|
75
|
+
if attacher && attacher.file != original_file
|
76
|
+
fail Shrine::AttachmentChanged, "attachment has changed"
|
77
|
+
end
|
78
|
+
|
79
|
+
yield attacher if block_given?
|
80
|
+
|
81
|
+
abstract_persist(persist)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
# Calls the reload strategy and yields a reloaded attacher from the
|
88
|
+
# reloaded record.
|
89
|
+
def abstract_reload(strategy)
|
90
|
+
return yield if strategy == false
|
91
|
+
|
92
|
+
strategy.call do |record|
|
93
|
+
reloaded_attacher = dup
|
94
|
+
reloaded_attacher.load_entity(record, name)
|
95
|
+
|
96
|
+
yield reloaded_attacher
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Calls the persist strategy.
|
101
|
+
def abstract_persist(strategy)
|
102
|
+
return if strategy == false
|
103
|
+
|
104
|
+
strategy.call
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
register_plugin(:atomic_helpers, AtomicHelpers)
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
module AttacherOptions
|
6
|
+
module AttacherMethods
|
7
|
+
def initialize(**options)
|
8
|
+
super
|
9
|
+
@options = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def attach_options(options = nil)
|
13
|
+
handle_option(:attach, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def promote_options(options = nil)
|
17
|
+
handle_option(:promote, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy_options(options = nil)
|
21
|
+
handle_option(:destroy, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def attach_cached(io, **options)
|
25
|
+
super(io, **attach_options, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def attach(io, **options)
|
29
|
+
super(io, **attach_options, **options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def promote_cached(**options)
|
33
|
+
super(**promote_options, **options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy_attached(**options)
|
37
|
+
super(**destroy_options, **options)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def handle_option(name, options)
|
43
|
+
if options
|
44
|
+
@options[name] ||= {}
|
45
|
+
@options[name].merge!(options)
|
46
|
+
else
|
47
|
+
@options[name] || {}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
register_plugin(:attacher_options, AttacherOptions)
|
54
|
+
end
|
55
|
+
end
|