shrine 1.0.0 → 1.1.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 +101 -149
- data/doc/carrierwave.md +12 -16
- data/doc/changing_location.md +50 -0
- data/doc/creating_plugins.md +2 -2
- data/doc/creating_storages.md +70 -9
- data/doc/direct_s3.md +132 -61
- data/doc/migrating_storage.md +12 -10
- data/doc/paperclip.md +12 -17
- data/doc/refile.md +338 -0
- data/doc/regenerating_versions.md +75 -11
- data/doc/securing_uploads.md +172 -0
- data/lib/shrine.rb +21 -16
- data/lib/shrine/plugins/activerecord.rb +2 -2
- data/lib/shrine/plugins/background_helpers.rb +2 -148
- data/lib/shrine/plugins/backgrounding.rb +148 -0
- data/lib/shrine/plugins/backup.rb +88 -0
- data/lib/shrine/plugins/data_uri.rb +25 -4
- data/lib/shrine/plugins/default_url.rb +37 -0
- data/lib/shrine/plugins/delete_uploaded.rb +40 -0
- data/lib/shrine/plugins/determine_mime_type.rb +4 -2
- data/lib/shrine/plugins/direct_upload.rb +107 -62
- data/lib/shrine/plugins/download_endpoint.rb +157 -0
- data/lib/shrine/plugins/hooks.rb +19 -5
- data/lib/shrine/plugins/keep_location.rb +43 -0
- data/lib/shrine/plugins/moving.rb +11 -10
- data/lib/shrine/plugins/parallelize.rb +1 -5
- data/lib/shrine/plugins/parsed_json.rb +7 -1
- data/lib/shrine/plugins/pretty_location.rb +6 -0
- data/lib/shrine/plugins/rack_file.rb +7 -1
- data/lib/shrine/plugins/remove_invalid.rb +22 -0
- data/lib/shrine/plugins/sequel.rb +2 -2
- data/lib/shrine/plugins/upload_options.rb +41 -0
- data/lib/shrine/plugins/versions.rb +9 -7
- data/lib/shrine/storage/file_system.rb +46 -30
- data/lib/shrine/storage/linter.rb +48 -25
- data/lib/shrine/storage/s3.rb +89 -22
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +3 -3
- metadata +16 -5
data/doc/paperclip.md
CHANGED
@@ -22,10 +22,6 @@ class ImageUploader < Shrine
|
|
22
22
|
def process(io, context)
|
23
23
|
# processing
|
24
24
|
end
|
25
|
-
|
26
|
-
def default_url(context)
|
27
|
-
# default URL
|
28
|
-
end
|
29
25
|
end
|
30
26
|
```
|
31
27
|
|
@@ -37,8 +33,8 @@ you can instantiate uploaders with a specific storage:
|
|
37
33
|
require "shrine/storage/file_system"
|
38
34
|
|
39
35
|
Shrine.storages = {
|
40
|
-
cache: Shrine::Storage::FileSystem.new("public",
|
41
|
-
store: Shrine::Storage::FileSystem.new("public",
|
36
|
+
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
|
37
|
+
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
|
42
38
|
}
|
43
39
|
```
|
44
40
|
```rb
|
@@ -74,9 +70,9 @@ end
|
|
74
70
|
#### Regenerating versions
|
75
71
|
|
76
72
|
Shrine doesn't have a built-in way of regenerating versions, because that's
|
77
|
-
very individual and depends on what versions you want regenerated, what ORM
|
78
|
-
|
79
|
-
|
73
|
+
very individual and depends on what versions you want regenerated, what ORM are
|
74
|
+
you using, how many records there are in your database etc. The [Regenerating
|
75
|
+
versions] guide provides some useful tips on this task.
|
80
76
|
|
81
77
|
### Logging
|
82
78
|
|
@@ -208,8 +204,8 @@ which you have to register:
|
|
208
204
|
require "shrine/storage/file_system"
|
209
205
|
|
210
206
|
Shrine.storages = {
|
211
|
-
cache: Shrine::Storage::FileSystem.new("public",
|
212
|
-
store: Shrine::Storage::FileSystem.new("public",
|
207
|
+
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
|
208
|
+
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
|
213
209
|
}
|
214
210
|
```
|
215
211
|
|
@@ -222,13 +218,12 @@ As explained in the "Processing" section, processing is done by overriding the
|
|
222
218
|
|
223
219
|
#### `:default_url`
|
224
220
|
|
225
|
-
|
226
|
-
uploader:
|
221
|
+
For default URLs you can use the `default_url` plugin:
|
227
222
|
|
228
223
|
```rb
|
229
224
|
class ImageUploader < Shrine
|
230
|
-
|
231
|
-
"/
|
225
|
+
plugin :default_url do |context|
|
226
|
+
"/attachments/#{context[:name]}/default.jpg"
|
232
227
|
end
|
233
228
|
end
|
234
229
|
```
|
@@ -301,8 +296,8 @@ user.avatar.id #=> "users/342/avatar/398543qjfdsf.jpg"
|
|
301
296
|
|
302
297
|
#### `#reprocess!`
|
303
298
|
|
304
|
-
Shrine doesn't have an equivalent to this, but
|
305
|
-
|
299
|
+
Shrine doesn't have an equivalent to this, but the [Regenerating versions]
|
300
|
+
guide provides some useful tips on how to do this.
|
306
301
|
|
307
302
|
[file]: http://linux.die.net/man/1/file
|
308
303
|
[Regenerating versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
|
data/doc/refile.md
ADDED
@@ -0,0 +1,338 @@
|
|
1
|
+
# Shrine for Refile Users
|
2
|
+
|
3
|
+
This guide is aimed at helping Refile users transition to Shrine. We will first
|
4
|
+
generally mention what are the key differences, and afterwards we will give a
|
5
|
+
complete reference of Refile's interface and note what is the equivalent in
|
6
|
+
Shrine.
|
7
|
+
|
8
|
+
## Uploaders
|
9
|
+
|
10
|
+
Shrine has the concept of storages very similar to Refile's backends. However,
|
11
|
+
while in Refile you usually work with storages directly, in Shrine you use
|
12
|
+
*uploaders* which act as wrappers around storages, and they are subclasses of
|
13
|
+
`Shrine`:
|
14
|
+
|
15
|
+
```rb
|
16
|
+
require "shrine/storage/file_system"
|
17
|
+
require "shrine/storage/s3"
|
18
|
+
|
19
|
+
Shrine.storages = {
|
20
|
+
cache: Shrine::Storage::FileSystem.new(*args),
|
21
|
+
store: Shrine::Storage::S3.new(*args),
|
22
|
+
}
|
23
|
+
```
|
24
|
+
```rb
|
25
|
+
class ImageUploader < Shrine
|
26
|
+
# uploading logic
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
While in Refile you configure attachments by passing options to `.attachment`,
|
31
|
+
in Shrine you define all your uploading logic inside uploaders, and then
|
32
|
+
generate an attacment module with that uploader which is included into the
|
33
|
+
model:
|
34
|
+
|
35
|
+
```rb
|
36
|
+
class ImageUploader < Shrine
|
37
|
+
plugin :store_dimensions
|
38
|
+
plugin :determine_mime_type
|
39
|
+
plugin :keep_files, destroyed: true
|
40
|
+
end
|
41
|
+
```
|
42
|
+
```rb
|
43
|
+
class User
|
44
|
+
include ImageUploader[:avatar] # requires "avatar_data" column
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Unlike Refile which has just a few options of configuring attachments, Shrine
|
49
|
+
has a very rich arsenal of features via plugins, and allows you to share your
|
50
|
+
uploading logic between uploaders through inheritance.
|
51
|
+
|
52
|
+
### ORMs
|
53
|
+
|
54
|
+
In Refile you extend the model with an Attachment module specific to the ORM
|
55
|
+
you're using. In Shrine you load the appropriate ORM plugin:
|
56
|
+
|
57
|
+
```rb
|
58
|
+
Shrine.plugin :sequel # or :activerecord
|
59
|
+
```
|
60
|
+
```rb
|
61
|
+
class Photo < Sequel::Model
|
62
|
+
include ImageUploader[:image]
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
These integrations work much like Refile; on assignment the file is cached,
|
67
|
+
and on saving the record file is moved from cache to store. Shrine doesn't
|
68
|
+
provide form helpers for Rails, because it's so easy to do it yourself:
|
69
|
+
|
70
|
+
```erb
|
71
|
+
<%= form_for @photo do |f| %>
|
72
|
+
<%= f.hidden_field :image, value: @photo.image_data %>
|
73
|
+
<%= f.file_field :image %>
|
74
|
+
<% end %>
|
75
|
+
```
|
76
|
+
|
77
|
+
### URLs
|
78
|
+
|
79
|
+
To get file URLs, in Shrine you just call `#url` on the file:
|
80
|
+
|
81
|
+
```rb
|
82
|
+
@photo.image.url
|
83
|
+
@photo.image_url # returns nil if attachment is missing
|
84
|
+
```
|
85
|
+
|
86
|
+
If you're using storages which don't expose files over URL, or you want to
|
87
|
+
secure your downloads, you can use the `download_endpoint` plugin.
|
88
|
+
|
89
|
+
### Metadata
|
90
|
+
|
91
|
+
While in Refile you're required to have a separate column for each metadata you
|
92
|
+
want to save (filename, size, content type), in Shrine all of the metadata are
|
93
|
+
stored in a single column (for "avatar" it's `avatar_data` column) as JSON.
|
94
|
+
|
95
|
+
```rb
|
96
|
+
user.avatar_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
|
97
|
+
```
|
98
|
+
|
99
|
+
By default Shrine stores "filename", "size" and "mime_type" metadata, but you
|
100
|
+
can also store image dimensions by loading the `store_dimensions` plugin.
|
101
|
+
|
102
|
+
### Processing
|
103
|
+
|
104
|
+
One of the key differences between Refile and Shrine is that in Refile you do
|
105
|
+
processing on-the-fly (like Dragonfly), while in Shrine you do your processing
|
106
|
+
on upload (like CarrierWave and Paperclip). However, there are storages which
|
107
|
+
you can use which support on-the-fly processing, like [shrine-cloudinary] or
|
108
|
+
[shrine-imgix].
|
109
|
+
|
110
|
+
In Shrine you do processing by overriding the `#process` method on your
|
111
|
+
uploader (for images you can use the [image_processing] gem):
|
112
|
+
|
113
|
+
```rb
|
114
|
+
require "image_processing/mini_magick"
|
115
|
+
|
116
|
+
class ImageUploader < Shrine
|
117
|
+
include ImageProcessing::MiniMagick
|
118
|
+
|
119
|
+
def process(io, context)
|
120
|
+
case context[:phase]
|
121
|
+
when :store
|
122
|
+
resize_to_fit!(io.download, 700, 700)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
### Validations
|
129
|
+
|
130
|
+
While in Refile you can do extension, mime type and filesize validation by
|
131
|
+
passing options to `.attachment`, in Shrine you do this logic instance level,
|
132
|
+
with the help of the `validation_helpers` plugin:
|
133
|
+
|
134
|
+
```rb
|
135
|
+
class ImageUploader < Shrine
|
136
|
+
plugin :validation_helpers
|
137
|
+
|
138
|
+
Attacher.validate do
|
139
|
+
validate_mime_type_inclusion ["image/jpeg", "image/png", "image/gif"]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
### Direct uploads
|
145
|
+
|
146
|
+
Shrine borrows Refile's idea of direct uploads, and ships with a
|
147
|
+
`direct_upload` plugin which provides the endpoint that you can mount:
|
148
|
+
|
149
|
+
```rb
|
150
|
+
class ImageUploader < Shrine
|
151
|
+
plugin :direct_upload
|
152
|
+
end
|
153
|
+
```
|
154
|
+
```rb
|
155
|
+
# config/routes.rb
|
156
|
+
Rails.application.routes.draw do
|
157
|
+
mount ImageUploader::UploadEndpoint => "/attachments/images"
|
158
|
+
end
|
159
|
+
```
|
160
|
+
```rb
|
161
|
+
# POST /attachments/images/cache/avatar
|
162
|
+
{
|
163
|
+
"id": "43kewit94.jpg",
|
164
|
+
"storage": "cache",
|
165
|
+
"metadata": {
|
166
|
+
"size": 384393,
|
167
|
+
"filename": "nature.jpg",
|
168
|
+
"mime_type": "image/jpeg"
|
169
|
+
}
|
170
|
+
}
|
171
|
+
```
|
172
|
+
|
173
|
+
Unlike Refile, Shrine doesn't ship with a JavaScript script which you can just
|
174
|
+
include to make it work. Instead, you're expected to use one of the many
|
175
|
+
excellent JavaScript libraries for generic file uploads, for example
|
176
|
+
[jQuery-File-Upload].
|
177
|
+
|
178
|
+
#### Presigned S3 uploads
|
179
|
+
|
180
|
+
The `direct_upload` plugin also provides an endpoint for getting S3 presigns,
|
181
|
+
you just need to pass the `presign: true` option. In the same way as with regular
|
182
|
+
direct uploads, you can use a generic JavaScript file upload library. For the
|
183
|
+
details read the [Direct Uploads to S3] guide.
|
184
|
+
|
185
|
+
### Multiple uploads
|
186
|
+
|
187
|
+
Shrine doesn't have a built-in solution for accepting multiple uploads, but
|
188
|
+
it's actually very easy to do manually, see the [example app] on how you can do
|
189
|
+
multiple uploads directly to S3.
|
190
|
+
|
191
|
+
## Refile to Shrine direct mapping
|
192
|
+
|
193
|
+
### `Refile`
|
194
|
+
|
195
|
+
#### `.cache`, `.store`, `.backends`
|
196
|
+
|
197
|
+
Shrine calles these "storages", and it doesn't have special accessors for
|
198
|
+
`:cache` and `:store`:
|
199
|
+
|
200
|
+
```rb
|
201
|
+
Shrine.storages = {
|
202
|
+
cache: Shrine::Storage::Foo.new(*args),
|
203
|
+
store: Shrine::Storage::Bar.new(*args),
|
204
|
+
}
|
205
|
+
```
|
206
|
+
|
207
|
+
#### `.app`, `.mount_point`, `.automount`
|
208
|
+
|
209
|
+
The `direct_upload` plugin provides a subset of Refile's app's functionality,
|
210
|
+
and you have to mount it in your framework's router:
|
211
|
+
|
212
|
+
```rb
|
213
|
+
# config/routes.rb
|
214
|
+
Rails.application.routes.draw do
|
215
|
+
# adds `POST /attachments/images/:storage/:name`
|
216
|
+
mount ImageUploader::UploadEndpoint => "/attachments/images"
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
#### `.allow_uploads_to`
|
221
|
+
|
222
|
+
```rb
|
223
|
+
Shrine.plugin :direct_upload, storages: [:cache]
|
224
|
+
```
|
225
|
+
|
226
|
+
#### `.logger`
|
227
|
+
|
228
|
+
```rb
|
229
|
+
Shrine.plugin :logging
|
230
|
+
```
|
231
|
+
|
232
|
+
#### `.processors`, `.processor`
|
233
|
+
|
234
|
+
In Shrine processing is done by overriding the `#process` method in your
|
235
|
+
uploader:
|
236
|
+
|
237
|
+
```rb
|
238
|
+
class MyUploader < Shrine
|
239
|
+
def process(io, context)
|
240
|
+
# ...
|
241
|
+
end
|
242
|
+
end
|
243
|
+
```
|
244
|
+
|
245
|
+
#### `.types`
|
246
|
+
|
247
|
+
In Shrine validations are done by calling `.validate` on the attacher class:
|
248
|
+
|
249
|
+
```rb
|
250
|
+
class MyUploader < Shrine
|
251
|
+
plugin :validation_helpers
|
252
|
+
|
253
|
+
Attacher.validate do
|
254
|
+
validate_max_size 5*1024*1024
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
#### `.extract_filename`, `.extract_content_type`
|
260
|
+
|
261
|
+
In Shrine equivalents are (private) methods `Shrine#extract_filename` and
|
262
|
+
`Shrine#extract_mime_type`.
|
263
|
+
|
264
|
+
#### `.app_url`
|
265
|
+
|
266
|
+
You should use your framework to generate the URL to your mounted direct
|
267
|
+
enpdoint.
|
268
|
+
|
269
|
+
#### `.attachment_url`, `.file_url`
|
270
|
+
|
271
|
+
You can call `#url` on the uploaded file, or `#<name>_url` on the model.
|
272
|
+
Additionally you can use the `download_endpoint` plugin.
|
273
|
+
|
274
|
+
#### `.upload_url`, `.attachment_upload_url`, `.presign_url`, `.attachment_presign_url`
|
275
|
+
|
276
|
+
These should be generated directly by you, it depends on where you've mounted
|
277
|
+
the direct endpoint.
|
278
|
+
|
279
|
+
#### `.host`, `.cdn_host`, `.app_host`, `.allow_downloads_from`, `allow_origin`, `.content_max_age`
|
280
|
+
|
281
|
+
Not needed since Shrine doesn't offer on-the-fly processing.
|
282
|
+
|
283
|
+
#### `.secret_key`, `.token`, `.valid_token?`
|
284
|
+
|
285
|
+
Not needed since Shrine doesn't offer on-the-fly processing.
|
286
|
+
|
287
|
+
### `attachment`
|
288
|
+
|
289
|
+
Shrine's equivalent to calling the attachment is including an attachment module
|
290
|
+
of an uploader:
|
291
|
+
|
292
|
+
```rb
|
293
|
+
class User
|
294
|
+
include ImageUploader[:avatar]
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
#### `:extension`, `:content_type`, `:type`
|
299
|
+
|
300
|
+
In Shrine validations are done instance-level inside the uploader, most
|
301
|
+
commonly with the `validation_helpers` plugin:
|
302
|
+
|
303
|
+
```rb
|
304
|
+
class ImageUploader < Shrine
|
305
|
+
plugin :validation_helpers
|
306
|
+
|
307
|
+
Attacher.validate do
|
308
|
+
validate_extension_inclusion [/jpe?g/, "png"]
|
309
|
+
validate_mime_type_inclusion [/image/jpeg/, "image/png"]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
#### `:cache`, `:store`
|
315
|
+
|
316
|
+
Shrine provides a `default_storage` plugin for setting custom storages on the
|
317
|
+
uploader:
|
318
|
+
|
319
|
+
```rb
|
320
|
+
Shrine.storages[:custom_cache] = Shrine::Storage::Foo.new(*args)
|
321
|
+
Shrine.storages[:custom_store] = Shrine::Storage::Bar.new(*args)
|
322
|
+
```
|
323
|
+
```rb
|
324
|
+
class ImageUploader < Shrine
|
325
|
+
plugin :default_storage, cache: :custom_cache, store: :custom_store
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
#### `:raise_errors`
|
330
|
+
|
331
|
+
No equivalent currently exists in Shrine.
|
332
|
+
|
333
|
+
[shrine-cloudinary]: https://github.com/janko-m/shrine-cloudinary
|
334
|
+
[shrine-imgix]: https://github.com/janko-m/shrine-imgix
|
335
|
+
[image_processing]: https://github.com/janko-m/image_processing
|
336
|
+
[jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
|
337
|
+
[Direct Uploads to S3]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|
338
|
+
[example app]: https://github.com/janko-m/shrine-example
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Reprocessing Versions
|
2
2
|
|
3
3
|
While your app is serving uploads in production, you may realize that you want
|
4
4
|
to change how your attachment's versions are generated. This means that, in
|
@@ -6,15 +6,66 @@ addition to changing you processing code, you also need to reprocess the
|
|
6
6
|
existing attachments. This guide is aimed to help doing this migration with
|
7
7
|
zero downtime and no unused files left in the main storage.
|
8
8
|
|
9
|
-
##
|
9
|
+
## Adding versions
|
10
|
+
|
11
|
+
Most common scenario is when initially you're not doing any processing, but
|
12
|
+
later decide that you want to generate versions. First you need to update your
|
13
|
+
code to generate versions, and you also need to change your views to use those
|
14
|
+
versions:
|
15
|
+
|
16
|
+
```rb
|
17
|
+
class ImageUploader < Shrine
|
18
|
+
plugin :versions, names: [:original, :thumb]
|
19
|
+
|
20
|
+
def process(io, context)
|
21
|
+
case context[:phase]
|
22
|
+
when :store
|
23
|
+
thumb = process_thumb(io.download) # replace with actual processing method
|
24
|
+
{original: io, thumb: thumb}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
```
|
29
|
+
```rb
|
30
|
+
# In your views add the version name to all <attachment>_url calls.
|
31
|
+
user.avatar_url(:thumb)
|
32
|
+
```
|
33
|
+
|
34
|
+
Note that you should deploy both of these changes at once, because the
|
35
|
+
`<attachment>_url` method will fail if there are versions generated but no
|
36
|
+
version name was passed in. If a version name was passed in but versions aren't
|
37
|
+
generated yet (which will be the case here), it will just return the
|
38
|
+
unprocessed file URL.
|
39
|
+
|
40
|
+
Afterwards you should run a script which reprocesses the versions for existing
|
41
|
+
files:
|
42
|
+
|
43
|
+
```rb
|
44
|
+
Shrine.plugin :migration_helpers # before the model is loaded
|
45
|
+
```
|
46
|
+
```rb
|
47
|
+
User.paged_each do |user|
|
48
|
+
user.update_avatar do |avatar|
|
49
|
+
unless avatar.is_a?(Hash)
|
50
|
+
file = some_processing(avatar.download)
|
51
|
+
thumb = user.avatar_store.upload(file, {record: user, name: :avatar, version: :thumb})
|
52
|
+
{original: avatar, thumb: thumb}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
## Reprocessing a single version
|
10
59
|
|
11
60
|
The simplest scenario is where you need to regenerate an existing version.
|
12
61
|
First you need to change and deploy your updated processing code, and
|
13
62
|
afterwards you can run a script like this on your production database:
|
14
63
|
|
15
64
|
```rb
|
16
|
-
Shrine.plugin :migration_helpers
|
65
|
+
Shrine.plugin :migration_helpers # before the model is loaded
|
66
|
+
```
|
17
67
|
|
68
|
+
```rb
|
18
69
|
User.paged_each do |user|
|
19
70
|
user.update_avatar do |avatar|
|
20
71
|
thumb = some_processing(avatar[:original].download)
|
@@ -47,15 +98,17 @@ After you've deployed this change, you should run a script that will generate
|
|
47
98
|
the new version for all existing records:
|
48
99
|
|
49
100
|
```rb
|
50
|
-
Shrine.plugin :migration_helpers
|
101
|
+
Shrine.plugin :migration_helpers # before the model is loaded
|
102
|
+
```
|
51
103
|
|
104
|
+
```rb
|
52
105
|
User.paged_each do |user|
|
53
106
|
user.update_avatar do |avatar|
|
54
|
-
unless
|
107
|
+
unless avatar[:new]
|
55
108
|
file = some_processing(avatar[:original].download, *args)
|
56
|
-
new = user.avatar_store.upload(file)
|
109
|
+
new = user.avatar_store.upload(file, {record: user, name: :avatar, version: :new})
|
110
|
+
avatar.merge(new: new)
|
57
111
|
end
|
58
|
-
avatar.merge(new: new)
|
59
112
|
end
|
60
113
|
end
|
61
114
|
```
|
@@ -72,29 +125,40 @@ not to use the new version, and deploy that code. After you've done that, you
|
|
72
125
|
can run a script which removes that version:
|
73
126
|
|
74
127
|
```rb
|
75
|
-
Shrine.plugin :migration_helpers
|
128
|
+
Shrine.plugin :migration_helpers # before the model is loaded
|
129
|
+
```
|
130
|
+
|
131
|
+
```rb
|
132
|
+
removed_versions = []
|
76
133
|
|
77
134
|
User.paged_each do |user|
|
78
135
|
user.update_avatar do |avatar|
|
79
136
|
old_version = avatar.delete(:old_version)
|
80
|
-
old_version
|
137
|
+
removed_versions << old_version if old_version
|
81
138
|
avatar
|
82
139
|
end
|
83
140
|
end
|
141
|
+
|
142
|
+
if removed_versions.any?
|
143
|
+
uploader = removed_versions.first.uploader
|
144
|
+
uploader.delete(removed_versions)
|
145
|
+
end
|
84
146
|
```
|
85
147
|
|
86
148
|
After the script has finished, you should be able to safely remove the version
|
87
149
|
name from the list.
|
88
150
|
|
89
|
-
##
|
151
|
+
## Reprocessing all versions
|
90
152
|
|
91
153
|
If you made a lot of changes to versions, it might make sense to simply
|
92
154
|
regenerate all versions. After you've deployed the change in processing, you
|
93
155
|
can run a script which updates existing records:
|
94
156
|
|
95
157
|
```rb
|
96
|
-
Shrine.plugin :migration_helpers
|
158
|
+
Shrine.plugin :migration_helpers # before the model is loaded
|
159
|
+
```
|
97
160
|
|
161
|
+
```rb
|
98
162
|
User.paged_each do |user|
|
99
163
|
if user.avatar && user.avatar_store.uploaded?(user.avatar)
|
100
164
|
user.update(avatar: user.avatar[:original])
|