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
@@ -13,19 +13,12 @@ class Shrine
|
|
13
13
|
}.inspect}"
|
14
14
|
end
|
15
15
|
|
16
|
-
def self.configure(uploader,
|
17
|
-
|
18
|
-
Shrine.deprecation("The :default analyzer of the determine_mime_type plugin has been renamed to :content_type. The :default alias will not be supported in Shrine 3.")
|
19
|
-
opts = opts.merge(analyzer: :content_type)
|
20
|
-
end
|
21
|
-
|
22
|
-
uploader.opts[:determine_mime_type] ||= { analyzer: :file, analyzer_options: {}, log_subscriber: LOG_SUBSCRIBER }
|
16
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
17
|
+
uploader.opts[:determine_mime_type] ||= { analyzer: :file, analyzer_options: {} }
|
23
18
|
uploader.opts[:determine_mime_type].merge!(opts)
|
24
19
|
|
25
20
|
# instrumentation plugin integration
|
26
|
-
if uploader.respond_to?(:subscribe)
|
27
|
-
uploader.subscribe(:mime_type, &uploader.opts[:determine_mime_type][:log_subscriber])
|
28
|
-
end
|
21
|
+
uploader.subscribe(:mime_type, &log_subscriber) if uploader.respond_to?(:subscribe)
|
29
22
|
end
|
30
23
|
|
31
24
|
module ClassMethods
|
@@ -79,11 +72,6 @@ class Shrine
|
|
79
72
|
def extract_mime_type(io)
|
80
73
|
self.class.determine_mime_type(io)
|
81
74
|
end
|
82
|
-
|
83
|
-
# Returns a hash of built-in MIME type analyzers.
|
84
|
-
def mime_type_analyzers
|
85
|
-
self.class.mime_type_analyzers
|
86
|
-
end
|
87
75
|
end
|
88
76
|
|
89
77
|
class MimeTypeAnalyzer
|
@@ -6,44 +6,20 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/download_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/download_endpoint.md
|
8
8
|
module DownloadEndpoint
|
9
|
-
def self.load_dependencies(uploader,
|
9
|
+
def self.load_dependencies(uploader, **)
|
10
10
|
uploader.plugin :rack_response
|
11
11
|
uploader.plugin :_urlsafe_serialization
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.configure(uploader, opts
|
14
|
+
def self.configure(uploader, **opts)
|
15
15
|
uploader.opts[:download_endpoint] ||= { disposition: "inline", download_options: {} }
|
16
16
|
uploader.opts[:download_endpoint].merge!(opts)
|
17
|
-
|
18
|
-
Shrine.deprecation("The :storages download_endpoint option is deprecated, you should use UploadedFile#download_url for generating URLs to the download endpoint.") if uploader.opts[:download_endpoint][:storages]
|
19
|
-
|
20
|
-
uploader.assign_download_endpoint(App) unless uploader.const_defined?(:DownloadEndpoint)
|
21
17
|
end
|
22
18
|
|
23
19
|
module ClassMethods
|
24
|
-
# Assigns the subclass a copy of the download endpoint class.
|
25
|
-
def inherited(subclass)
|
26
|
-
super
|
27
|
-
subclass.assign_download_endpoint(@download_endpoint)
|
28
|
-
end
|
29
|
-
|
30
20
|
# Returns the Rack application that retrieves requested files.
|
31
21
|
def download_endpoint(**options)
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
# Assigns the subclassed endpoint as the `DownloadEndpoint` constant.
|
36
|
-
def assign_download_endpoint(app_class)
|
37
|
-
@download_endpoint = new_download_endpoint(app_class)
|
38
|
-
|
39
|
-
const_set(:DownloadEndpoint, @download_endpoint)
|
40
|
-
deprecate_constant(:DownloadEndpoint)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def new_download_endpoint(app_class, **options)
|
46
|
-
app_class.new(
|
22
|
+
Shrine::DownloadEndpoint.new(
|
47
23
|
shrine_class: self,
|
48
24
|
**opts[:download_endpoint],
|
49
25
|
**options,
|
@@ -52,28 +28,10 @@ class Shrine
|
|
52
28
|
end
|
53
29
|
|
54
30
|
module FileMethods
|
55
|
-
# Constructs the URL from the optional host, prefix, storage key and
|
56
|
-
# uploaded file's id. For other uploaded files that aren't in the list
|
57
|
-
# of storages it just returns their original URL.
|
58
|
-
def url(**options)
|
59
|
-
if download_storages && download_storages.include?(storage_key.to_sym)
|
60
|
-
Shrine.deprecation("The :storages option for download_endpoint plugin is deprecated and will be obsolete in Shrine 3. Use UploadedFile#download_url instead.")
|
61
|
-
download_url
|
62
|
-
else
|
63
|
-
super
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
31
|
# Returns file URL on the download endpoint.
|
68
32
|
def download_url(**options)
|
69
33
|
FileUrl.new(self).call(**options)
|
70
34
|
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def download_storages
|
75
|
-
shrine_class.opts[:download_endpoint][:storages]
|
76
|
-
end
|
77
35
|
end
|
78
36
|
|
79
37
|
class FileUrl
|
@@ -105,116 +63,110 @@ class Shrine
|
|
105
63
|
file.shrine_class.opts[:download_endpoint]
|
106
64
|
end
|
107
65
|
end
|
66
|
+
end
|
108
67
|
|
109
|
-
|
110
|
-
|
111
|
-
# streaming.
|
112
|
-
class App
|
113
|
-
# Writes given options to instance variables.
|
114
|
-
def initialize(options)
|
115
|
-
options.each do |name, value|
|
116
|
-
instance_variable_set("@#{name}", value)
|
117
|
-
end
|
118
|
-
end
|
68
|
+
register_plugin(:download_endpoint, DownloadEndpoint)
|
69
|
+
end
|
119
70
|
|
120
|
-
def call(env)
|
121
|
-
request = Rack::Request.new(env)
|
122
71
|
|
123
|
-
|
124
|
-
|
72
|
+
# Routes incoming requests. It first asserts that the storage is existent
|
73
|
+
# and allowed. Afterwards it proceeds with the file download using
|
74
|
+
# streaming.
|
75
|
+
class DownloadEndpoint
|
76
|
+
# Writes given options to instance variables.
|
77
|
+
def initialize(options)
|
78
|
+
options.each do |name, value|
|
79
|
+
instance_variable_set("@#{name}", value)
|
80
|
+
end
|
81
|
+
end
|
125
82
|
|
126
|
-
|
127
|
-
|
83
|
+
def call(env)
|
84
|
+
request = Rack::Request.new(env)
|
128
85
|
|
129
|
-
|
86
|
+
status, headers, body = catch(:halt) do
|
87
|
+
error!(405, "Method Not Allowed") unless request.get?
|
130
88
|
|
131
|
-
|
132
|
-
|
89
|
+
handle_request(request)
|
90
|
+
end
|
133
91
|
|
134
|
-
|
135
|
-
"#<#{@shrine_class}::DownloadEndpoint>"
|
136
|
-
end
|
137
|
-
alias to_s inspect
|
92
|
+
headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
|
138
93
|
|
139
|
-
|
94
|
+
[status, headers, body]
|
95
|
+
end
|
140
96
|
|
141
|
-
|
142
|
-
|
97
|
+
def inspect
|
98
|
+
"#<#{@shrine_class}::DownloadEndpoint>"
|
99
|
+
end
|
100
|
+
alias to_s inspect
|
143
101
|
|
144
|
-
|
145
|
-
uploaded_file = get_uploaded_file(components.first)
|
146
|
-
elsif components.count == 2
|
147
|
-
# handle legacy "/:storage/:id" URLs
|
148
|
-
uploaded_file = @shrine_class::UploadedFile.new(
|
149
|
-
"storage" => components.first,
|
150
|
-
"id" => components.last,
|
151
|
-
)
|
152
|
-
end
|
102
|
+
private
|
153
103
|
|
154
|
-
|
155
|
-
|
104
|
+
def handle_request(request)
|
105
|
+
_, serialized, * = request.path_info.split("/")
|
156
106
|
|
157
|
-
|
158
|
-
def serve_file(uploaded_file, request)
|
159
|
-
if @redirect
|
160
|
-
redirect_to_file(uploaded_file, request)
|
161
|
-
else
|
162
|
-
stream_file(uploaded_file, request)
|
163
|
-
end
|
164
|
-
end
|
107
|
+
uploaded_file = get_uploaded_file(serialized)
|
165
108
|
|
166
|
-
|
167
|
-
|
168
|
-
open_file(uploaded_file, request)
|
109
|
+
serve_file(uploaded_file, request)
|
110
|
+
end
|
169
111
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
112
|
+
# Streams or redirects to the uploaded file.
|
113
|
+
def serve_file(uploaded_file, request)
|
114
|
+
if @redirect
|
115
|
+
redirect_to_file(uploaded_file, request)
|
116
|
+
else
|
117
|
+
stream_file(uploaded_file, request)
|
118
|
+
end
|
119
|
+
end
|
174
120
|
|
175
|
-
|
121
|
+
# Streams the uploaded file content.
|
122
|
+
def stream_file(uploaded_file, request)
|
123
|
+
open_file(uploaded_file, request)
|
176
124
|
|
177
|
-
|
178
|
-
|
125
|
+
response = uploaded_file.to_rack_response(
|
126
|
+
disposition: @disposition,
|
127
|
+
range: request.env["HTTP_RANGE"],
|
128
|
+
)
|
179
129
|
|
180
|
-
|
181
|
-
def redirect_to_file(uploaded_file, request)
|
182
|
-
if @redirect == true
|
183
|
-
redirect_url = uploaded_file.url
|
184
|
-
else
|
185
|
-
redirect_url = @redirect.call(uploaded_file, request)
|
186
|
-
end
|
130
|
+
response[1]["Cache-Control"] = "max-age=#{365*24*60*60}" # cache for a year
|
187
131
|
|
188
|
-
|
189
|
-
|
132
|
+
response
|
133
|
+
end
|
190
134
|
|
191
|
-
|
192
|
-
|
193
|
-
|
135
|
+
# Redirects to the uploaded file's direct URL or the specified URL proc.
|
136
|
+
def redirect_to_file(uploaded_file, request)
|
137
|
+
if @redirect == true
|
138
|
+
redirect_url = uploaded_file.url
|
139
|
+
else
|
140
|
+
redirect_url = @redirect.call(uploaded_file, request)
|
141
|
+
end
|
194
142
|
|
195
|
-
|
196
|
-
|
143
|
+
[302, { "Location" => redirect_url }, []]
|
144
|
+
end
|
197
145
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
not_found! unless uploaded_file.exists?
|
202
|
-
uploaded_file
|
203
|
-
rescue Shrine::Error # storage not found
|
204
|
-
not_found!
|
205
|
-
end
|
146
|
+
def open_file(uploaded_file, request)
|
147
|
+
download_options = @download_options
|
148
|
+
download_options = download_options.call(uploaded_file, request) if download_options.respond_to?(:call)
|
206
149
|
|
207
|
-
|
208
|
-
|
209
|
-
|
150
|
+
uploaded_file.open(**download_options)
|
151
|
+
rescue Shrine::FileNotFound
|
152
|
+
not_found!
|
153
|
+
end
|
210
154
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
155
|
+
# Deserializes a Shrine::UploadedFile from a URL component. Returns 404 if
|
156
|
+
# storage is not found.
|
157
|
+
def get_uploaded_file(serialized)
|
158
|
+
@shrine_class::UploadedFile.urlsafe_load(serialized)
|
159
|
+
rescue Shrine::Error # storage not found
|
160
|
+
not_found!
|
216
161
|
end
|
217
162
|
|
218
|
-
|
163
|
+
def not_found!
|
164
|
+
error!(404, "File Not Found")
|
165
|
+
end
|
166
|
+
|
167
|
+
# Halts the request with the error message.
|
168
|
+
def error!(status, message)
|
169
|
+
throw :halt, [status, { "Content-Type" => "text/plain" }, [message]]
|
170
|
+
end
|
219
171
|
end
|
220
172
|
end
|
@@ -6,17 +6,13 @@ class Shrine
|
|
6
6
|
#
|
7
7
|
# [doc/plugins/dynamic_storage.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/dynamic_storage.md
|
8
8
|
module DynamicStorage
|
9
|
-
def self.configure(uploader
|
10
|
-
uploader.opts[:
|
9
|
+
def self.configure(uploader)
|
10
|
+
uploader.opts[:dynamic_storage] ||= { resolvers: {} }
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
def dynamic_storages
|
15
|
-
opts[:dynamic_storages]
|
16
|
-
end
|
17
|
-
|
18
14
|
def storage(regex, &block)
|
19
|
-
|
15
|
+
opts[:dynamic_storage][:resolvers][regex] = block
|
20
16
|
end
|
21
17
|
|
22
18
|
def find_storage(name)
|
@@ -26,7 +22,7 @@ class Shrine
|
|
26
22
|
private
|
27
23
|
|
28
24
|
def resolve_dynamic_storage(name)
|
29
|
-
|
25
|
+
opts[:dynamic_storage][:resolvers].each do |regex, block|
|
30
26
|
if match = name.to_s.match(regex)
|
31
27
|
return block.call(match)
|
32
28
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# Documentation lives in [doc/plugins/entity.md] on GitHub.
|
6
|
+
#
|
7
|
+
# [doc/plugins/entity.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/entity.md
|
8
|
+
module Entity
|
9
|
+
def self.load_dependencies(uploader, **)
|
10
|
+
uploader.plugin :column
|
11
|
+
end
|
12
|
+
|
13
|
+
module AttachmentMethods
|
14
|
+
# Defines `#<name>`, `#<name>_url`, and `#<name>_attacher` methods.
|
15
|
+
def initialize(name, **options)
|
16
|
+
super
|
17
|
+
|
18
|
+
attachment = self
|
19
|
+
|
20
|
+
# Returns the attached file.
|
21
|
+
define_method :"#{name}" do |*args|
|
22
|
+
send(:"#{name}_attacher").get(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the URL to the attached file.
|
26
|
+
define_method :"#{name}_url" do |*args|
|
27
|
+
send(:"#{name}_attacher").url(*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an attacher instance.
|
31
|
+
define_method :"#{name}_attacher" do |**options|
|
32
|
+
attachment.attacher(self, options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates an instance of the corresponding Attacher subclass. It's not
|
37
|
+
# memoized because the entity object could be frozen.
|
38
|
+
def attacher(record, options)
|
39
|
+
shrine_class::Attacher.from_entity(record, @name, @options.merge(options))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module AttacherClassMethods
|
44
|
+
# Initializes itself from an entity instance and attachment name.
|
45
|
+
#
|
46
|
+
# photo.image_data #=> "{...}" # a file is attached
|
47
|
+
#
|
48
|
+
# attacher = Attacher.from_entity(photo, :image)
|
49
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
50
|
+
def from_entity(record, name, type: :entity, **options)
|
51
|
+
attacher = new(**options)
|
52
|
+
attacher.load_entity(record, name, type: type)
|
53
|
+
attacher
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module AttacherMethods
|
58
|
+
attr_reader :record, :name
|
59
|
+
|
60
|
+
# Saves record and name and initializes attachment from the entity
|
61
|
+
# attribute. Called from `Attacher.from_entity`.
|
62
|
+
def load_entity(record, name, type: :entity)
|
63
|
+
@record = record
|
64
|
+
@name = name.to_sym
|
65
|
+
@type = type
|
66
|
+
|
67
|
+
@context.merge!(record: record, name: name)
|
68
|
+
|
69
|
+
read
|
70
|
+
end
|
71
|
+
|
72
|
+
# Overwrites the current attachment with the one from model attribute.
|
73
|
+
#
|
74
|
+
# photo.image_data #=> nil
|
75
|
+
# attacher = Shrine::Attacher.from_entity(photo, :image)
|
76
|
+
# photo.image_data = uploaded_file.to_json
|
77
|
+
#
|
78
|
+
# attacher.file #=> nil
|
79
|
+
# attacher.reload
|
80
|
+
# attacher.file #=> #<Shrine::UploadedFile>
|
81
|
+
def reload
|
82
|
+
read
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns a hash with entity attribute name and column data.
|
87
|
+
#
|
88
|
+
# attacher.column_values
|
89
|
+
# #=> { image_data: '{"id":"...","storage":"...","metadata":{...}}' }
|
90
|
+
def column_values
|
91
|
+
{ attribute => column_data }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the entity attribute name used for reading and writing
|
95
|
+
# attachment data.
|
96
|
+
#
|
97
|
+
# attacher = Shrine::Attacher.from_entity(photo, :image)
|
98
|
+
# attacher.attribute #=> :image_data
|
99
|
+
def attribute
|
100
|
+
fail Shrine::Error, "record is not loaded" if name.nil?
|
101
|
+
|
102
|
+
:"#{name}_data"
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Loads attachment from the entity attribute.
|
108
|
+
def read
|
109
|
+
load_column(read_attribute)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Reads value from the entity attribute.
|
113
|
+
def read_attribute
|
114
|
+
record.public_send(attribute)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns whether the attacher has been loaded from an entity instance.
|
118
|
+
def entity?
|
119
|
+
type == :entity
|
120
|
+
end
|
121
|
+
|
122
|
+
attr_reader :type
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
register_plugin(:entity, Entity)
|
127
|
+
end
|
128
|
+
end
|