shrine 2.4.1 → 2.5.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/README.md +510 -416
- data/doc/carrierwave.md +4 -2
- data/doc/direct_s3.md +21 -2
- data/doc/multiple_files.md +118 -0
- data/doc/paperclip.md +4 -2
- data/doc/testing.md +18 -5
- data/lib/shrine.rb +3 -1
- data/lib/shrine/plugins/add_metadata.rb +50 -12
- data/lib/shrine/plugins/backgrounding.rb +103 -51
- data/lib/shrine/plugins/default_url.rb +32 -10
- data/lib/shrine/plugins/direct_upload.rb +3 -1
- data/lib/shrine/plugins/processing.rb +35 -16
- data/lib/shrine/plugins/rack_file.rb +54 -21
- data/lib/shrine/plugins/remove_invalid.rb +1 -0
- data/lib/shrine/plugins/store_dimensions.rb +14 -5
- data/lib/shrine/plugins/upload_options.rb +5 -0
- data/lib/shrine/plugins/validation_helpers.rb +32 -37
- data/lib/shrine/plugins/versions.rb +69 -47
- data/lib/shrine/storage/file_system.rb +3 -3
- data/lib/shrine/storage/linter.rb +1 -1
- data/lib/shrine/storage/s3.rb +8 -4
- data/lib/shrine/version.rb +2 -2
- metadata +3 -2
@@ -12,22 +12,27 @@ class Shrine
|
|
12
12
|
# plugin :processing
|
13
13
|
#
|
14
14
|
# process(:store) do |io, context|
|
15
|
-
#
|
16
|
-
# size_500 = resize_to_limit(size_700, 500, 500)
|
17
|
-
# size_300 = resize_to_limit(size_500, 300, 300)
|
15
|
+
# original = io.download
|
18
16
|
#
|
19
|
-
#
|
17
|
+
# size_800 = resize_to_limit!(original, 800, 800)
|
18
|
+
# size_500 = resize_to_limit(size_800, 500, 500)
|
19
|
+
# size_300 = resize_to_limit(size_500, 300, 300)
|
20
|
+
#
|
21
|
+
# {large: size_800, medium: size_500, small: size_300}
|
20
22
|
# end
|
21
23
|
#
|
24
|
+
# You probably want to load the `delete_raw` plugin to automatically
|
25
|
+
# delete processed files after they have been uploaded.
|
26
|
+
#
|
22
27
|
# Now when you access the stored attachment through the model, a hash of
|
23
28
|
# uploaded files will be returned:
|
24
29
|
#
|
25
|
-
#
|
26
|
-
# # {
|
27
|
-
# # "large"
|
28
|
-
# # "medium"
|
29
|
-
# # "small"
|
30
|
-
# # }
|
30
|
+
# user.avatar_data #=>
|
31
|
+
# # '{
|
32
|
+
# # "large": {"id":"lg043.jpg", "storage":"store", "metadata":{...}},
|
33
|
+
# # "medium": {"id":"kd9fk.jpg", "storage":"store", "metadata":{...}},
|
34
|
+
# # "small": {"id":"932fl.jpg", "storage":"store", "metadata":{...}}
|
35
|
+
# # }'
|
31
36
|
#
|
32
37
|
# user.avatar #=>
|
33
38
|
# # {
|
@@ -36,43 +41,50 @@ class Shrine
|
|
36
41
|
# # :small => #<Shrine::UploadedFile @data={"id"=>"932fl.jpg", ...}>,
|
37
42
|
# # }
|
38
43
|
#
|
39
|
-
#
|
40
|
-
# user.avatar[:
|
41
|
-
# user.avatar[:medium].width #=> 500
|
42
|
-
# user.avatar[:small].width #=> 300
|
44
|
+
# user.avatar[:medium] #=> #<Shrine::UploadedFile>
|
45
|
+
# user.avatar[:medium].url #=> "/uploads/store/lg043.jpg"
|
43
46
|
#
|
44
|
-
#
|
45
|
-
#
|
47
|
+
# The plugin also extends the `Attacher#url` to accept versions:
|
48
|
+
#
|
49
|
+
# user.avatar_url(:large)
|
50
|
+
# user.avatar_url(:small, download: true) # with URL options
|
46
51
|
#
|
47
52
|
# ## Original file
|
48
53
|
#
|
49
|
-
# If you want to keep the original file, you can
|
50
|
-
#
|
51
|
-
# IO-like object), which might avoid downloading depending on the storage:
|
54
|
+
# If you want to keep the original file, you can include the original
|
55
|
+
# `Shrine::UploadedFile` object as one of the versions:
|
52
56
|
#
|
53
57
|
# process(:store) do |io, context|
|
54
58
|
# # processing thumbnail
|
55
59
|
# {original: io, thumbnail: thumbnail}
|
56
60
|
# end
|
57
61
|
#
|
62
|
+
# If both temporary and permanent storage are Amazon S3, the cached original
|
63
|
+
# will simply be copied over to permanent storage (without any downloading
|
64
|
+
# and reuploading), so in these cases the performance impact of storing the
|
65
|
+
# original file in addition to processed versions is neglibible.
|
66
|
+
#
|
58
67
|
# ## Fallbacks
|
59
68
|
#
|
60
|
-
#
|
61
|
-
#
|
69
|
+
# If versions are processed in a background job, there will be a period
|
70
|
+
# where the user will browse the site before versions have finished
|
71
|
+
# processing. In this period `Attacher#url` will by default fall back to
|
72
|
+
# the original file.
|
62
73
|
#
|
63
|
-
# user.
|
74
|
+
# user.avatar #=> #<Shrine::UploadedFile>
|
75
|
+
# user.avatar_url(:large) # falls back to `user.avatar_url`
|
64
76
|
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
77
|
+
# This behaviour is convenient if you want to gracefully degrade to the
|
78
|
+
# cached file until the background job has finished processing. However, if
|
79
|
+
# you would rather provide your own default URLs for versions, you can
|
80
|
+
# disable this fallback:
|
68
81
|
#
|
69
|
-
#
|
70
|
-
# to gracefully degrade before the background job finishes.
|
82
|
+
# plugin :versions, fallback_to_original: false
|
71
83
|
#
|
72
|
-
# If you already have some versions processed in the foreground
|
73
|
-
# background job is kicked off (with the `recache` plugin),
|
74
|
-
#
|
75
|
-
#
|
84
|
+
# If you already have some versions processed in the foreground after a
|
85
|
+
# background job is kicked off (with the `recache` plugin), you can have
|
86
|
+
# URLs for versions that are yet to be processed fall back to existing
|
87
|
+
# versions:
|
76
88
|
#
|
77
89
|
# plugin :versions, fallbacks: {
|
78
90
|
# :thumb_2x => :thumb,
|
@@ -84,22 +96,17 @@ class Shrine
|
|
84
96
|
# user.avatar_url(:thumb_2x) # returns :thumb URL until :thumb_2x becomes available
|
85
97
|
# user.avatar_url(:large_2x) # returns :large URL until :large_2x becomes available
|
86
98
|
#
|
87
|
-
# Any additional options will be properly forwarded to the underlying
|
88
|
-
# storage:
|
89
|
-
#
|
90
|
-
# user.avatar_url(:medium, download: true)
|
91
|
-
#
|
92
99
|
# ## Context
|
93
100
|
#
|
94
|
-
# The
|
95
|
-
#
|
101
|
+
# The version name will be available via `:version` when generating
|
102
|
+
# location or a default URL.
|
96
103
|
#
|
97
104
|
# def generate_location(io, context)
|
98
105
|
# "uploads/#{context[:version]}-#{super}"
|
99
106
|
# end
|
100
107
|
#
|
101
|
-
#
|
102
|
-
# "/images/defaults/#{
|
108
|
+
# Attacher.default_url do |options|
|
109
|
+
# "/images/defaults/#{options[:version]}.jpg"
|
103
110
|
# end
|
104
111
|
#
|
105
112
|
# [image_processing]: https://github.com/janko-m/image_processing
|
@@ -114,6 +121,7 @@ class Shrine
|
|
114
121
|
|
115
122
|
uploader.opts[:version_names] = opts.fetch(:names, uploader.opts[:version_names])
|
116
123
|
uploader.opts[:version_fallbacks] = opts.fetch(:fallbacks, uploader.opts.fetch(:version_fallbacks, {}))
|
124
|
+
uploader.opts[:versions_fallback_to_original] = opts.fetch(:fallback_to_original, uploader.opts.fetch(:versions_fallback_to_original, true))
|
117
125
|
end
|
118
126
|
|
119
127
|
module ClassMethods
|
@@ -163,6 +171,10 @@ class Shrine
|
|
163
171
|
def _store(io, context)
|
164
172
|
if (hash = io).is_a?(Hash)
|
165
173
|
raise Error, ":location is not applicable to versions" if context.key?(:location)
|
174
|
+
if duplicates = hash.keys.group_by{|k| hash[k]}.values.reject(&:one?).first
|
175
|
+
raise Error, "The following version keys point to the same IO object: #{duplicates.inspect}. Shrine cannot upload the same IO object multiple times."
|
176
|
+
end
|
177
|
+
|
166
178
|
hash.inject({}) do |result, (name, version)|
|
167
179
|
result.update(name => _store(version, version: name, **context))
|
168
180
|
end
|
@@ -186,29 +198,39 @@ class Shrine
|
|
186
198
|
# Smart versioned URLs, which include the version name in the default
|
187
199
|
# URL, and properly forwards any options to the underlying storage.
|
188
200
|
def url(version = nil, **options)
|
189
|
-
|
201
|
+
attachment = get
|
202
|
+
|
203
|
+
if attachment.is_a?(Hash)
|
190
204
|
if version
|
191
|
-
if
|
192
|
-
|
205
|
+
if attachment.key?(version)
|
206
|
+
attachment[version].url(**options)
|
193
207
|
elsif fallback = shrine_class.version_fallbacks[version]
|
194
208
|
url(fallback, **options)
|
195
209
|
else
|
196
210
|
default_url(**options, version: version)
|
197
211
|
end
|
198
212
|
else
|
199
|
-
raise Error, "must call #
|
213
|
+
raise Error, "must call Shrine::Attacher#url with the name of the version"
|
200
214
|
end
|
201
215
|
else
|
202
|
-
if
|
203
|
-
|
216
|
+
if version
|
217
|
+
if attachment && fallback_to_original?
|
218
|
+
attachment.url(**options)
|
219
|
+
else
|
220
|
+
default_url(**options, version: version)
|
221
|
+
end
|
204
222
|
else
|
205
|
-
|
223
|
+
super(**options)
|
206
224
|
end
|
207
225
|
end
|
208
226
|
end
|
209
227
|
|
210
228
|
private
|
211
229
|
|
230
|
+
def fallback_to_original?
|
231
|
+
shrine_class.opts[:versions_fallback_to_original]
|
232
|
+
end
|
233
|
+
|
212
234
|
def assign_cached(value)
|
213
235
|
cached_file = uploaded_file(value)
|
214
236
|
warn "Assigning cached hash of files is deprecated for security reasons and will be removed in Shrine 3." if cached_file.is_a?(Hash)
|
@@ -42,8 +42,8 @@ class Shrine
|
|
42
42
|
# ## Clearing cache
|
43
43
|
#
|
44
44
|
# If you're using FileSystem as cache, you will probably want to
|
45
|
-
# periodically delete old files which aren't used anymore. You can
|
46
|
-
#
|
45
|
+
# periodically delete old files which aren't used anymore. You can run
|
46
|
+
# something like this periodically:
|
47
47
|
#
|
48
48
|
# file_system = Shrine.storages[:cache]
|
49
49
|
# file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
|
@@ -187,7 +187,7 @@ class Shrine
|
|
187
187
|
if name == :download
|
188
188
|
warn "Shrine::Storage::FileSystem#download is deprecated and will be removed in Shrine 3."
|
189
189
|
require "down"
|
190
|
-
open(
|
190
|
+
open(*args) { |file| Down.copy_to_tempfile(*args, file) }
|
191
191
|
else
|
192
192
|
super
|
193
193
|
end
|
@@ -85,7 +85,7 @@ class Shrine
|
|
85
85
|
storage.delete(id)
|
86
86
|
error :delete, "file still #exists? after deleting" if storage.exists?(id)
|
87
87
|
begin
|
88
|
-
storage.delete(
|
88
|
+
storage.delete(id)
|
89
89
|
rescue => exception
|
90
90
|
error :delete, "shouldn't fail if the file doesn't exist, but raised #{exception.class}"
|
91
91
|
end
|
data/lib/shrine/storage/s3.rb
CHANGED
@@ -39,7 +39,7 @@ class Shrine
|
|
39
39
|
# [copying] and [presigning].
|
40
40
|
#
|
41
41
|
# You can also generate upload options per upload with the `upload_options`
|
42
|
-
# plugin
|
42
|
+
# plugin
|
43
43
|
#
|
44
44
|
# class MyUploader < Shrine
|
45
45
|
# plugin :upload_options, store: ->(io, context) do
|
@@ -51,9 +51,13 @@ class Shrine
|
|
51
51
|
# end
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
54
|
+
# or when using the uploader directly
|
55
|
+
#
|
56
|
+
# uploader.upload(file, upload_options: {acl: "public-read"})
|
57
|
+
#
|
58
|
+
# Note that, unlike the `:upload_options` storage option, upload options
|
59
|
+
# given on the uploader level won't be forwarded for generating presigns,
|
60
|
+
# since presigns are generated using the storage directly.
|
57
61
|
#
|
58
62
|
# ## URL options
|
59
63
|
#
|
data/lib/shrine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shrine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: down
|
@@ -301,6 +301,7 @@ files:
|
|
301
301
|
- doc/design.md
|
302
302
|
- doc/direct_s3.md
|
303
303
|
- doc/migrating_storage.md
|
304
|
+
- doc/multiple_files.md
|
304
305
|
- doc/paperclip.md
|
305
306
|
- doc/refile.md
|
306
307
|
- doc/regenerating_versions.md
|