shrine 2.14.0 → 2.15.0
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 +384 -374
- data/README.md +132 -63
- data/doc/advantages.md +191 -109
- data/doc/attacher.md +1 -1
- data/doc/carrierwave.md +4 -4
- data/doc/creating_storages.md +2 -2
- data/doc/design.md +2 -2
- data/doc/direct_s3.md +3 -3
- data/doc/metadata.md +1 -1
- data/doc/multiple_files.md +2 -2
- data/doc/paperclip.md +3 -3
- data/doc/plugins/activerecord.md +92 -0
- data/doc/plugins/add_metadata.md +93 -0
- data/doc/plugins/backgrounding.md +148 -0
- data/doc/plugins/backup.md +29 -0
- data/doc/plugins/cached_attachment_data.md +23 -0
- data/doc/plugins/copy.md +22 -0
- data/doc/plugins/data_uri.md +92 -0
- data/doc/plugins/default_storage.md +16 -0
- data/doc/plugins/default_url.md +33 -0
- data/doc/plugins/default_url_options.md +22 -0
- data/doc/plugins/delete_promoted.md +10 -0
- data/doc/plugins/delete_raw.md +16 -0
- data/doc/plugins/derivation_endpoint.md +747 -0
- data/doc/plugins/determine_mime_type.md +64 -0
- data/doc/plugins/direct_upload.md +170 -0
- data/doc/plugins/download_endpoint.md +83 -0
- data/doc/plugins/dynamic_storage.md +20 -0
- data/doc/plugins/hooks.md +56 -0
- data/doc/plugins/included.md +15 -0
- data/doc/plugins/infer_extension.md +57 -0
- data/doc/plugins/keep_files.md +20 -0
- data/doc/plugins/logging.md +39 -0
- data/doc/plugins/metadata_attribues.md +43 -0
- data/doc/plugins/migration_helpers.md +58 -0
- data/doc/plugins/module_include.md +40 -0
- data/doc/plugins/moving.md +17 -0
- data/doc/plugins/multi_delete.md +18 -0
- data/doc/plugins/parallelize.md +14 -0
- data/doc/plugins/parsed_json.md +9 -0
- data/doc/plugins/presign_endpoint.md +133 -0
- data/doc/plugins/pretty_location.md +29 -0
- data/doc/plugins/processing.md +68 -0
- data/doc/plugins/rack_file.md +49 -0
- data/doc/plugins/rack_response.md +96 -0
- data/doc/plugins/recache.md +27 -0
- data/doc/plugins/refresh_metadata.md +31 -0
- data/doc/plugins/remote_url.md +104 -0
- data/doc/plugins/remove_attachment.md +16 -0
- data/doc/plugins/remove_invalid.md +9 -0
- data/doc/plugins/restore_cached_data.md +14 -0
- data/doc/plugins/sequel.md +64 -0
- data/doc/plugins/signature.md +49 -0
- data/doc/plugins/store_dimensions.md +68 -0
- data/doc/plugins/tempfile.md +40 -0
- data/doc/plugins/upload_endpoint.md +123 -0
- data/doc/plugins/upload_options.md +28 -0
- data/doc/plugins/validation_helpers.md +129 -0
- data/doc/plugins/versions.md +179 -0
- data/doc/processing.md +217 -247
- data/doc/refile.md +3 -3
- data/doc/release_notes/1.0.0.md +143 -0
- data/doc/release_notes/1.1.0.md +184 -0
- data/doc/release_notes/1.2.0.md +37 -0
- data/doc/release_notes/1.3.0.md +90 -0
- data/doc/release_notes/1.4.0.md +167 -0
- data/doc/release_notes/1.4.1.md +9 -0
- data/doc/release_notes/1.4.2.md +20 -0
- data/doc/release_notes/2.0.0.md +173 -0
- data/doc/release_notes/2.0.1.md +12 -0
- data/doc/release_notes/2.1.0.md +59 -0
- data/doc/release_notes/2.1.1.md +8 -0
- data/doc/release_notes/2.10.0.md +52 -0
- data/doc/release_notes/2.10.1.md +6 -0
- data/doc/release_notes/2.11.0.md +69 -0
- data/doc/release_notes/2.12.0.md +65 -0
- data/doc/release_notes/2.13.0.md +146 -0
- data/doc/release_notes/2.14.0.md +278 -0
- data/doc/release_notes/2.15.0.md +82 -0
- data/doc/release_notes/2.2.0.md +98 -0
- data/doc/release_notes/2.3.0.md +50 -0
- data/doc/release_notes/2.3.1.md +10 -0
- data/doc/release_notes/2.4.0.md +87 -0
- data/doc/release_notes/2.4.1.md +29 -0
- data/doc/release_notes/2.5.0.md +130 -0
- data/doc/release_notes/2.6.0.md +254 -0
- data/doc/release_notes/2.6.1.md +14 -0
- data/doc/release_notes/2.7.0.md +180 -0
- data/doc/release_notes/2.8.0.md +95 -0
- data/doc/release_notes/2.9.0.md +82 -0
- data/doc/retrieving_uploads.md +1 -1
- data/doc/storage/file_system.md +96 -0
- data/doc/storage/s3.md +293 -0
- data/doc/validation.md +1 -1
- data/lib/shrine/plugins/_urlsafe_serialization.rb +33 -125
- data/lib/shrine/plugins/activerecord.rb +0 -78
- data/lib/shrine/plugins/add_metadata.rb +0 -80
- data/lib/shrine/plugins/backgrounding.rb +0 -134
- data/lib/shrine/plugins/backup.rb +0 -22
- data/lib/shrine/plugins/cached_attachment_data.rb +0 -15
- data/lib/shrine/plugins/copy.rb +0 -14
- data/lib/shrine/plugins/data_uri.rb +0 -73
- data/lib/shrine/plugins/default_storage.rb +0 -11
- data/lib/shrine/plugins/default_url.rb +0 -25
- data/lib/shrine/plugins/default_url_options.rb +0 -16
- data/lib/shrine/plugins/delete_promoted.rb +0 -6
- data/lib/shrine/plugins/delete_raw.rb +0 -10
- data/lib/shrine/plugins/derivation_endpoint.rb +652 -0
- data/lib/shrine/plugins/determine_mime_type.rb +1 -85
- data/lib/shrine/plugins/direct_upload.rb +0 -155
- data/lib/shrine/plugins/download_endpoint.rb +11 -73
- data/lib/shrine/plugins/dynamic_storage.rb +0 -17
- data/lib/shrine/plugins/hooks.rb +0 -48
- data/lib/shrine/plugins/included.rb +0 -12
- data/lib/shrine/plugins/infer_extension.rb +0 -49
- data/lib/shrine/plugins/keep_files.rb +0 -19
- data/lib/shrine/plugins/logging.rb +0 -39
- data/lib/shrine/plugins/metadata_attributes.rb +0 -35
- data/lib/shrine/plugins/migration_helpers.rb +0 -50
- data/lib/shrine/plugins/module_include.rb +0 -32
- data/lib/shrine/plugins/moving.rb +0 -12
- data/lib/shrine/plugins/multi_delete.rb +0 -13
- data/lib/shrine/plugins/parallelize.rb +0 -8
- data/lib/shrine/plugins/parsed_json.rb +0 -5
- data/lib/shrine/plugins/presign_endpoint.rb +2 -117
- data/lib/shrine/plugins/pretty_location.rb +0 -22
- data/lib/shrine/plugins/processing.rb +0 -55
- data/lib/shrine/plugins/rack_file.rb +0 -39
- data/lib/shrine/plugins/rack_response.rb +0 -81
- data/lib/shrine/plugins/recache.rb +0 -21
- data/lib/shrine/plugins/refresh_metadata.rb +0 -24
- data/lib/shrine/plugins/remote_url.rb +0 -85
- data/lib/shrine/plugins/remove_attachment.rb +0 -10
- data/lib/shrine/plugins/remove_invalid.rb +0 -6
- data/lib/shrine/plugins/restore_cached_data.rb +0 -10
- data/lib/shrine/plugins/sequel.rb +0 -54
- data/lib/shrine/plugins/signature.rb +0 -37
- data/lib/shrine/plugins/store_dimensions.rb +0 -63
- data/lib/shrine/plugins/tempfile.rb +4 -35
- data/lib/shrine/plugins/upload_endpoint.rb +2 -109
- data/lib/shrine/plugins/upload_options.rb +0 -20
- data/lib/shrine/plugins/validation_helpers.rb +0 -36
- data/lib/shrine/plugins/versions.rb +0 -156
- data/lib/shrine/storage/file_system.rb +0 -77
- data/lib/shrine/storage/s3.rb +0 -249
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +2 -2
- metadata +86 -6
data/doc/validation.md
CHANGED
@@ -2,116 +2,66 @@
|
|
2
2
|
|
3
3
|
require "base64"
|
4
4
|
require "json"
|
5
|
-
require "openssl"
|
6
5
|
|
7
6
|
class Shrine
|
8
7
|
module Plugins
|
9
|
-
# The `urlsafe_serialization` plugin provides the ability to serialize and
|
10
|
-
# deserialize a `Shrine::UploadedFile` in a way that's suitable for
|
11
|
-
# including in a URL.
|
12
|
-
#
|
13
|
-
# plugin :urlsafe_serialization
|
14
|
-
#
|
15
|
-
# The plugin defines `urlsafe_dump` and `urlsafe_load` methods on
|
16
|
-
# `Shrine::UploadedFile`. The file is first serialized to JSON, then
|
17
|
-
# encoded with base64.
|
18
|
-
#
|
19
|
-
# serialized = uploaded_file.urlsafe_dump
|
20
|
-
# # or
|
21
|
-
# serialized = MyUploader::UploadedFile.urlsafe_dump(uploaded_file)
|
22
|
-
# serialized #=> "eyJpZCI6IjlhZGM0NmIzZjI..."
|
23
|
-
#
|
24
|
-
# # ...
|
25
|
-
#
|
26
|
-
# uploaded_file = MyUploader::UploadedFile.urlsafe_load(serialized)
|
27
|
-
# uploaded_file #=> #<MyUploader::UploadedFile>
|
28
|
-
#
|
29
|
-
# ## Metadata
|
30
|
-
#
|
31
|
-
# By default no metadata is included in the serialization:
|
32
|
-
#
|
33
|
-
# uploaded_file.metadata #=> { ... metadata ... }
|
34
|
-
#
|
35
|
-
# serialized = MyUploader::UploadedFile.urlsafe_dump(uploaded_file)
|
36
|
-
# uploaded_file = MyUploader::UploadedFile.urlsafe_load(serialized)
|
37
|
-
#
|
38
|
-
# uploaded_file.metadata #=> {}
|
39
|
-
#
|
40
|
-
# The `:metadata` option can be used to specify metadata you want to
|
41
|
-
# serialize:
|
42
|
-
#
|
43
|
-
# serialized = MyUploader::UploadedFile.urlsafe_dump(uploaded_file, metadata: %w[size mime_type])
|
44
|
-
# uploaded_file = MyUploader::UploadedFile.urlsafe_load(serialized)
|
45
|
-
#
|
46
|
-
# uploaded_file.metadata #=> { "size" => 4394, "mime_type" => "image/jpeg" }
|
47
|
-
#
|
48
|
-
# ## Signing
|
49
|
-
#
|
50
|
-
# By default the seralization is done with simple JSON + base64 encoding.
|
51
|
-
# If you want to ensure the serialized data hasn't been tampred with, you
|
52
|
-
# can have it signed with a secret key.
|
53
|
-
#
|
54
|
-
# plugin :urlsafe_serialization, secret_key: "my secret key"
|
55
|
-
#
|
56
|
-
# Now the `urlsafe_dump` will automatically sign serialized data with your
|
57
|
-
# secret key, and `urlsafe_load` will automatically verify it.
|
58
|
-
#
|
59
|
-
# serialized = MyUploader::UploadedFile.urlsafe_dump(uploaded_file)
|
60
|
-
# serialized #=> "<signature>--<json-base64-encoded-data>"
|
61
|
-
#
|
62
|
-
# uploaded_file = MyUploader::UploadedFile.urlsafe_load(serialized) # verifies the signature
|
63
|
-
# uploaded_file #=> #<MyUploader::UploadedFile>
|
64
|
-
#
|
65
|
-
# If the signature is missing or invalid,
|
66
|
-
# `Shrine::Plugins::UrlsafeSerialization::InvalidSignature` exception is
|
67
|
-
# raised.
|
68
8
|
module UrlsafeSerialization
|
69
|
-
|
9
|
+
module ClassMethods
|
10
|
+
def urlsafe_serialize(hash)
|
11
|
+
urlsafe_serializer.encode(hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
def urlsafe_deserialize(string)
|
15
|
+
urlsafe_serializer.decode(string)
|
16
|
+
end
|
70
17
|
|
71
|
-
|
72
|
-
|
73
|
-
|
18
|
+
def urlsafe_serializer
|
19
|
+
Serializer.new
|
20
|
+
end
|
74
21
|
end
|
75
22
|
|
76
23
|
module FileMethods
|
77
24
|
def urlsafe_dump(**options)
|
78
25
|
self.class.urlsafe_dump(self, **options)
|
79
26
|
end
|
27
|
+
|
28
|
+
def urlsafe_data(metadata: [])
|
29
|
+
data = self.data.dup
|
30
|
+
|
31
|
+
if metadata.any?
|
32
|
+
# order metadata in the specified order
|
33
|
+
data["metadata"] = metadata
|
34
|
+
.map { |name| [name, self.metadata[name]] }
|
35
|
+
.to_h
|
36
|
+
else
|
37
|
+
# save precious characters
|
38
|
+
data.delete("metadata")
|
39
|
+
end
|
40
|
+
|
41
|
+
data
|
42
|
+
end
|
80
43
|
end
|
81
44
|
|
82
45
|
module FileClassMethods
|
83
|
-
def urlsafe_dump(file,
|
84
|
-
data = file.
|
85
|
-
data["metadata"] = metadata
|
86
|
-
.map { |name| [name, file.metadata[name]] }
|
87
|
-
.to_h
|
46
|
+
def urlsafe_dump(file, **options)
|
47
|
+
data = file.urlsafe_data(**options)
|
88
48
|
|
89
|
-
|
49
|
+
shrine_class.urlsafe_serialize(data)
|
90
50
|
end
|
91
51
|
|
92
52
|
def urlsafe_load(string)
|
93
|
-
data =
|
53
|
+
data = shrine_class.urlsafe_deserialize(string)
|
94
54
|
|
95
55
|
new(data)
|
96
56
|
end
|
97
|
-
|
98
|
-
def urlsafe_serializer
|
99
|
-
secret_key = shrine_class.opts[:urlsafe_serialization][:secret_key]
|
100
|
-
|
101
|
-
if secret_key
|
102
|
-
SecureSerializer.new(secret_key: secret_key)
|
103
|
-
else
|
104
|
-
Serializer.new
|
105
|
-
end
|
106
|
-
end
|
107
57
|
end
|
108
58
|
|
109
59
|
class Serializer
|
110
|
-
def
|
60
|
+
def encode(data)
|
111
61
|
base64_encode(json_encode(data))
|
112
62
|
end
|
113
63
|
|
114
|
-
def
|
64
|
+
def decode(data)
|
115
65
|
json_decode(base64_decode(data))
|
116
66
|
end
|
117
67
|
|
@@ -133,48 +83,6 @@ class Shrine
|
|
133
83
|
JSON.parse(data)
|
134
84
|
end
|
135
85
|
end
|
136
|
-
|
137
|
-
class SecureSerializer < Serializer
|
138
|
-
attr_reader :secret_key
|
139
|
-
|
140
|
-
def initialize(secret_key:)
|
141
|
-
@secret_key = secret_key
|
142
|
-
end
|
143
|
-
|
144
|
-
def dump(data)
|
145
|
-
hmac_encode(super)
|
146
|
-
end
|
147
|
-
|
148
|
-
def load(data)
|
149
|
-
super(hmac_decode(data))
|
150
|
-
end
|
151
|
-
|
152
|
-
def hmac_encode(data)
|
153
|
-
"#{generate_hmac(data)}--#{data}"
|
154
|
-
end
|
155
|
-
|
156
|
-
def hmac_decode(data)
|
157
|
-
data, hmac = data.split("--", 2).reverse
|
158
|
-
verify_hmac(hmac, data)
|
159
|
-
data
|
160
|
-
end
|
161
|
-
|
162
|
-
def verify_hmac(provided_hmac, data)
|
163
|
-
if provided_hmac.nil?
|
164
|
-
raise InvalidSignature, "signature is missing"
|
165
|
-
end
|
166
|
-
|
167
|
-
expected_hmac = generate_hmac(data)
|
168
|
-
|
169
|
-
if provided_hmac != expected_hmac
|
170
|
-
raise InvalidSignature, "provided signature doesn't match the expected"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def generate_hmac(data)
|
175
|
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret_key, data)
|
176
|
-
end
|
177
|
-
end
|
178
86
|
end
|
179
87
|
|
180
88
|
register_plugin(:_urlsafe_serialization, UrlsafeSerialization)
|
@@ -4,84 +4,6 @@ require "active_record"
|
|
4
4
|
|
5
5
|
class Shrine
|
6
6
|
module Plugins
|
7
|
-
# The `activerecord` plugin extends the "attachment" interface with support
|
8
|
-
# for ActiveRecord.
|
9
|
-
#
|
10
|
-
# plugin :activerecord
|
11
|
-
#
|
12
|
-
# ## Callbacks
|
13
|
-
#
|
14
|
-
# Now the attachment module will add additional callbacks to the model:
|
15
|
-
#
|
16
|
-
# * "before save" -- Used by the `recache` plugin.
|
17
|
-
# * "after commit" (save) -- Promotes the attachment, deletes replaced ones.
|
18
|
-
# * "after commit" (destroy) -- Deletes the attachment.
|
19
|
-
#
|
20
|
-
# Note that ActiveRecord versions 3.x and 4.x have errors automatically
|
21
|
-
# silenced in hooks, which can make debugging more difficult, so it's
|
22
|
-
# recommended that you enable errors:
|
23
|
-
#
|
24
|
-
# # This is the default in ActiveRecord 5
|
25
|
-
# ActiveRecord::Base.raise_in_transactional_callbacks = true
|
26
|
-
#
|
27
|
-
# If you want to put promoting/deleting into a background job, see the
|
28
|
-
# `backgrounding` plugin.
|
29
|
-
#
|
30
|
-
# Since attaching first saves the record with a cached attachment, then
|
31
|
-
# saves again with a stored attachment, you can detect this in callbacks:
|
32
|
-
#
|
33
|
-
# class User < ActiveRecord::Base
|
34
|
-
# include ImageUploader::Attachment.new(:avatar)
|
35
|
-
#
|
36
|
-
# before_save do
|
37
|
-
# if avatar_data_changed? && avatar_attacher.cached?
|
38
|
-
# # cached
|
39
|
-
# elsif avatar_data_changed? && avatar_attacher.stored?
|
40
|
-
# # promoted
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# Note that ActiveRecord currently has a [bug with transaction callbacks],
|
46
|
-
# so if you have any "after commit" callbacks, make sure to include Shrine's
|
47
|
-
# attachment module *after* they have all been defined.
|
48
|
-
#
|
49
|
-
# If you don't want the attachment module to add any callbacks to the
|
50
|
-
# model, and would instead prefer to call these actions manually, you can
|
51
|
-
# disable callbacks:
|
52
|
-
#
|
53
|
-
# plugin :activerecord, callbacks: false
|
54
|
-
#
|
55
|
-
# ## Validations
|
56
|
-
#
|
57
|
-
# Additionally, any Shrine validation errors will be added to
|
58
|
-
# ActiveRecord's errors upon validation. Note that Shrine validation
|
59
|
-
# messages don't have to be strings, they can also be symbols or symbols
|
60
|
-
# and options, which allows them to be internationalized together with
|
61
|
-
# other ActiveRecord validation messages.
|
62
|
-
#
|
63
|
-
# class MyUploader < Shrine
|
64
|
-
# plugin :validation_helpers
|
65
|
-
#
|
66
|
-
# Attacher.validate do
|
67
|
-
# validate_max_size 256 * 1024**2, message: ->(max) { [:max_size, max: max] }
|
68
|
-
# end
|
69
|
-
# end
|
70
|
-
#
|
71
|
-
# If you want to validate presence of the attachment, you can do it
|
72
|
-
# directly on the model.
|
73
|
-
#
|
74
|
-
# class User < ActiveRecord::Base
|
75
|
-
# include ImageUploader::Attachment.new(:avatar)
|
76
|
-
# validates_presence_of :avatar
|
77
|
-
# end
|
78
|
-
#
|
79
|
-
# If don't want the attachment module to merge file validations errors into
|
80
|
-
# model errors, you can disable it:
|
81
|
-
#
|
82
|
-
# plugin :activerecord, validations: false
|
83
|
-
#
|
84
|
-
# [bug with transaction callbacks]: https://github.com/rails/rails/issues/14493
|
85
7
|
module Activerecord
|
86
8
|
def self.configure(uploader, opts = {})
|
87
9
|
uploader.opts[:activerecord_callbacks] = opts.fetch(:callbacks, uploader.opts.fetch(:activerecord_callbacks, true))
|
@@ -2,86 +2,6 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
-
# The `add_metadata` plugin provides a convenient method for extracting and
|
6
|
-
# adding custom metadata values.
|
7
|
-
#
|
8
|
-
# plugin :add_metadata
|
9
|
-
#
|
10
|
-
# add_metadata :exif do |io, context|
|
11
|
-
# begin
|
12
|
-
# Exif::Data.new(io).to_h
|
13
|
-
# rescue Exif::NotReadable # not a valid image
|
14
|
-
# {}
|
15
|
-
# end
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# The above will add "exif" to the metadata hash, and also create the
|
19
|
-
# `#exif` reader method on Shrine::UploadedFile.
|
20
|
-
#
|
21
|
-
# image.metadata["exif"]
|
22
|
-
# # or
|
23
|
-
# image.exif
|
24
|
-
#
|
25
|
-
# You can also extract multiple metadata values at once, by using
|
26
|
-
# `add_metadata` without an argument and returning a hash of metadata.
|
27
|
-
#
|
28
|
-
# add_metadata do |io, context|
|
29
|
-
# begin
|
30
|
-
# data = Exif::Data.new(io)
|
31
|
-
# rescue Exif::NotReadable # not a valid image
|
32
|
-
# next {}
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# { date_time: data.date_time,
|
36
|
-
# flash: data.flash,
|
37
|
-
# focal_length: data.focal_length,
|
38
|
-
# exposure_time: data.exposure_time }
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# In this case Shrine won't automatically create reader methods for the
|
42
|
-
# extracted metadata on Shrine::UploadedFile, but you can create them via
|
43
|
-
# `#metadata_method`.
|
44
|
-
#
|
45
|
-
# metadata_method :date_time, :flash
|
46
|
-
#
|
47
|
-
# The `io` might not always be a file object, so if you're using an
|
48
|
-
# analyzer which requires the source file to be on disk, you can use
|
49
|
-
# `Shrine.with_file` to ensure you have a file object.
|
50
|
-
#
|
51
|
-
# add_metadata do |io, context|
|
52
|
-
# movie = Shrine.with_file(io) { |file| FFMPEG::Movie.new(file.path) }
|
53
|
-
#
|
54
|
-
# { "duration" => movie.duration,
|
55
|
-
# "bitrate" => movie.bitrate,
|
56
|
-
# "resolution" => movie.resolution,
|
57
|
-
# "frame_rate" => movie.frame_rate }
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
# Any previously extracted metadata can be accessed via
|
61
|
-
# `context[:metadata]`:
|
62
|
-
#
|
63
|
-
# add_metadata :foo do |io, context|
|
64
|
-
# context[:metadata] #=>
|
65
|
-
# # {
|
66
|
-
# # "size" => 239823,
|
67
|
-
# # "filename" => "nature.jpg",
|
68
|
-
# # "mime_type" => "image/jpeg"
|
69
|
-
# # }
|
70
|
-
#
|
71
|
-
# "foo"
|
72
|
-
# end
|
73
|
-
#
|
74
|
-
# add_metadata :bar do |io, context|
|
75
|
-
# context[:metadata] #=>
|
76
|
-
# # {
|
77
|
-
# # "size" => 239823,
|
78
|
-
# # "filename" => "nature.jpg",
|
79
|
-
# # "mime_type" => "image/jpeg",
|
80
|
-
# # "foo" => "foo"
|
81
|
-
# # }
|
82
|
-
#
|
83
|
-
# "bar"
|
84
|
-
# end
|
85
5
|
module AddMetadata
|
86
6
|
def self.configure(uploader)
|
87
7
|
uploader.opts[:metadata] ||= []
|
@@ -2,140 +2,6 @@
|
|
2
2
|
|
3
3
|
class Shrine
|
4
4
|
module Plugins
|
5
|
-
# The `backgrounding` plugin enables you to move promoting and deleting of
|
6
|
-
# files from record's lifecycle into background jobs. This is especially
|
7
|
-
# useful if you're doing processing and/or you're storing files on an
|
8
|
-
# external storage service.
|
9
|
-
#
|
10
|
-
# The plugin provides `Attacher.promote` and `Attacher.delete` methods,
|
11
|
-
# which allow you to hook up to promoting and deleting and spawn background
|
12
|
-
# jobs, by passing a block.
|
13
|
-
#
|
14
|
-
# Shrine.plugin :backgrounding
|
15
|
-
#
|
16
|
-
# # makes all uploaders use background jobs
|
17
|
-
# Shrine::Attacher.promote { |data| PromoteJob.perform_async(data) }
|
18
|
-
# Shrine::Attacher.delete { |data| DeleteJob.perform_async(data) }
|
19
|
-
#
|
20
|
-
# If you don't want to apply backgrounding for all uploaders, you can
|
21
|
-
# declare the hooks only for specific uploaders (in this case it's still
|
22
|
-
# recommended to keep the plugin loaded globally).
|
23
|
-
#
|
24
|
-
# class MyUploader < Shrine
|
25
|
-
# # makes this uploader use background jobs
|
26
|
-
# Attacher.promote { |data| PromoteJob.perform_async(data) }
|
27
|
-
# Attacher.delete { |data| DeleteJob.perform_async(data) }
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# The yielded `data` variable is a serializable hash containing all context
|
31
|
-
# needed for promotion/deletion. Now you just need to declare the job
|
32
|
-
# classes, and inside them call `Attacher.promote` or `Attacher.delete`,
|
33
|
-
# this time with the received data.
|
34
|
-
#
|
35
|
-
# class PromoteJob
|
36
|
-
# include Sidekiq::Worker
|
37
|
-
# def perform(data)
|
38
|
-
# Shrine::Attacher.promote(data)
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# class DeleteJob
|
43
|
-
# include Sidekiq::Worker
|
44
|
-
# def perform(data)
|
45
|
-
# Shrine::Attacher.delete(data)
|
46
|
-
# end
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# This example used Sidekiq, but obviously you could just as well use
|
50
|
-
# any other backgrounding library. This setup will be applied globally for
|
51
|
-
# all uploaders.
|
52
|
-
#
|
53
|
-
# If you're generating versions, and you want to process some versions in
|
54
|
-
# the foreground before kicking off a background job, you can use the
|
55
|
-
# `recache` plugin.
|
56
|
-
#
|
57
|
-
# In your application you can use `Attacher#cached?` and `Attacher#stored?`
|
58
|
-
# to differentiate between your background job being in progress and
|
59
|
-
# having completed.
|
60
|
-
#
|
61
|
-
# if user.avatar_attacher.cached? # background job is still in progress
|
62
|
-
# # ...
|
63
|
-
# elsif user.avatar_attacher.stored? # background job has finished
|
64
|
-
# # ...
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
# ## `Attacher.promote` and `Attacher.delete`
|
68
|
-
#
|
69
|
-
# In background jobs, `Attacher.promote` and `Attacher.delete` will resolve
|
70
|
-
# all necessary objects, and do the promotion/deletion. If
|
71
|
-
# `Attacher.find_record` is defined (which comes with ORM plugins), model
|
72
|
-
# instances will be treated as database records, with the `#id` attribute
|
73
|
-
# assumed to represent the primary key. Then promotion will have the
|
74
|
-
# following behaviour:
|
75
|
-
#
|
76
|
-
# 1. retrieves the database record
|
77
|
-
# * if record is not found, it finishes
|
78
|
-
# * if record is found but attachment has changed, it finishes
|
79
|
-
# 2. uploads cached file to permanent storage
|
80
|
-
# 3. reloads the database record
|
81
|
-
# * if record is not found, it deletes the promoted files and finishes
|
82
|
-
# * if record is found but attachment has changed, it deletes the promoted files and finishes
|
83
|
-
# 4. updates the record with the promoted files
|
84
|
-
#
|
85
|
-
# Both `Attacher.promote` and `Attacher.delete` return a `Shrine::Attacher`
|
86
|
-
# instance (if the action hasn't aborted), so you can use it to perform
|
87
|
-
# additional tasks:
|
88
|
-
#
|
89
|
-
# def perform(data)
|
90
|
-
# attacher = Shrine::Attacher.promote(data)
|
91
|
-
# attacher.record.update(published: true) if attacher && attacher.record.is_a?(Post)
|
92
|
-
# end
|
93
|
-
#
|
94
|
-
# ### Plain models
|
95
|
-
#
|
96
|
-
# You can also do backgrounding with plain models which don't represent
|
97
|
-
# database records; the plugin will use that mode if `Attacher.find_record`
|
98
|
-
# is not defined. In that case promotion will have the following behaviour:
|
99
|
-
#
|
100
|
-
# 1. instantiates the model
|
101
|
-
# 2. uploads cached file to permanent storage
|
102
|
-
# 3. writes promoted files to the model instance
|
103
|
-
#
|
104
|
-
# You can then retrieve the promoted files via the attacher object that
|
105
|
-
# `Attacher.promote` returns, and do any additional tasks if you need to.
|
106
|
-
#
|
107
|
-
# ## `Attacher#_promote` and `Attacher#_delete`
|
108
|
-
#
|
109
|
-
# The plugin modifies `Attacher#_promote` and `Attacher#_delete` to call
|
110
|
-
# the registered blocks with serializable attacher data, and these methods
|
111
|
-
# are internally called by the attacher. `Attacher#promote` and
|
112
|
-
# `Attacher#delete!` remain synchronous.
|
113
|
-
#
|
114
|
-
# # asynchronous (spawn background jobs)
|
115
|
-
# attacher._promote
|
116
|
-
# attacher._delete(attachment)
|
117
|
-
#
|
118
|
-
# # synchronous
|
119
|
-
# attacher.promote
|
120
|
-
# attacher.delete!(attachment)
|
121
|
-
#
|
122
|
-
# ## `Attacher.dump` and `Attacher.load`
|
123
|
-
#
|
124
|
-
# The plugin adds `Attacher.dump` and `Attacher.load` methods for
|
125
|
-
# serializing attacher object and loading it back up. You can use them to
|
126
|
-
# spawn background jobs for custom tasks.
|
127
|
-
#
|
128
|
-
# data = Shrine::Attacher.dump(attacher)
|
129
|
-
# SomethingJob.perform_async(data)
|
130
|
-
#
|
131
|
-
# # ...
|
132
|
-
#
|
133
|
-
# class SomethingJob
|
134
|
-
# def perform(data)
|
135
|
-
# attacher = Shrine::Attacher.load(data)
|
136
|
-
# # ...
|
137
|
-
# end
|
138
|
-
# end
|
139
5
|
module Backgrounding
|
140
6
|
module AttacherClassMethods
|
141
7
|
# If block is passed in, stores it to be called on promotion. Otherwise
|