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.

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +143 -84
  3. data/doc/carrierwave.md +187 -47
  4. data/doc/direct_s3.md +57 -39
  5. data/doc/paperclip.md +183 -91
  6. data/doc/refile.md +148 -124
  7. data/doc/regenerating_versions.md +2 -3
  8. data/lib/shrine.rb +26 -28
  9. data/lib/shrine/plugins/activerecord.rb +22 -31
  10. data/lib/shrine/plugins/add_metadata.rb +1 -1
  11. data/lib/shrine/plugins/backgrounding.rb +19 -7
  12. data/lib/shrine/plugins/backup.rb +2 -2
  13. data/lib/shrine/plugins/cached_attachment_data.rb +1 -1
  14. data/lib/shrine/plugins/copy.rb +52 -0
  15. data/lib/shrine/plugins/data_uri.rb +1 -1
  16. data/lib/shrine/plugins/default_storage.rb +2 -2
  17. data/lib/shrine/plugins/default_url.rb +1 -1
  18. data/lib/shrine/plugins/default_url_options.rb +1 -1
  19. data/lib/shrine/plugins/delete_promoted.rb +1 -1
  20. data/lib/shrine/plugins/delete_raw.rb +1 -1
  21. data/lib/shrine/plugins/determine_mime_type.rb +3 -2
  22. data/lib/shrine/plugins/direct_upload.rb +36 -24
  23. data/lib/shrine/plugins/download_endpoint.rb +3 -3
  24. data/lib/shrine/plugins/dynamic_storage.rb +2 -2
  25. data/lib/shrine/plugins/hooks.rb +1 -1
  26. data/lib/shrine/plugins/included.rb +3 -4
  27. data/lib/shrine/plugins/keep_files.rb +1 -1
  28. data/lib/shrine/plugins/logging.rb +1 -1
  29. data/lib/shrine/plugins/module_include.rb +1 -1
  30. data/lib/shrine/plugins/moving.rb +10 -5
  31. data/lib/shrine/plugins/multi_delete.rb +2 -2
  32. data/lib/shrine/plugins/parallelize.rb +2 -2
  33. data/lib/shrine/plugins/parsed_json.rb +1 -1
  34. data/lib/shrine/plugins/pretty_location.rb +1 -1
  35. data/lib/shrine/plugins/processing.rb +11 -9
  36. data/lib/shrine/plugins/rack_file.rb +1 -1
  37. data/lib/shrine/plugins/recache.rb +14 -4
  38. data/lib/shrine/plugins/remote_url.rb +1 -1
  39. data/lib/shrine/plugins/remove_attachment.rb +3 -4
  40. data/lib/shrine/plugins/remove_invalid.rb +1 -1
  41. data/lib/shrine/plugins/restore_cached_data.rb +11 -4
  42. data/lib/shrine/plugins/sequel.rb +34 -45
  43. data/lib/shrine/plugins/store_dimensions.rb +1 -1
  44. data/lib/shrine/plugins/upload_options.rb +2 -2
  45. data/lib/shrine/plugins/validation_helpers.rb +7 -8
  46. data/lib/shrine/plugins/versions.rb +31 -30
  47. data/lib/shrine/storage/file_system.rb +16 -12
  48. data/lib/shrine/storage/s3.rb +36 -2
  49. data/lib/shrine/version.rb +1 -1
  50. data/shrine.gemspec +9 -8
  51. metadata +11 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06b571aff1653427247e09a1ec98f9e11a10eb34
4
- data.tar.gz: cbdd5d75713e42ea5aa449eb1c0a604b2eae35a9
3
+ metadata.gz: ed4ba1e9f5dafe48af21c7f80039ca3d0014e600
4
+ data.tar.gz: 2f8f7903b3032d8ef43508a0e7259ab5deaedb25
5
5
  SHA512:
6
- metadata.gz: 44c88e4f5bb848bc8854cde7ff4eb0fd3209f3b829998cad60377bb975816d94fce356ed8728f1ff86152523aa0e735e07dd6c9d1e008000170d312565e799d1
7
- data.tar.gz: 056383f1b82fb43ecd62d3a43923c9864323c8c70ace4532ee35e293d94c74ae86b190d83f2870b4b44f7ee005d7688e443bb783b34cc38a8976bfdb5b6c5fe3
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 handling file uploads in Ruby applications.
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
- And add attachment fields to the Photo form:
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 when a Photo is created with the image attached, you can get the URL to
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/storage/metadata of the uploaded file to a single `<attachment>_data`
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 uploaded files:
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 will upload the attachment to permanent storage
118
- (`:store`) when the record is saved, and delete the attachment when record
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
- In these examples we used `image` as the name of the attachment, but we can
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 contains any additional information that can affect the
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 by calling `Attacher.validate`, and are best done
327
- with the validation_helpers plugin:
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 DocumentUploader < Shrine
380
+ class VideoUploader < Shrine
331
381
  plugin :validation_helpers
332
382
 
333
383
  Attacher.validate do
334
- # Evaluated inside an instance of Shrine::Attacher.
335
- if record.resume?
336
- validate_max_size 10*1024*1024, message: "is too large (max is 10 MB)"
337
- validate_mime_type_inclusion ["application/pdf"]
338
- end
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
- document = Document.new(resume: true)
345
- document.file = File.open("resume.pdf")
346
- document.valid? #=> false
347
- document.errors.to_hash #=> {file: ["is too large (max is 2 MB)"]}
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 metadata
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 "exif" do |io, context|
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 [direct_upload] plugin which provides a [Roda] endpoint that
502
- accepts file uploads. This allows you to asynchronously start caching the file
503
- the moment the user selects it via AJAX (e.g. using the [jQuery-File-Upload] JS
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
- Shrine.plugin :direct_upload # Provides a Roda endpoint
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 VideoUploader::UploadEndpoint => "/videos"
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
- Along with the upload route, this endpoint also includes a route for generating
525
- presigns for direct uploads to 3rd-party services like Amazon S3. See the
526
- [direct_upload] plugin documentation for more details, as well as the
527
- [Roda](https://github.com/janko-m/shrine-example)/[Rails](https://github.com/erikdahlstrand/shrine-rails-example)
528
- example apps which demonstrate multiple uploads directly to S3.
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 perform processing on-the-fly, only when the URL is
611
- requested? Unlike Refile or Dragonfly, Shrine doesn't come with an image server
612
- built in, instead it expects you to integrate any of the existing generic image
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, so
616
- you can use [shrine-cloudinary], [shrine-imgix] or [shrine-uploadcare].
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
- [shrine-cloudinary]: https://github.com/janko-m/shrine-cloudinary
661
- [shrine-imgix]: https://github.com/janko-m/shrine-imgix
662
- [shrine-uploadcare]: https://github.com/janko-m/shrine-uploadcare
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
@@ -1,97 +1,236 @@
1
1
  # Shrine for CarrierWave Users
2
2
 
3
- This guide is aimed at helping CarrierWave users transition to Shrine. First it
4
- explains some key differences in the design between the two libraries.
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
- ## Uploaders
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
- Shrine has a concept of uploaders similar to CarrierWave's, which allows you to
12
- have different uploading logic for different types of files:
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
- class ImageUploader < Shrine
16
- # uploading logic
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
- While in CarrierWave you choose a storages for uploaders directly, in Shrine
21
- you first register storages globally (under a symbol name), and then you can
22
- instantiate uploaders with a specific storage.
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::Storage::FileSystem.new("public", prefix: "uploads/cache"),
29
- store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
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
- cache_uploader = Shrine.new(:cache)
34
- store_uploader = Shrine.new(:store)
53
+ class ImageUploader < Shrine
54
+ # attachment logic
55
+ end
35
56
  ```
36
57
 
37
- CarrierWave uses symbols for referencing storages (`:file`, `:fog`, ...), but
38
- in Shrine storages are simple Ruby classes which you can instantiate directly.
39
- This makes storages much more flexible, because this way they can have their
40
- own options that are specific to them.
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 Shrine processing is defined and performed on the instance-level, which
45
- gives a lot of flexibility. You can return a single processed file or a hash of
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
- require "image_processing/mini_magick" # part of the "image_processing" gem
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
- thumbnail = resize_to_limit(io.download, 300, 300)
58
- {original: io, thumbnail: thumbnail}
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, it ships with
66
- plugins for both Sequel and ActiveRecord (but it can also be used with simple
67
- PORO models).
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 # If you're using Sequel
71
- Shrine.plugin :activerecord # If you're using 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 User < Sequel::Model
79
- include ImageUploader[:avatar] # adds `avatar`, `avatar=` and `avatar_url` methods
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
- You models are required to have the `<attachment>_data` column, in the above
84
- case `avatar_data`. Shrine stores storage, location, and additional metadata of
85
- the uploaded file to that column.
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 implement multiple uploads yourself using a separate
91
- model. This is a good thing, because the implementation is specific to the ORM
92
- you're using, and it's analogous to how you would implement adding items to any
93
- dynamic one-to-many relationship. Take a look at the [example app] which
94
- demonstrates how easy it is to implement multiple uploads.
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
- [example app]: https://github.com/janko-m/shrine-example
513
- [Regenerating versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
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