shrine 3.0.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -0
- data/LICENSE.txt +1 -1
- data/README.md +15 -5
- data/doc/advantages.md +33 -16
- data/doc/attacher.md +2 -2
- data/doc/carrierwave.md +78 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/direct_s3.md +1 -0
- data/doc/external/articles.md +57 -45
- data/doc/external/extensions.md +41 -35
- data/doc/external/misc.md +23 -8
- data/doc/getting_started.md +177 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +6 -4
- data/doc/paperclip.md +119 -42
- data/doc/plugins/activerecord.md +1 -1
- data/doc/plugins/add_metadata.md +112 -35
- data/doc/plugins/atomic_helpers.md +41 -3
- data/doc/plugins/backgrounding.md +12 -2
- data/doc/plugins/column.md +36 -7
- data/doc/plugins/data_uri.md +2 -2
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivation_endpoint.md +26 -28
- data/doc/plugins/derivatives.md +238 -171
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +11 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +22 -10
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/persistence.md +11 -1
- data/doc/plugins/refresh_metadata.md +5 -4
- data/doc/plugins/remote_url.md +8 -3
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +12 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +7 -11
- data/doc/plugins/upload_options.md +1 -1
- data/doc/plugins/url_options.md +4 -4
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +7 -7
- data/doc/processing.md +290 -127
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- data/doc/release_notes/2.8.0.md +1 -1
- data/doc/release_notes/3.0.0.md +1 -1
- data/doc/release_notes/3.0.1.md +4 -0
- data/doc/release_notes/3.1.0.md +73 -0
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/securing_uploads.md +3 -3
- data/doc/storage/file_system.md +1 -1
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +105 -82
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +97 -49
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +24 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/activerecord.rb +1 -1
- data/lib/shrine/plugins/add_metadata.rb +18 -7
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- data/lib/shrine/plugins/default_storage.rb +6 -6
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +12 -7
- data/lib/shrine/plugins/derivatives.rb +61 -29
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- data/lib/shrine/plugins/mirroring.rb +8 -8
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +16 -4
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/refresh_metadata.rb +2 -2
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/remove_attachment.rb +5 -0
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/sequel.rb +1 -1
- data/lib/shrine/plugins/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +22 -11
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +10 -5
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/url_options.rb +2 -2
- data/lib/shrine/plugins/validation.rb +9 -7
- data/lib/shrine/storage/linter.rb +4 -4
- data/lib/shrine/storage/memory.rb +5 -3
- data/lib/shrine/storage/s3.rb +117 -38
- data/lib/shrine/uploaded_file.rb +0 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +7 -8
- metadata +25 -31
data/doc/metadata.md
CHANGED
@@ -15,6 +15,18 @@ uploaded_file.metadata #=>
|
|
15
15
|
# }
|
16
16
|
```
|
17
17
|
|
18
|
+
Under the hood, `Shrine#upload` calls `Shrine#extract_metadata`, which you can
|
19
|
+
also use directly to extract metadata from any IO object:
|
20
|
+
|
21
|
+
```rb
|
22
|
+
uploader.extract_metadata(io) #=>
|
23
|
+
# {
|
24
|
+
# "size" => 345993,
|
25
|
+
# "filename" => "matrix.mp4",
|
26
|
+
# "mime_type" => "video/mp4",
|
27
|
+
# }
|
28
|
+
```
|
29
|
+
|
18
30
|
The following metadata is extracted by default:
|
19
31
|
|
20
32
|
| Key | Default source |
|
@@ -23,7 +35,9 @@ The following metadata is extracted by default:
|
|
23
35
|
| `mime_type` | extracted from `io.content_type` |
|
24
36
|
| `size` | extracted from `io.size` |
|
25
37
|
|
26
|
-
|
38
|
+
## Accessing metadata
|
39
|
+
|
40
|
+
You can access the stored metadata in three ways:
|
27
41
|
|
28
42
|
```rb
|
29
43
|
# via methods (if they're defined)
|
@@ -42,17 +56,7 @@ uploaded_file["filename"]
|
|
42
56
|
uploaded_file["mime_type"]
|
43
57
|
```
|
44
58
|
|
45
|
-
|
46
|
-
also use directly to extract metadata from any IO object:
|
47
|
-
|
48
|
-
```rb
|
49
|
-
uploader.extract_metadata(io) #=>
|
50
|
-
# {
|
51
|
-
# "size" => 345993,
|
52
|
-
# "filename" => "matrix.mp4",
|
53
|
-
# "mime_type" => "video/mp4",
|
54
|
-
# }
|
55
|
-
```
|
59
|
+
## Controlling extraction
|
56
60
|
|
57
61
|
`Shrine#upload` accepts a `:metadata` option which accepts the following values:
|
58
62
|
|
@@ -85,11 +89,11 @@ By default, the `mime_type` metadata will be copied over from the
|
|
85
89
|
`#content_type` attribute of the input file (if present). However, since
|
86
90
|
`#content_type` value comes from the `Content-Type` header of the upload
|
87
91
|
request, it's *not guaranteed* to hold the actual MIME type of the file (browser
|
88
|
-
determines this header based on file extension).
|
89
|
-
|
90
|
-
`
|
91
|
-
|
92
|
-
|
92
|
+
determines this header based on file extension).
|
93
|
+
|
94
|
+
Moreover, only `ActionDispatch::Http::UploadedFile`, `Shrine::RackFile`, and
|
95
|
+
`Shrine::DataFile` objects have `#content_type` defined, so when uploading
|
96
|
+
objects such as `File`, the `mime_type` value will be nil by default.
|
93
97
|
|
94
98
|
To remedy that, Shrine comes with a
|
95
99
|
[`determine_mime_type`][determine_mime_type] plugin which is able to extract
|
@@ -112,15 +116,14 @@ You can choose different analyzers, and even mix-and-match them. See the
|
|
112
116
|
|
113
117
|
## Image Dimensions
|
114
118
|
|
115
|
-
Shrine comes with a `store_dimensions` plugin for
|
116
|
-
It adds `width` and `height` metadata values, and
|
117
|
-
`#height`, and `#dimensions` methods to the
|
118
|
-
|
119
|
-
have it use [MiniMagick] or [ruby-vips]:
|
119
|
+
Shrine comes with a [`store_dimensions`][store_dimensions] plugin for
|
120
|
+
extracting image dimensions. It adds `width` and `height` metadata values, and
|
121
|
+
also adds `#width`, `#height`, and `#dimensions` methods to the
|
122
|
+
`Shrine::UploadedFile` object.
|
120
123
|
|
121
124
|
```rb
|
122
125
|
# Gemfile
|
123
|
-
gem "fastimage"
|
126
|
+
gem "fastimage" # default analyzer
|
124
127
|
```
|
125
128
|
```rb
|
126
129
|
Shrine.plugin :store_dimensions
|
@@ -136,12 +139,17 @@ uploaded_file.height #=> 900
|
|
136
139
|
uploaded_file.dimensions #=> [1600, 900]
|
137
140
|
```
|
138
141
|
|
142
|
+
By default, the plugin uses [FastImage] to analyze dimensions, but you can also
|
143
|
+
have it use [MiniMagick] or [ruby-vips]. See the
|
144
|
+
[`store_dimensions`][store_dimensions] plugin docs for more details.
|
145
|
+
|
139
146
|
## Custom metadata
|
140
147
|
|
141
148
|
In addition to the built-in metadata, Shrine allows you to extract and store
|
142
|
-
any custom metadata, using the `add_metadata` plugin (which
|
143
|
-
`Shrine#extract_metadata`).
|
144
|
-
|
149
|
+
any custom metadata, using the [`add_metadata`][add_metadata] plugin (which
|
150
|
+
internally extends `Shrine#extract_metadata`).
|
151
|
+
|
152
|
+
For example, you might want to extract EXIF data from images:
|
145
153
|
|
146
154
|
```rb
|
147
155
|
# Gemfile
|
@@ -203,9 +211,20 @@ uploaded_file.metadata #=>
|
|
203
211
|
|
204
212
|
The yielded `io` object will not always be an object that responds to `#path`.
|
205
213
|
For example, with the `data_uri` plugin the `io` can be a `StringIO` wrapper,
|
206
|
-
with `restore_cached_data` or `refresh_metadata` plugins the `io` might
|
207
|
-
`Shrine::UploadedFile` object. So we're using `Shrine.with_file` to
|
208
|
-
have a file object.
|
214
|
+
while with `restore_cached_data` or `refresh_metadata` plugins the `io` might
|
215
|
+
be a `Shrine::UploadedFile` object. So, we're using `Shrine.with_file` to
|
216
|
+
ensure we have a file object.
|
217
|
+
|
218
|
+
### Adding metadata
|
219
|
+
|
220
|
+
If you wish to add metadata to an already attached file, you can do it as
|
221
|
+
follows:
|
222
|
+
|
223
|
+
```rb
|
224
|
+
photo.image_attacher.add_metadata("foo" => "bar")
|
225
|
+
photo.image.metadata #=> { ..., "foo" => "bar" }
|
226
|
+
photo.save # persist changes
|
227
|
+
```
|
209
228
|
|
210
229
|
## Metadata columns
|
211
230
|
|
@@ -225,15 +244,20 @@ photo.image_type #=> "image/jpeg"
|
|
225
244
|
When attaching files that were uploaded directly to the cloud or a [tus
|
226
245
|
server], Shrine won't automatically extract metadata from them, instead it will
|
227
246
|
copy any existing metadata that was set on the client side. The reason why this
|
228
|
-
is the default behaviour is because
|
229
|
-
|
230
|
-
|
231
|
-
|
247
|
+
is the default behaviour is because metadata extraction requires (at least
|
248
|
+
partially) retrieving file content from the storage, which could potentially be
|
249
|
+
expensive depending on the storage and the type of metadata being extracted.
|
250
|
+
|
251
|
+
```rb
|
252
|
+
# no additional metadata will be extracted in this assignment by default
|
253
|
+
photo.image = '{"id":"9e6581a4ea1.jpg","storage":"cache","metadata":{...}}'
|
254
|
+
```
|
232
255
|
|
233
|
-
|
234
|
-
|
235
|
-
you want to
|
236
|
-
|
256
|
+
### Extracting on attachment
|
257
|
+
|
258
|
+
If you want metadata to be automatically extracted on assignment (which is
|
259
|
+
useful if you want to validate the extracted metadata or have it immediately
|
260
|
+
available for any other reason), you can load the `restore_cached_data` plugin:
|
237
261
|
|
238
262
|
```rb
|
239
263
|
Shrine.plugin :restore_cached_data # automatically extract metadata from cached files on assignment
|
@@ -248,7 +272,9 @@ photo.image.metadata #=>
|
|
248
272
|
# }
|
249
273
|
```
|
250
274
|
|
251
|
-
###
|
275
|
+
### Extracting in the background
|
276
|
+
|
277
|
+
#### A) Extracting with promotion
|
252
278
|
|
253
279
|
If you're using [backgrounding], you can extract metadata during background
|
254
280
|
promotion using the `refresh_metadata` plugin (which the `restore_cached_data`
|
@@ -271,12 +297,14 @@ class PromoteJob
|
|
271
297
|
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
272
298
|
|
273
299
|
attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
|
274
|
-
attacher.refresh_metadata!
|
300
|
+
attacher.refresh_metadata! # extract metadata
|
275
301
|
attacher.atomic_promote
|
276
302
|
end
|
277
303
|
end
|
278
304
|
```
|
279
305
|
|
306
|
+
#### B) Extracting separately from promotion
|
307
|
+
|
280
308
|
You can also extract metadata in the background separately from promotion:
|
281
309
|
|
282
310
|
```rb
|
@@ -303,11 +331,13 @@ class MetadataJob
|
|
303
331
|
end
|
304
332
|
```
|
305
333
|
|
334
|
+
### Combining foreground and background
|
335
|
+
|
306
336
|
If you have some metadata that you want to extract in the foreground and some
|
307
337
|
that you want to extract in the background, you can use the uploader context:
|
308
338
|
|
309
339
|
```rb
|
310
|
-
class
|
340
|
+
class VideoUploader < Shrine
|
311
341
|
plugin :add_metadata
|
312
342
|
|
313
343
|
add_metadata do |io, **options|
|
@@ -324,7 +354,7 @@ class MyUploader < Shrine
|
|
324
354
|
end
|
325
355
|
```
|
326
356
|
```rb
|
327
|
-
class
|
357
|
+
class PromoteJob
|
328
358
|
include Sidekiq::Worker
|
329
359
|
|
330
360
|
def perform(attacher_class, record_class, record_id, name, file_data)
|
@@ -332,12 +362,16 @@ class MetadataJob
|
|
332
362
|
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
333
363
|
|
334
364
|
attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
|
335
|
-
attacher.refresh_metadata!(background: true)
|
336
|
-
attacher.
|
365
|
+
attacher.refresh_metadata!(background: true) # specify the flag
|
366
|
+
attacher.atomic_promote
|
337
367
|
end
|
338
368
|
end
|
339
369
|
```
|
340
370
|
|
371
|
+
Now triggering metadata extraction in the controller on attachment (using
|
372
|
+
`restore_cached_data` or `refresh_metadata` plugin) will skip the video
|
373
|
+
metadata block, which will be triggered later in the background job.
|
374
|
+
|
341
375
|
### Optimizations
|
342
376
|
|
343
377
|
If you want to do both metadata extraction and file processing during
|
@@ -389,4 +423,6 @@ end
|
|
389
423
|
[ruby-vips]: https://github.com/libvips/ruby-vips
|
390
424
|
[tus server]: https://github.com/janko/tus-ruby-server
|
391
425
|
[determine_mime_type]: https://shrinerb.com/docs/plugins/determine_mime_type
|
426
|
+
[store_dimensions]: https://shrinerb.com/docs/plugins/store_dimensions
|
427
|
+
[add_metadata]: https://shrinerb.com/docs/plugins/add_metadata
|
392
428
|
[backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
|
data/doc/multiple_files.md
CHANGED
@@ -14,7 +14,7 @@ relationship with the main table, and files will be attached on the records in
|
|
14
14
|
the new table. That way each record from the main table can implicitly have
|
15
15
|
multiple attachments through the associated records.
|
16
16
|
|
17
|
-
```
|
17
|
+
```
|
18
18
|
album1
|
19
19
|
photo1
|
20
20
|
- attachment1
|
@@ -74,13 +74,15 @@ Sequel.migration do
|
|
74
74
|
change do
|
75
75
|
create_table :albums do
|
76
76
|
primary_key :id
|
77
|
-
|
77
|
+
|
78
|
+
String :title
|
78
79
|
end
|
79
80
|
|
80
81
|
create_table :photos do
|
81
82
|
primary_key :id
|
82
83
|
foreign_key :album_id, :albums
|
83
|
-
|
84
|
+
|
85
|
+
String :image_data
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
@@ -226,7 +228,7 @@ photos_attributes = album_params[:photos_attributes].to_h.merge(new_photos_attri
|
|
226
228
|
album_attributes = album_params.merge(photos_attributes: photos_attributes)
|
227
229
|
|
228
230
|
# create the album with photos
|
229
|
-
Album.create(
|
231
|
+
Album.create(album_attributes)
|
230
232
|
```
|
231
233
|
|
232
234
|
In this case you need to make sure your form tag has
|
data/doc/paperclip.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
---
|
2
|
-
title:
|
2
|
+
title: Upgrading from Paperclip
|
3
3
|
---
|
4
4
|
|
5
5
|
This guide is aimed at helping Paperclip users transition to Shrine, and it
|
6
6
|
consists of three parts:
|
7
7
|
|
8
8
|
1. Explanation of the key differences in design between Paperclip and Shrine
|
9
|
-
2. Instructions how to migrate
|
9
|
+
2. Instructions how to migrate an existing app that uses Paperclip to Shrine
|
10
10
|
3. Extensive reference of Paperclip's interface with Shrine equivalents
|
11
11
|
|
12
12
|
## Overview
|
@@ -136,7 +136,7 @@ into separate columns.
|
|
136
136
|
While Paperclip works only with Active Record, Shrine is designed to integrate
|
137
137
|
with any persistence library (there are integrations for [Active
|
138
138
|
Record][activerecord], [Sequel][sequel], [ROM][rom], [Hanami][hanami] and
|
139
|
-
[
|
139
|
+
[Mongoid][mongoid]), and can also be used standalone:
|
140
140
|
|
141
141
|
```rb
|
142
142
|
attacher = ImageUploader::Attacher.new
|
@@ -182,7 +182,7 @@ require "image_processing/mini_magick"
|
|
182
182
|
class ImageUploader < Shrine
|
183
183
|
plugin :derivatives
|
184
184
|
|
185
|
-
Attacher.
|
185
|
+
Attacher.derivatives do |original|
|
186
186
|
magick = ImageProcessing::MiniMagick.source(original)
|
187
187
|
|
188
188
|
{
|
@@ -297,16 +297,21 @@ file.mime_type #=> "application/x-php"
|
|
297
297
|
## Migrating from Paperclip
|
298
298
|
|
299
299
|
You have an existing app using Paperclip and you want to transfer it to Shrine.
|
300
|
-
|
301
|
-
|
300
|
+
Let's assume we have a `Photo` model with the "image" attachment.
|
301
|
+
|
302
|
+
### 1. Add Shrine column
|
303
|
+
|
304
|
+
First we need to create the `image_data` column for Shrine:
|
302
305
|
|
303
306
|
```rb
|
304
307
|
add_column :photos, :image_data, :text
|
305
308
|
```
|
306
309
|
|
307
|
-
|
308
|
-
|
309
|
-
attachments
|
310
|
+
### 2. Dual write
|
311
|
+
|
312
|
+
Next, we need to make new Paperclip attachments write to the `image_data`
|
313
|
+
column. This can be done by including the below module to all models that have
|
314
|
+
Paperclip attachments:
|
310
315
|
|
311
316
|
```rb
|
312
317
|
require "shrine"
|
@@ -318,8 +323,7 @@ Shrine.storages = {
|
|
318
323
|
|
319
324
|
Shrine.plugin :model
|
320
325
|
Shrine.plugin :derivatives
|
321
|
-
|
322
|
-
```rb
|
326
|
+
|
323
327
|
module PaperclipShrineSynchronization
|
324
328
|
def self.included(model)
|
325
329
|
model.before_save do
|
@@ -336,8 +340,8 @@ module PaperclipShrineSynchronization
|
|
336
340
|
if attachment.size.present?
|
337
341
|
attacher.set shrine_file(attachment)
|
338
342
|
|
339
|
-
attachment.styles.each do |
|
340
|
-
attacher.merge_derivatives(
|
343
|
+
attachment.styles.each do |style_name, style|
|
344
|
+
attacher.merge_derivatives(style_name => shrine_file(style))
|
341
345
|
end
|
342
346
|
else
|
343
347
|
attacher.set nil
|
@@ -354,13 +358,14 @@ module PaperclipShrineSynchronization
|
|
354
358
|
end
|
355
359
|
end
|
356
360
|
|
357
|
-
# If you'll be using a `:prefix` on your Shrine storage, or you're storing
|
358
|
-
# files on the filesystem, make sure to subtract the appropriate part
|
359
|
-
# from the path assigned to `:id`.
|
360
361
|
def shrine_attachment_file(attachment)
|
362
|
+
location = attachment.path
|
363
|
+
# if you're storing files on disk, make sure to subtract the absolute path
|
364
|
+
location = location.sub(%r{^#{storage.prefix}/}, "") if storage.prefix
|
365
|
+
|
361
366
|
Shrine.uploaded_file(
|
362
367
|
storage: :store,
|
363
|
-
id:
|
368
|
+
id: location,
|
364
369
|
metadata: {
|
365
370
|
"size" => attachment.size,
|
366
371
|
"filename" => attachment.original_filename,
|
@@ -372,13 +377,21 @@ module PaperclipShrineSynchronization
|
|
372
377
|
# If you'll be using a `:prefix` on your Shrine storage, or you're storing
|
373
378
|
# files on the filesystem, make sure to subtract the appropriate part
|
374
379
|
# from the path assigned to `:id`.
|
375
|
-
def
|
380
|
+
def shrine_style_file(style)
|
381
|
+
location = style.attachment.path(style.name)
|
382
|
+
# if you're storing files on disk, make sure to subtract the absolute path
|
383
|
+
location = location.sub(%r{^#{storage.prefix}/}, "") if storage.prefix
|
384
|
+
|
376
385
|
Shrine.uploaded_file(
|
377
386
|
storage: :store,
|
378
|
-
id:
|
387
|
+
id: location,
|
379
388
|
metadata: {},
|
380
389
|
)
|
381
390
|
end
|
391
|
+
|
392
|
+
def storage
|
393
|
+
Shrine.storages[:store]
|
394
|
+
end
|
382
395
|
end
|
383
396
|
```
|
384
397
|
```rb
|
@@ -389,34 +402,35 @@ end
|
|
389
402
|
```
|
390
403
|
|
391
404
|
After you deploy this code, the `image_data` column should now be successfully
|
392
|
-
synchronized with new attachments.
|
393
|
-
|
405
|
+
synchronized with new attachments.
|
406
|
+
|
407
|
+
### 3. Data migration
|
408
|
+
|
409
|
+
Next step is to run a script which writes all existing Paperclip attachments to
|
410
|
+
`image_data`:
|
394
411
|
|
395
412
|
```rb
|
396
413
|
Photo.find_each do |photo|
|
397
|
-
|
398
|
-
photo.write_shrine_data(name) if klass == Photo
|
399
|
-
end
|
414
|
+
photo.write_shrine_data(:image)
|
400
415
|
photo.save!
|
401
416
|
end
|
402
417
|
```
|
403
418
|
|
419
|
+
### 4. Rewrite code
|
420
|
+
|
404
421
|
Now you should be able to rewrite your application so that it uses Shrine
|
405
|
-
instead of Paperclip
|
406
|
-
|
407
|
-
below.
|
422
|
+
instead of Paperclip (you can consult the reference in the next section). You
|
423
|
+
can remove the `PaperclipShrineSynchronization` module as well.
|
408
424
|
|
409
|
-
|
410
|
-
(specifically versions). You can run a script that will fill in any missing
|
411
|
-
metadata defined in your Shrine uploader:
|
425
|
+
### 5. Remove Paperclip columns
|
412
426
|
|
413
|
-
|
414
|
-
Shrine.plugin :refresh_metadata
|
427
|
+
If everything is looking good, we can remove Paperclip columns:
|
415
428
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
429
|
+
```rb
|
430
|
+
remove_column :photos, :image_file_name
|
431
|
+
remove_column :photos, :image_file_size
|
432
|
+
remove_column :photos, :image_content_type
|
433
|
+
remove_column :photos, :image_updated_at
|
420
434
|
```
|
421
435
|
|
422
436
|
## Paperclip to Shrine direct mapping
|
@@ -457,8 +471,8 @@ Processing is defined by using the `derivatives` plugin:
|
|
457
471
|
class ImageUploader < Shrine
|
458
472
|
plugin :derivatives
|
459
473
|
|
460
|
-
Attacher.
|
461
|
-
magick = ImageProcessing::MiniMagick.source(
|
474
|
+
Attacher.derivatives do |original|
|
475
|
+
magick = ImageProcessing::MiniMagick.source(original)
|
462
476
|
|
463
477
|
{
|
464
478
|
large: magick.resize_to_limit!(800, 800),
|
@@ -477,8 +491,8 @@ For default URLs you can use the `default_url` plugin:
|
|
477
491
|
class ImageUploader < Shrine
|
478
492
|
plugin :default_url
|
479
493
|
|
480
|
-
Attacher.default_url do |
|
481
|
-
"/
|
494
|
+
Attacher.default_url do |derivative: nil, **|
|
495
|
+
"/images/placeholders/#{derivative || "original"}.jpg"
|
482
496
|
end
|
483
497
|
end
|
484
498
|
```
|
@@ -506,16 +520,79 @@ Alternatively, if you want to generate locations yourself you can override the
|
|
506
520
|
|
507
521
|
```rb
|
508
522
|
class ImageUploader < Shrine
|
509
|
-
def generate_location(io, **
|
510
|
-
|
523
|
+
def generate_location(io, record: nil, name: nil, **)
|
524
|
+
[ storage_key,
|
525
|
+
record && record.class.name.underscore,
|
526
|
+
record && record.id,
|
527
|
+
super,
|
528
|
+
io.original_filename ].compact.join("/")
|
511
529
|
end
|
512
530
|
end
|
513
531
|
```
|
532
|
+
```
|
533
|
+
cache/user/123/2feff8c724e7ce17/nature.jpg
|
534
|
+
store/user/456/7f99669fde1e01fc/kitten.jpg
|
535
|
+
...
|
536
|
+
```
|
514
537
|
|
515
538
|
#### `:validate_media_type`
|
516
539
|
|
517
540
|
Shrine has this functionality in the `determine_mime_type` plugin.
|
518
541
|
|
542
|
+
### `validates_attachment`
|
543
|
+
|
544
|
+
#### `:presence`
|
545
|
+
|
546
|
+
For presence validation you can use your ORM's presence validator:
|
547
|
+
|
548
|
+
```rb
|
549
|
+
class Photo < ActiveRecord::Base
|
550
|
+
include ImageUploader::Attachment(:image)
|
551
|
+
validates_presence_of :image
|
552
|
+
end
|
553
|
+
```
|
554
|
+
|
555
|
+
#### `:content_type`
|
556
|
+
|
557
|
+
You can do MIME type validation with Shrine's `validation_helpers` plugin:
|
558
|
+
|
559
|
+
```rb
|
560
|
+
class ImageUploader < Shrine
|
561
|
+
plugin :validation_helpers
|
562
|
+
|
563
|
+
Attacher.validate do
|
564
|
+
validate_mime_type %w[image/jpeg image/png image/webp]
|
565
|
+
end
|
566
|
+
end
|
567
|
+
```
|
568
|
+
|
569
|
+
Make sure to also load the `determine_mime_type` plugin to detect MIME type
|
570
|
+
from file content.
|
571
|
+
|
572
|
+
```rb
|
573
|
+
# Gemfile
|
574
|
+
gem "mimemagic"
|
575
|
+
```
|
576
|
+
```rb
|
577
|
+
Shrine.plugin :determine_mime_type, analyzer: -> (io, analyzers) do
|
578
|
+
analyzers[:mimemagic].call(io) || analyzers[:file].call(io)
|
579
|
+
end
|
580
|
+
```
|
581
|
+
|
582
|
+
#### `:size`
|
583
|
+
|
584
|
+
You can do filesize validation with Shrine's `validation_helpers` plugin:
|
585
|
+
|
586
|
+
```rb
|
587
|
+
class ImageUploader < Shrine
|
588
|
+
plugin :validation_helpers
|
589
|
+
|
590
|
+
Attacher.validate do
|
591
|
+
validate_max_size 10*1024*1024
|
592
|
+
end
|
593
|
+
end
|
594
|
+
```
|
595
|
+
|
519
596
|
### `Paperclip::Attachment`
|
520
597
|
|
521
598
|
This section explains the equivalent of Paperclip attachment's methods, in
|