shrine 2.2.0 → 2.3.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 +143 -84
- data/doc/carrierwave.md +187 -47
- data/doc/direct_s3.md +57 -39
- data/doc/paperclip.md +183 -91
- data/doc/refile.md +148 -124
- data/doc/regenerating_versions.md +2 -3
- data/lib/shrine.rb +26 -28
- data/lib/shrine/plugins/activerecord.rb +22 -31
- data/lib/shrine/plugins/add_metadata.rb +1 -1
- data/lib/shrine/plugins/backgrounding.rb +19 -7
- data/lib/shrine/plugins/backup.rb +2 -2
- data/lib/shrine/plugins/cached_attachment_data.rb +1 -1
- data/lib/shrine/plugins/copy.rb +52 -0
- data/lib/shrine/plugins/data_uri.rb +1 -1
- data/lib/shrine/plugins/default_storage.rb +2 -2
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/default_url_options.rb +1 -1
- data/lib/shrine/plugins/delete_promoted.rb +1 -1
- data/lib/shrine/plugins/delete_raw.rb +1 -1
- data/lib/shrine/plugins/determine_mime_type.rb +3 -2
- data/lib/shrine/plugins/direct_upload.rb +36 -24
- data/lib/shrine/plugins/download_endpoint.rb +3 -3
- data/lib/shrine/plugins/dynamic_storage.rb +2 -2
- data/lib/shrine/plugins/hooks.rb +1 -1
- data/lib/shrine/plugins/included.rb +3 -4
- data/lib/shrine/plugins/keep_files.rb +1 -1
- data/lib/shrine/plugins/logging.rb +1 -1
- data/lib/shrine/plugins/module_include.rb +1 -1
- data/lib/shrine/plugins/moving.rb +10 -5
- data/lib/shrine/plugins/multi_delete.rb +2 -2
- data/lib/shrine/plugins/parallelize.rb +2 -2
- data/lib/shrine/plugins/parsed_json.rb +1 -1
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +11 -9
- data/lib/shrine/plugins/rack_file.rb +1 -1
- data/lib/shrine/plugins/recache.rb +14 -4
- data/lib/shrine/plugins/remote_url.rb +1 -1
- data/lib/shrine/plugins/remove_attachment.rb +3 -4
- data/lib/shrine/plugins/remove_invalid.rb +1 -1
- data/lib/shrine/plugins/restore_cached_data.rb +11 -4
- data/lib/shrine/plugins/sequel.rb +34 -45
- data/lib/shrine/plugins/store_dimensions.rb +1 -1
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/validation_helpers.rb +7 -8
- data/lib/shrine/plugins/versions.rb +31 -30
- data/lib/shrine/storage/file_system.rb +16 -12
- data/lib/shrine/storage/s3.rb +36 -2
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +9 -8
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed4ba1e9f5dafe48af21c7f80039ca3d0014e600
|
4
|
+
data.tar.gz: 2f8f7903b3032d8ef43508a0e7259ab5deaedb25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d9200a6bfc5fe5705272a4d01a2e62a7146e869c53ede22534ec9043fa87e7c9566b58b5a7cf10f9667de47a9e91d468e34dae146b8cc8526f94cdcbe0f4404
|
7
|
+
data.tar.gz: 3c9673213a120a2d8427a9bee3280bccb97819154f1bd5bc7bc548f3a70274891133c4c86151b52bd8f94bdc158a4f8be84adf364edd2fd3488f7df98b75d5ef
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Shrine
|
2
2
|
|
3
|
-
Shrine is a toolkit for
|
3
|
+
Shrine is a toolkit for file attachments in Ruby applications.
|
4
4
|
|
5
5
|
If you're new, you're encouraged to read the [introductory blog post] which
|
6
6
|
explains the motivation behind Shrine.
|
@@ -59,7 +59,8 @@ class Photo < Sequel::Model # ActiveRecord::Base
|
|
59
59
|
end
|
60
60
|
```
|
61
61
|
|
62
|
-
|
62
|
+
This creates an `image` attachment attribute which accepts files. Let's now
|
63
|
+
add the form fields needed for attaching files:
|
63
64
|
|
64
65
|
```erb
|
65
66
|
<form action="/photos" method="post" enctype="multipart/form-data">
|
@@ -68,15 +69,23 @@ And add attachment fields to the Photo form:
|
|
68
69
|
</form>
|
69
70
|
|
70
71
|
<!-- Rails: -->
|
71
|
-
|
72
72
|
<%= form_for @photo do |f| %>
|
73
73
|
<%= f.hidden_field :image, value: @photo.cached_image_data %>
|
74
74
|
<%= f.file_field :image %>
|
75
75
|
<% end %>
|
76
76
|
```
|
77
77
|
|
78
|
-
Now
|
79
|
-
the image:
|
78
|
+
Now assigning the request parameters in your router/controller will
|
79
|
+
automatically handle the image attachment:
|
80
|
+
|
81
|
+
```rb
|
82
|
+
post "/photos" do
|
83
|
+
Photo.create(params[:photo])
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
87
|
+
When a Photo is created with the image attached, you can display the image via
|
88
|
+
its URL:
|
80
89
|
|
81
90
|
```erb
|
82
91
|
<img src="<%= @photo.image_url %>">
|
@@ -86,8 +95,8 @@ the image:
|
|
86
95
|
|
87
96
|
When we assign an IO-like object to the record, Shrine will upload it to the
|
88
97
|
registered `:cache` storage, which acts as a temporary storage, and write the
|
89
|
-
location
|
90
|
-
column:
|
98
|
+
location, storage, and metadata of the uploaded file to a single
|
99
|
+
`<attachment>_data` column:
|
91
100
|
|
92
101
|
```rb
|
93
102
|
photo = Photo.new
|
@@ -105,7 +114,8 @@ The Shrine attachment module added the following methods to the `Photo` model:
|
|
105
114
|
* `#image_url` – calls `image.url` if attachment is present, otherwise returns nil
|
106
115
|
* `#image_attacher` - instance of `Shrine::Attacher` which handles attaching
|
107
116
|
|
108
|
-
In addition to assigning new files, you can also assign already
|
117
|
+
In addition to assigning new files, you can also assign already cached files
|
118
|
+
using their JSON representation:
|
109
119
|
|
110
120
|
```rb
|
111
121
|
photo.image = '{"storage":"cache","id":"9260ea09d8effd.jpg","metadata":{...}}'
|
@@ -114,9 +124,9 @@ photo.image = '{"storage":"cache","id":"9260ea09d8effd.jpg","metadata":{...}}'
|
|
114
124
|
This allows Shrine to retain uploaded files in case of validation errors, and
|
115
125
|
handle [direct uploads], via the hidden form field.
|
116
126
|
|
117
|
-
The ORM plugin that we loaded
|
118
|
-
|
119
|
-
is destroyed:
|
127
|
+
The ORM plugin that we loaded adds appropriate callbacks, so when record is
|
128
|
+
saved the attachment is uploaded to permanent storge (`:store`), and when
|
129
|
+
record is destroyed the attachment is destroyed as well:
|
120
130
|
|
121
131
|
```rb
|
122
132
|
photo.image = File.open("waterfall.jpg")
|
@@ -129,7 +139,15 @@ photo.destroy
|
|
129
139
|
photo.image.exists? #=> false
|
130
140
|
```
|
131
141
|
|
132
|
-
|
142
|
+
The ORM plugin will also delete replaced attachments:
|
143
|
+
|
144
|
+
```rb
|
145
|
+
photo.update(image: new_file) # changes the attachment and deletes previous
|
146
|
+
# or
|
147
|
+
photo.update(image: nil) # removes the attachment and deletes previous
|
148
|
+
```
|
149
|
+
|
150
|
+
In all these examples we used `image` as the name of the attachment, but we can
|
133
151
|
create attachment modules for any kind of attachments:
|
134
152
|
|
135
153
|
```rb
|
@@ -143,6 +161,21 @@ class Movie < Sequel::Model
|
|
143
161
|
end
|
144
162
|
```
|
145
163
|
|
164
|
+
### Multiple files
|
165
|
+
|
166
|
+
Sometimes we want to allow users to upload multiple files at once. This can be
|
167
|
+
achieved with by adding a `multiple` HTML attribute to the file field: `<input
|
168
|
+
type="file" multiple>`.
|
169
|
+
|
170
|
+
Shrine doesn't accept multiple files on single a attachment attribute, but you
|
171
|
+
can instead attach each file to a separate database record, which is a much
|
172
|
+
more flexible solution.
|
173
|
+
|
174
|
+
The best way is to [directly upload][direct uploads] selected files, and then
|
175
|
+
send the data of uploaded files as nested attributes for associated records.
|
176
|
+
Alternatively you can send all selected files at once, and then transform them
|
177
|
+
into nested association attributes in the controller.
|
178
|
+
|
146
179
|
## Uploader
|
147
180
|
|
148
181
|
"Uploaders" are subclasses of `Shrine`, and this is where we define all our
|
@@ -184,6 +217,24 @@ the record:
|
|
184
217
|
photo.image #=> #<Shrine::UploadedFile>
|
185
218
|
```
|
186
219
|
|
220
|
+
### Plugins
|
221
|
+
|
222
|
+
Shrine comes with a small core which provides only the essential functionality,
|
223
|
+
and any additional features are available via plugins. This way you can choose
|
224
|
+
exactly what and how much Shrine does for you. See the [website] for a complete
|
225
|
+
list of plugins.
|
226
|
+
|
227
|
+
The plugin system respects inheritance, so you can choose which plugins will
|
228
|
+
be applied to which uploaders:
|
229
|
+
|
230
|
+
```rb
|
231
|
+
Shrine.plugin :logging # enables logging for all uploaders
|
232
|
+
|
233
|
+
class ImageUploader < Shrine
|
234
|
+
plugin :backup # stores backups only for this uploader and its descendants
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
187
238
|
## Processing
|
188
239
|
|
189
240
|
Shrine allows you to perform file processing in functional style; you receive
|
@@ -233,7 +284,7 @@ we're uploading images, we might want to store various thumbnails alongside the
|
|
233
284
|
original image. If we're uploading videos, we might want to save a screenshot
|
234
285
|
or transcode it into different formats.
|
235
286
|
|
236
|
-
To save multiple files, we just need to load the versions plugin, and then in
|
287
|
+
To save multiple files, we just need to load the `versions` plugin, and then in
|
237
288
|
`#process` we can return a Hash of files:
|
238
289
|
|
239
290
|
```rb
|
@@ -257,7 +308,7 @@ end
|
|
257
308
|
Being able to define processing on instance-level like this provides a lot of
|
258
309
|
flexibility. For example, you can choose to process files in a certain order
|
259
310
|
for maximum performance, and you can also add parallelization. It is
|
260
|
-
recommended to load the delete_raw plugin for automatically deleting processed
|
311
|
+
recommended to load the `delete_raw` plugin for automatically deleting processed
|
261
312
|
files after uploading.
|
262
313
|
|
263
314
|
Each version will be saved to the attachment column, and the attachment getter
|
@@ -266,7 +317,7 @@ will simply return a Hash of `Shrine::UploadedFile` objects:
|
|
266
317
|
```rb
|
267
318
|
photo.image #=> {large: ..., medium: ..., small: ...}
|
268
319
|
|
269
|
-
# With the store_dimensions plugin
|
320
|
+
# With the store_dimensions plugin (requires fastimage gem)
|
270
321
|
photo.image[:large].width #=> 700
|
271
322
|
photo.image[:medium].width #=> 500
|
272
323
|
photo.image[:small].width #=> 300
|
@@ -309,8 +360,7 @@ end
|
|
309
360
|
|
310
361
|
You may have noticed the `context` variable floating around as the second
|
311
362
|
argument for processing. This variable is present all the way from input file
|
312
|
-
to uploaded file, and
|
313
|
-
upload:
|
363
|
+
to uploaded file, and can contain useful information depending on the situation:
|
314
364
|
|
315
365
|
* `context[:record]` -- the model instance
|
316
366
|
* `context[:name]` -- attachment name on the model
|
@@ -323,28 +373,50 @@ generating location etc, and it is also used by some plugins internally.
|
|
323
373
|
|
324
374
|
## Validation
|
325
375
|
|
326
|
-
Validations are registered
|
327
|
-
|
376
|
+
Validations are registered inside a `Attacher.validate` block, and you can load
|
377
|
+
the `validation_helpers` plugin to get some convenient file validation methods:
|
328
378
|
|
329
379
|
```rb
|
330
|
-
class
|
380
|
+
class VideoUploader < Shrine
|
331
381
|
plugin :validation_helpers
|
332
382
|
|
333
383
|
Attacher.validate do
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
384
|
+
validate_max_size 50*1024*1024, message: "is too large (max is 50 MB)"
|
385
|
+
validate_mime_type_inclusion ["video/mp4"]
|
386
|
+
end
|
387
|
+
end
|
388
|
+
```
|
389
|
+
|
390
|
+
```rb
|
391
|
+
trailer = Trailer.new
|
392
|
+
trailer.video = File.open("matrix.mp4")
|
393
|
+
trailer.valid? #=> false
|
394
|
+
trailer.errors.to_hash #=> {video: ["is too large (max is 50 MB)"]}
|
395
|
+
```
|
396
|
+
|
397
|
+
You can also do custom validations:
|
398
|
+
|
399
|
+
```rb
|
400
|
+
class VideoUploader < Shrine
|
401
|
+
Attacher.validate do
|
402
|
+
errors << "is longer than 5 minutes" if get.duration > 300
|
339
403
|
end
|
340
404
|
end
|
341
405
|
```
|
342
406
|
|
407
|
+
The `Attacher.validate` block is executed in context of a `Shrine::Attacher`
|
408
|
+
instance:
|
409
|
+
|
343
410
|
```rb
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
411
|
+
class VideoUploader < Shrine
|
412
|
+
Attacher.validate do
|
413
|
+
self #=> #<Shrine::Attacher>
|
414
|
+
|
415
|
+
get #=> #<Shrine::UploadedFile>
|
416
|
+
record # the model instance
|
417
|
+
errors # array of error messages for this file
|
418
|
+
end
|
419
|
+
end
|
348
420
|
```
|
349
421
|
|
350
422
|
## Metadata
|
@@ -374,7 +446,7 @@ which is set from the "Content-Type" request header, which is determined by the
|
|
374
446
|
browser solely based on the file extension. This means that by default Shrine's
|
375
447
|
"mime_type" is *not* guaranteed to hold the actual MIME type of the file.
|
376
448
|
|
377
|
-
To help with that Shrine provides the determine_mime_type plugin, which by
|
449
|
+
To help with that Shrine provides the `determine_mime_type` plugin, which by
|
378
450
|
default uses the UNIX [file] utility to determine the actual MIME type:
|
379
451
|
|
380
452
|
```rb
|
@@ -388,8 +460,8 @@ photo.image.mime_type #=> "text/x-php"
|
|
388
460
|
|
389
461
|
### Custom metadata
|
390
462
|
|
391
|
-
You can also extract and store completely custom metadata with the
|
392
|
-
plugin:
|
463
|
+
You can also extract and store completely custom metadata with the
|
464
|
+
`add_metadata` plugin:
|
393
465
|
|
394
466
|
```rb
|
395
467
|
require "mini_magick"
|
@@ -397,7 +469,7 @@ require "mini_magick"
|
|
397
469
|
class ImageUploader < Shrine
|
398
470
|
plugin :add_metadata
|
399
471
|
|
400
|
-
add_metadata
|
472
|
+
add_metadata :exif do |io, context|
|
401
473
|
MiniMagick::Image.new(io.path).exif
|
402
474
|
end
|
403
475
|
end
|
@@ -413,7 +485,7 @@ photo.image.exif
|
|
413
485
|
Before Shrine uploads a file, it generates a random location for it. By
|
414
486
|
default the hierarchy is flat, all files are stored in the root of the storage.
|
415
487
|
If you want that each attachment has its own directory, you can load the
|
416
|
-
pretty_location plugin:
|
488
|
+
`pretty_location` plugin:
|
417
489
|
|
418
490
|
```rb
|
419
491
|
Shrine.plugin :pretty_location
|
@@ -489,7 +561,7 @@ website.
|
|
489
561
|
### Upload options
|
490
562
|
|
491
563
|
Many storages accept additional upload options, which you can pass via the
|
492
|
-
upload_options plugin, or manually when uploading:
|
564
|
+
`upload_options` plugin, or manually when uploading:
|
493
565
|
|
494
566
|
```rb
|
495
567
|
uploader = MyUploader.new(:store)
|
@@ -498,40 +570,40 @@ uploader.upload(file, upload_options: {acl: "private"})
|
|
498
570
|
|
499
571
|
## Direct uploads
|
500
572
|
|
501
|
-
Shrine comes with a
|
502
|
-
|
503
|
-
|
504
|
-
library).
|
573
|
+
Shrine comes with a `direct_upload` plugin for asynchronous uploads to your
|
574
|
+
app or an external service. It provides a [Roda] endpoint which you can mount
|
575
|
+
in your app:
|
505
576
|
|
506
577
|
```rb
|
507
|
-
|
578
|
+
gem "roda"
|
579
|
+
```
|
580
|
+
```rb
|
581
|
+
Shrine.plugin :direct_upload
|
508
582
|
```
|
509
583
|
```rb
|
510
584
|
Rails.application.routes.draw do
|
511
|
-
mount
|
585
|
+
mount ImageUploader::UploadEndpoint => "/images"
|
512
586
|
end
|
513
587
|
```
|
514
|
-
```js
|
515
|
-
$('[type="file"]').fileupload({
|
516
|
-
url: '/videos/cache/upload',
|
517
|
-
paramName: 'file',
|
518
|
-
add: function(e, data) { /* Disable the submit button */ },
|
519
|
-
progress: function(e, data) { /* Add a nice progress bar */ },
|
520
|
-
done: function(e, data) { /* Fill in the hidden field with the result */ }
|
521
|
-
});
|
522
|
-
```
|
523
588
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
589
|
+
This endpoint provides the following routes:
|
590
|
+
|
591
|
+
* `POST /images/cache/upload` - for direct uploads to your app
|
592
|
+
* `GET /images/cache/presign` - for direct uploads to external service
|
593
|
+
|
594
|
+
These routes can be used to asynchronously start caching the file the moment
|
595
|
+
the user selects it, using JavaScript file upload libraries like
|
596
|
+
[jQuery-File-Upload], [Dropzone] or [FineUploader].
|
597
|
+
|
598
|
+
See the [direct_upload] plugin documentation and [Direct Uploads to S3] guide
|
599
|
+
for more details, as well as the [Roda][roda_demo] and [Rails][rails_demo]
|
600
|
+
demo apps which implement multiple uploads directly to S3.
|
529
601
|
|
530
602
|
## Backgrounding
|
531
603
|
|
532
604
|
Shrine is the first file upload library designed for backgrounding support.
|
533
605
|
Moving phases of managing attachments to background jobs is essential for
|
534
|
-
scaling and good user experience, and Shrine provides a backgrounding plugin
|
606
|
+
scaling and good user experience, and Shrine provides a `backgrounding` plugin
|
535
607
|
which makes it really easy to plug in your favourite backgrounding library:
|
536
608
|
|
537
609
|
```rb
|
@@ -586,34 +658,16 @@ file_system = Shrine.storages[:cache]
|
|
586
658
|
file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
|
587
659
|
```
|
588
660
|
|
589
|
-
## Plugins
|
590
|
-
|
591
|
-
Shrine comes with a small core which provides only the essential functionality,
|
592
|
-
and any additional features are available via plugins. This way you can choose
|
593
|
-
exactly what and how much Shrine does for you. Shrine itself [ships with over
|
594
|
-
35 plugins], most of which I didn't cover here.
|
595
|
-
|
596
|
-
The plugin system respects inheritance, so you can choose which plugins will
|
597
|
-
be applied to which uploaders:
|
598
|
-
|
599
|
-
```rb
|
600
|
-
Shrine.plugin :logging # enables logging for all uploaders
|
601
|
-
|
602
|
-
class ImageUploader < Shrine
|
603
|
-
plugin :store_dimensions # stores dimensions only for this uploader and its descendants
|
604
|
-
end
|
605
|
-
```
|
606
|
-
|
607
661
|
## On-the-fly processing
|
608
662
|
|
609
663
|
Shrine allows you to define processing that will be performed on upload.
|
610
|
-
However, what if want to
|
611
|
-
requested? Unlike Refile or Dragonfly, Shrine doesn't come with an image
|
612
|
-
built in
|
613
|
-
servers.
|
664
|
+
However, what if you want to have processing performed on-the-fly when the URL
|
665
|
+
is requested? Unlike Refile or Dragonfly, Shrine doesn't come with an image
|
666
|
+
server built in; instead it expects you to integrate any of the existing
|
667
|
+
generic image servers.
|
614
668
|
|
615
|
-
Shrine has integrations for many commercial on-the-fly processing services,
|
616
|
-
|
669
|
+
Shrine has integrations for many commercial on-the-fly processing services,
|
670
|
+
including [Cloudinary], [Imgix] and [Uploadcare].
|
617
671
|
|
618
672
|
If you don't want to use a commercial service, [Attache] is a great open-source
|
619
673
|
image server. There isn't a Shrine integration written for it yet, but it
|
@@ -643,11 +697,12 @@ The gem is available as open source under the terms of the [MIT License].
|
|
643
697
|
[image bombs]: https://www.bamsoftware.com/hacks/deflate.html
|
644
698
|
[aws-sdk]: https://github.com/aws/aws-sdk-ruby
|
645
699
|
[jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
|
700
|
+
[Dropzone]: https://github.com/enyo/dropzone
|
701
|
+
[FineUploader]: https://github.com/FineUploader/fine-uploader
|
646
702
|
[Roda]: https://github.com/jeremyevans/roda
|
647
703
|
[Refile]: https://github.com/refile/refile
|
648
704
|
[plugin system]: http://twin.github.io/the-plugin-system-of-sequel-and-roda/
|
649
705
|
[MIT License]: http://opensource.org/licenses/MIT
|
650
|
-
[example app]: https://github.com/janko-m/shrine-example
|
651
706
|
[ships with over 35 plugins]: http://shrinerb.com#plugins
|
652
707
|
[introductory blog post]: http://twin.github.io/introducing-shrine/
|
653
708
|
[FileSystem]: http://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
|
@@ -657,7 +712,11 @@ The gem is available as open source under the terms of the [MIT License].
|
|
657
712
|
[direct uploads]: #direct-uploads
|
658
713
|
[ffmpeg]: https://github.com/streamio/streamio-ffmpeg
|
659
714
|
[direct_upload]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/DirectUpload.html
|
660
|
-
[
|
661
|
-
[
|
662
|
-
[
|
715
|
+
[Cloudinary]: https://github.com/janko-m/shrine-cloudinary
|
716
|
+
[Imgix]: https://github.com/janko-m/shrine-imgix
|
717
|
+
[Uploadcare]: https://github.com/janko-m/shrine-uploadcare
|
663
718
|
[Attache]: https://github.com/choonkeat/attache
|
719
|
+
[roda_demo]: /demo
|
720
|
+
[rails_demo]: https://github.com/erikdahlstrand/shrine-rails-example
|
721
|
+
[Direct Uploads to S3]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|
722
|
+
[website]: http://shrinerb.com
|
data/doc/carrierwave.md
CHANGED
@@ -1,97 +1,236 @@
|
|
1
1
|
# Shrine for CarrierWave Users
|
2
2
|
|
3
|
-
This guide is aimed at helping CarrierWave users transition to Shrine
|
4
|
-
|
5
|
-
Afterwards it explains how you can transition an existing app that uses
|
6
|
-
CarrierWave to Shrine. It then finishes off with an extensive reference of
|
7
|
-
CarrierWave's interface and what is the equivalent in Shrine.
|
3
|
+
This guide is aimed at helping CarrierWave users transition to Shrine, and it
|
4
|
+
consists of three parts:
|
8
5
|
|
9
|
-
|
6
|
+
1. Explanation of the key differences in design between CarrierWave and Shrine
|
7
|
+
2. Instructions how to migrate and existing app that uses CarrierWave to Shrine
|
8
|
+
3. Extensive reference of CarrierWave's interface with Shrine equivalents
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
## Storage
|
11
|
+
|
12
|
+
While in CarrierWave you configure storage in global configuration, in Shrine
|
13
|
+
storage is a class which you can pass options to during initialization:
|
13
14
|
|
14
15
|
```rb
|
15
|
-
|
16
|
-
|
16
|
+
CarrierWave.configure do |config|
|
17
|
+
config.fog_provider = "fog/aws"
|
18
|
+
config.fog_credentials = {
|
19
|
+
provider: "AWS",
|
20
|
+
aws_access_key_id: "abc",
|
21
|
+
aws_secret_access_key: "xyz",
|
22
|
+
}
|
23
|
+
config.fog_directory = "my-bucket"
|
17
24
|
end
|
18
25
|
```
|
26
|
+
```rb
|
27
|
+
Shrine.storages[:store] = Shrine::Storage::S3.new(
|
28
|
+
bucket: "my-bucket",
|
29
|
+
aws_access_key_id: "abc",
|
30
|
+
aws_secret_access_key: "xyz",
|
31
|
+
)
|
32
|
+
```
|
19
33
|
|
20
|
-
|
21
|
-
|
22
|
-
|
34
|
+
In CarrierWave temporary storage cannot be configured; it saves and retrieves
|
35
|
+
files from the filesystem, you can only set the directory. With Shrine both
|
36
|
+
temporary (`:cache`) and permanent (`:store`) storage are first-class citizens
|
37
|
+
and fully configurable, so you can also have files *cached* on S3 (preferrably
|
38
|
+
via [direct uploads]):
|
23
39
|
|
24
40
|
```rb
|
25
|
-
require "shrine/storage/file_system"
|
26
|
-
|
27
41
|
Shrine.storages = {
|
28
|
-
cache: Shrine::
|
29
|
-
store: Shrine::
|
42
|
+
cache: Shrine::Storages::S3.new(prefix: "cache", **s3_options),
|
43
|
+
store: Shrine::Storages::S3.new(prefix: "store", **s3_options),
|
30
44
|
}
|
31
45
|
```
|
46
|
+
|
47
|
+
## Uploader
|
48
|
+
|
49
|
+
Shrine shares CarrierWave's concept of *uploaders*, classes which encapsulate
|
50
|
+
file attachment logic for different file types:
|
51
|
+
|
32
52
|
```rb
|
33
|
-
|
34
|
-
|
53
|
+
class ImageUploader < Shrine
|
54
|
+
# attachment logic
|
55
|
+
end
|
35
56
|
```
|
36
57
|
|
37
|
-
CarrierWave
|
38
|
-
|
39
|
-
|
40
|
-
|
58
|
+
However, uploaders in CarrierWave are very broad; in addition to uploading and
|
59
|
+
deleting files, they also represent the uploaded file. Shrine has a separate
|
60
|
+
`Shrine::UploadedFile` class which represents the uploaded file.
|
61
|
+
|
62
|
+
```rb
|
63
|
+
uploader = ImageUploader.new(:store)
|
64
|
+
uploaded_file = uploader.upload(image)
|
65
|
+
uploaded_file #=> #<Shrine::UploadedFile>
|
66
|
+
uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
|
67
|
+
uploaded_file.download #=> #<Tempfile>
|
68
|
+
```
|
41
69
|
|
42
70
|
### Processing
|
43
71
|
|
44
|
-
In
|
45
|
-
|
46
|
-
versions:
|
72
|
+
In contrast to CarrierWave's class-level DSL, in Shrine processing is defined
|
73
|
+
and performed on the instance-level. The result of processing can be a single
|
74
|
+
file or a hash of versions:
|
47
75
|
|
48
76
|
```rb
|
49
|
-
|
77
|
+
class ImageUploader < CarrierWave::Uploader::Base
|
78
|
+
include CarrierWave::MiniMagick
|
79
|
+
|
80
|
+
process resize_to_limit: [800, 800]
|
81
|
+
|
82
|
+
version :medium do
|
83
|
+
process resize_to_limit: [500, 500]
|
84
|
+
end
|
50
85
|
|
86
|
+
version :small, from_version: :medium do
|
87
|
+
process resize_to_limit: [300, 300]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
```rb
|
51
93
|
class ImageUploader < Shrine
|
52
94
|
include ImageProcessing::MiniMagick
|
53
95
|
plugin :processing
|
54
96
|
plugin :versions
|
55
97
|
|
56
98
|
process(:store) do |io, context|
|
57
|
-
|
58
|
-
|
99
|
+
size_800 = resize_to_limit(io.download, 800, 800)
|
100
|
+
size_500 = resize_to_limit(size_800, 500, 500)
|
101
|
+
size_300 = resize_to_limit(size_500, 300, 300)
|
102
|
+
|
103
|
+
{original: size_800, medium: size_500, small: size_300}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
This allows you to fully optimize processing, because you can easily specify
|
109
|
+
which files are processed from which, and even add parallelization.
|
110
|
+
|
111
|
+
CarrierWave performs processing before validations, which is a huge security
|
112
|
+
issue, as it allows users to give arbitrary files to your processing tool, even
|
113
|
+
if you have validations. Shrine performs processing after validations.
|
114
|
+
|
115
|
+
#### Reprocessing versions
|
116
|
+
|
117
|
+
Shrine doesn't have a built-in way of regenerating versions, because that has
|
118
|
+
to be written and optimized differently depending on whether you're adding or
|
119
|
+
removing a version, what ORM are you using, how many records there are in the
|
120
|
+
database etc. The [Reprocessing versions] guide provides some useful tips on
|
121
|
+
this task.
|
122
|
+
|
123
|
+
### Validations
|
124
|
+
|
125
|
+
Like with processing, validations in Shrine are also defined and performed on
|
126
|
+
instance-level:
|
127
|
+
|
128
|
+
```rb
|
129
|
+
class ImageUploader < CarrierWave::Uploader::Base
|
130
|
+
def extension_whitelist
|
131
|
+
%w[jpg jpeg gif png]
|
132
|
+
end
|
133
|
+
|
134
|
+
def content_type_whitelist
|
135
|
+
/image\//
|
136
|
+
end
|
137
|
+
|
138
|
+
def size_range
|
139
|
+
0..(10*1024*1024)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
```rb
|
145
|
+
class ImageUploader < Shrine
|
146
|
+
plugin :validation_helpers
|
147
|
+
|
148
|
+
Attacher.validate do
|
149
|
+
validate_extension_inclusion [/jpe?g/, "gif", "png"]
|
150
|
+
validate_mime_type_inclusion %w[image/jpeg image/gif image/png]
|
151
|
+
validate_max_size 10*1024*1024 unless record.admin?
|
59
152
|
end
|
60
153
|
end
|
61
154
|
```
|
62
155
|
|
63
156
|
## Attachments
|
64
157
|
|
65
|
-
Like CarrierWave, Shrine also provides integrations with ORMs
|
66
|
-
plugins for both Sequel and ActiveRecord
|
67
|
-
|
158
|
+
Like CarrierWave, Shrine also provides integrations with ORMs. It ships with
|
159
|
+
plugins for both Sequel and ActiveRecord, but can also be used with just PORO
|
160
|
+
models.
|
68
161
|
|
69
162
|
```rb
|
70
|
-
Shrine.plugin :sequel #
|
71
|
-
Shrine.plugin :activerecord #
|
163
|
+
Shrine.plugin :sequel # if you're using Sequel
|
164
|
+
Shrine.plugin :activerecord # if you're using ActiveRecord
|
72
165
|
```
|
73
166
|
|
74
167
|
Instead of giving you class methods for "mounting" uploaders, in Shrine you
|
75
|
-
generate attachment modules which you simply include in your models
|
168
|
+
generate attachment modules which you simply include in your models, which
|
169
|
+
gives your models similar set of methods that CarrierWave gives:
|
76
170
|
|
77
171
|
```rb
|
78
|
-
class
|
79
|
-
|
172
|
+
class Photo < ActiveRecord::Base
|
173
|
+
extend CarrierWave::ActiveRecord # done automatically by CarrierWave
|
174
|
+
mount_uploader :image, ImageUploader
|
80
175
|
end
|
81
176
|
```
|
177
|
+
```rb
|
178
|
+
class Photo < ActiveRecord::Base
|
179
|
+
include ImageUploader[:avatar]
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
### Attachment column
|
184
|
+
|
185
|
+
You models are required to have the `<attachment>_data` column, which Shrine
|
186
|
+
uses to save storage, location, and metadata of the uploaded file.
|
187
|
+
|
188
|
+
```rb
|
189
|
+
photo.image_data #=>
|
190
|
+
# {
|
191
|
+
# "storage" => "store",
|
192
|
+
# "id" => "photo/1/image/0d9o8dk42.png",
|
193
|
+
# "metadata" => {
|
194
|
+
# "filename" => "nature.png",
|
195
|
+
# "size" => 49349138,
|
196
|
+
# "mime_type" => "image/png"
|
197
|
+
# }
|
198
|
+
# }
|
199
|
+
|
200
|
+
photo.image.original_filename #=> "nature.png"
|
201
|
+
photo.image.size #=> 49349138
|
202
|
+
photo.image.mime_type #=> "image/png"
|
203
|
+
```
|
204
|
+
|
205
|
+
This is much more powerful than storing only the filename like CarrierWave
|
206
|
+
does, as it allows you to also store any additional metadata that you might
|
207
|
+
want to extract.
|
208
|
+
|
209
|
+
Unlike CarrierWave, Shrine will store this information for each processed
|
210
|
+
version, making them first-class citizens:
|
211
|
+
|
212
|
+
```rb
|
213
|
+
photo.image[:original] #=> #<Shrine::UploadedFile>
|
214
|
+
photo.image[:original].width #=> 800
|
215
|
+
|
216
|
+
photo.image[:thumb] #=> #<Shrine::UploadedFile>
|
217
|
+
photo.image[:thumb].width #=> 300
|
218
|
+
```
|
82
219
|
|
83
|
-
|
84
|
-
|
85
|
-
|
220
|
+
Also, since CarrierWave stores only the filename, it has to recalculate the
|
221
|
+
full location each time it wants to generate the URL. That makes it really
|
222
|
+
difficult to move files to a new location, because changing how the location is
|
223
|
+
generated will now cause incorrect URLs to be generated for all existing files.
|
224
|
+
Shrine calculates the whole location only once and saves it to the column.
|
86
225
|
|
87
226
|
### Multiple uploads
|
88
227
|
|
89
228
|
Shrine doesn't have support for multiple uploads like CarrierWave does, instead
|
90
|
-
it expects that you will
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
229
|
+
it expects that you will attach each file to a separate database record. This
|
230
|
+
is a good thing, because the implementation is specific to the ORM you're
|
231
|
+
using, and it's analogous to how you would implement any nested one-to-many
|
232
|
+
associations. Take a look at the [demo app] which shows how easy it is to
|
233
|
+
implement multiple uploads.
|
95
234
|
|
96
235
|
## Migrating from CarrierWave
|
97
236
|
|
@@ -509,6 +648,7 @@ No equivalent, it depends on your application whether you need the form to be
|
|
509
648
|
multipart or not.
|
510
649
|
|
511
650
|
[image_processing]: https://github.com/janko-m/image_processing
|
512
|
-
[
|
513
|
-
[
|
651
|
+
[demo app]: https://github.com/janko-m/shrine/tree/master/demo
|
652
|
+
[Reprocessing versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
|
514
653
|
[shrine-fog]: https://github.com/janko-m/shrine-fog
|
654
|
+
[direct uploads]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|