shrine 3.0.0.beta2 → 3.0.0.beta3
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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +45 -1
 - data/README.md +100 -106
 - data/doc/advantages.md +90 -88
 - data/doc/attacher.md +322 -152
 - data/doc/carrierwave.md +105 -113
 - data/doc/changing_derivatives.md +308 -0
 - data/doc/changing_location.md +92 -21
 - data/doc/changing_storage.md +107 -0
 - data/doc/creating_plugins.md +1 -1
 - data/doc/design.md +8 -9
 - data/doc/direct_s3.md +3 -2
 - data/doc/metadata.md +97 -78
 - data/doc/multiple_files.md +3 -3
 - data/doc/paperclip.md +89 -88
 - data/doc/plugins/activerecord.md +3 -12
 - data/doc/plugins/backgrounding.md +126 -100
 - data/doc/plugins/derivation_endpoint.md +4 -5
 - data/doc/plugins/derivatives.md +63 -32
 - data/doc/plugins/download_endpoint.md +54 -1
 - data/doc/plugins/entity.md +1 -0
 - data/doc/plugins/form_assign.md +53 -0
 - data/doc/plugins/mirroring.md +37 -16
 - data/doc/plugins/multi_cache.md +22 -0
 - data/doc/plugins/presign_endpoint.md +1 -1
 - data/doc/plugins/remote_url.md +19 -4
 - data/doc/plugins/validation.md +83 -0
 - data/doc/processing.md +149 -133
 - data/doc/refile.md +68 -63
 - data/doc/release_notes/3.0.0.md +835 -0
 - data/doc/securing_uploads.md +56 -36
 - data/doc/storage/s3.md +2 -2
 - data/doc/testing.md +104 -120
 - data/doc/upgrading_to_3.md +538 -0
 - data/doc/validation.md +48 -87
 - data/lib/shrine.rb +7 -4
 - data/lib/shrine/attacher.rb +16 -6
 - data/lib/shrine/plugins/activerecord.rb +33 -14
 - data/lib/shrine/plugins/atomic_helpers.rb +1 -1
 - data/lib/shrine/plugins/backgrounding.rb +23 -89
 - data/lib/shrine/plugins/data_uri.rb +13 -2
 - data/lib/shrine/plugins/derivation_endpoint.rb +7 -11
 - data/lib/shrine/plugins/derivatives.rb +44 -20
 - data/lib/shrine/plugins/download_endpoint.rb +26 -0
 - data/lib/shrine/plugins/form_assign.rb +6 -3
 - data/lib/shrine/plugins/keep_files.rb +2 -2
 - data/lib/shrine/plugins/mirroring.rb +62 -22
 - data/lib/shrine/plugins/model.rb +2 -2
 - data/lib/shrine/plugins/multi_cache.rb +27 -0
 - data/lib/shrine/plugins/remote_url.rb +25 -10
 - data/lib/shrine/plugins/remove_invalid.rb +1 -1
 - data/lib/shrine/plugins/sequel.rb +39 -20
 - data/lib/shrine/plugins/validation.rb +3 -0
 - data/lib/shrine/storage/s3.rb +16 -1
 - data/lib/shrine/uploaded_file.rb +1 -0
 - data/lib/shrine/version.rb +1 -1
 - data/shrine.gemspec +1 -1
 - metadata +12 -7
 - data/doc/migrating_storage.md +0 -76
 - data/doc/regenerating_versions.md +0 -143
 - data/lib/shrine/plugins/attacher_options.rb +0 -55
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ad93860597c6a720f0159063d8cbb95a6aa0e93ea89947803342500f1193eb64
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 79801e74ecd993986099f0f4679fcb37c2b778157c4df5bf6bb80064f2338a44
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 12337c687f57272f9af85b8b8831de448fea258e25e6b7191b95131a60e705374807a87efb7c5e4e7c3db0c6f11a8c3dc87dfe21255d24785e908a93cc94be7e
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 04561d887c0612ed193c31c470fcdd9ca5ed466794923ceecf979f78049ec3c9c46cf0161776121c72419bcc5f77babfc565ceaf6892a06a547bd93f9c1cccce
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 3.0.0.beta3 (2019-09-25)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * `multi_cache` – Add new plugin for whitelisting additional temporary storages (@janko, @jrochkind)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * `s3` – Allow uploading files larger than 50 GB (@janko)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            * `sequel` – Extract callback code into attacher methods that can be overridden (@janko)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            * `activerecord` – Extract callback code into attacher methods that can be overridden (@janko)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * `derivatives` – Ensure binary mode for `File` and `Tempfile` objects (@janko)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            * `derivatives` – Ensure refreshed file descriptor for `Tempfile` objects (@janko)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            * `derivation_endpoint` – Stop re-opening `File` objects returned in derivation result (@janko)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            * `derivation_endpoint` – Allow only `File` or `Tempfile` object as derivation result (@janko)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            * `derivatives` – Add support for default processors (@janko)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            * `remote_url` – Convert only `Down::Error` and `DownloadError` exceptions into validation errors (@janko)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            * `remote_url` – Bring back `Attacher#remote_url=` and `Attacher#remote_url` (@janko)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            * `data_uri` – Bring back `Attacher#data_uri=` and `Attacher#data_uri` (@janko)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            * `mirroring` – Allow skipping mirroring by passing `mirror: false` to `#upload` and `#delete` (@janko)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            * `mirroring` – Rename `Shrine.mirror_(upload|delete)` to `Shrine.mirror_(upload|delete)_block` (@janko)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            * `mirroring` – Add `UploadedFile#mirror_(upload|delete)_background` (@janko)
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            * `attacher_options` – Remove plugin (@janko)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            * `backgrounding` – Move `Attacher#destroy(background: true)` to `Attacher#destroy_background` (@janko)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            * `backgrounding` – Move `Attacher#promote(background: true)` to `Attacher#promote_background` (@janko)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            * `download_endpoint` – Add `Shrine.download_response` for calling in controller (@janko)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            * `core` – Fetch storage object lazily in `Shrine` instance (@janko)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
       1 
43 
     | 
    
         
             
            ## 3.0.0.beta2 (2019-09-11)
         
     | 
| 
       2 
44 
     | 
    
         | 
| 
       3 
45 
     | 
    
         
             
            * `column` – Allow `Attacher#load_column` to receive a hash (@janko)
         
     | 
| 
         @@ -154,7 +196,7 @@ 
     | 
|
| 
       154 
196 
     | 
    
         | 
| 
       155 
197 
     | 
    
         
             
            * `upload_endpoint` – Stop passing `Rack::Request` object to the uploader (@janko)
         
     | 
| 
       156 
198 
     | 
    
         | 
| 
       157 
     | 
    
         
            -
            * `remote_url` – Require custom  
     | 
| 
      
 199 
     | 
    
         
            +
            * `remote_url` – Require custom downloaders to raise `Shrine::Plugins::RemoteUrl::DownloadError` for conversion into a validation error (@janko)
         
     | 
| 
       158 
200 
     | 
    
         | 
| 
       159 
201 
     | 
    
         
             
            * `remote_url` – Remove `Attacher#remote_url=` and `Attacher#remote_url` (@janko)
         
     | 
| 
       160 
202 
     | 
    
         | 
| 
         @@ -262,6 +304,8 @@ 
     | 
|
| 
       262 
304 
     | 
    
         | 
| 
       263 
305 
     | 
    
         
             
            * `hooks` – Remove plugin (@janko)
         
     | 
| 
       264 
306 
     | 
    
         | 
| 
      
 307 
     | 
    
         
            +
            * `core` – Remove deprecated `Shrine::IO_METHODS` constant (@janko)
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
       265 
309 
     | 
    
         
             
            * `s3` – Replace source object metadata when copying a file from S3 (@janko)
         
     | 
| 
       266 
310 
     | 
    
         | 
| 
       267 
311 
     | 
    
         
             
            * `core` – Change `UploadedFile#storage_key` to return a Symbol instead of a String (@janko)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -5,8 +5,8 @@ Shrine is a toolkit for file attachments in Ruby applications. Some highlights: 
     | 
|
| 
       5 
5 
     | 
    
         
             
            * **Modular design** – the [plugin system] allows you to load only the functionality you need
         
     | 
| 
       6 
6 
     | 
    
         
             
            * **Memory friendly** – streaming uploads and [downloads][Retrieving Uploads] make it work great with large files
         
     | 
| 
       7 
7 
     | 
    
         
             
            * **Cloud storage** – store files on [disk][FileSystem], [AWS S3][S3], [Google Cloud][GCS], [Cloudinary] and [others][external]
         
     | 
| 
       8 
     | 
    
         
            -
            * ** 
     | 
| 
       9 
     | 
    
         
            -
            * **Flexible processing** – generate thumbnails [ 
     | 
| 
      
 8 
     | 
    
         
            +
            * **Persistence integrations** – works with [Sequel][sequel plugin], [ActiveRecord][activerecord plugin], [ROM][rom plugin], [Hanami::Model][hanami plugin] and [Mongoid][mongoid plugin]
         
     | 
| 
      
 9 
     | 
    
         
            +
            * **Flexible processing** – generate thumbnails [up front] or [on-the-fly] using [ImageMagick][ImageProcessing::MiniMagick] or [libvips][ImageProcessing::Vips]
         
     | 
| 
       10 
10 
     | 
    
         
             
            * **Metadata validation** – [validate files][validation] based on [extracted metadata][metadata]
         
     | 
| 
       11 
11 
     | 
    
         
             
            * **Direct uploads** – upload asynchronously [to your app][simple upload] or [to the cloud][presigned upload] using [Uppy]
         
     | 
| 
       12 
12 
     | 
    
         
             
            * **Resumable uploads** – make large file uploads [resumable][resumable upload] on [S3][uppy-s3_multipart] or [tus][tus-ruby-server]
         
     | 
| 
         @@ -16,14 +16,14 @@ If you're curious how it compares to other file attachment libraries, see the [A 
     | 
|
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
            ## Resources
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
            | Resource          | URL 
     | 
| 
       20 
     | 
    
         
            -
            | :---------------- |  
     | 
| 
       21 
     | 
    
         
            -
            | Website           | [shrinerb.com](https://shrinerb.com) 
     | 
| 
       22 
     | 
    
         
            -
            | Demo code         | [Roda][roda demo] / [Rails][rails demo] 
     | 
| 
       23 
     | 
    
         
            -
            | Source            | [github.com/shrinerb/shrine](https://github.com/shrinerb/shrine) 
     | 
| 
       24 
     | 
    
         
            -
            | Wiki              | [github.com/shrinerb/shrine/wiki](https://github.com/shrinerb/shrine/wiki) 
     | 
| 
       25 
     | 
    
         
            -
            | Bugs              | [github.com/shrinerb/shrine/issues](https://github.com/shrinerb/shrine/issues) 
     | 
| 
       26 
     | 
    
         
            -
            | Help & Discussion | [ 
     | 
| 
      
 19 
     | 
    
         
            +
            | Resource          | URL                                                                            |
         
     | 
| 
      
 20 
     | 
    
         
            +
            | :---------------- | :----------------------------------------------------------------------------- |
         
     | 
| 
      
 21 
     | 
    
         
            +
            | Website           | [shrinerb.com](https://shrinerb.com)                                           |
         
     | 
| 
      
 22 
     | 
    
         
            +
            | Demo code         | [Roda][roda demo] / [Rails][rails demo]                                        |
         
     | 
| 
      
 23 
     | 
    
         
            +
            | Source            | [github.com/shrinerb/shrine](https://github.com/shrinerb/shrine)               |
         
     | 
| 
      
 24 
     | 
    
         
            +
            | Wiki              | [github.com/shrinerb/shrine/wiki](https://github.com/shrinerb/shrine/wiki)     |
         
     | 
| 
      
 25 
     | 
    
         
            +
            | Bugs              | [github.com/shrinerb/shrine/issues](https://github.com/shrinerb/shrine/issues) |
         
     | 
| 
      
 26 
     | 
    
         
            +
            | Help & Discussion | [discourse.shrinerb.com](https://discourse.shrinerb.com)                       |
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
            ## Contents
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
         @@ -40,7 +40,7 @@ If you're curious how it compares to other file attachment libraries, see the [A 
     | 
|
| 
       40 
40 
     | 
    
         
             
              * [MIME type](#mime-type)
         
     | 
| 
       41 
41 
     | 
    
         
             
              * [Other metadata](#other-metadata)
         
     | 
| 
       42 
42 
     | 
    
         
             
            * [Processing](#processing)
         
     | 
| 
       43 
     | 
    
         
            -
              * [Processing  
     | 
| 
      
 43 
     | 
    
         
            +
              * [Processing up front](#processing-up-front)
         
     | 
| 
       44 
44 
     | 
    
         
             
              * [Processing on-the-fly](#processing-on-the-fly)
         
     | 
| 
       45 
45 
     | 
    
         
             
            * [Validation](#validation)
         
     | 
| 
       46 
46 
     | 
    
         
             
            * [Location](#location)
         
     | 
| 
         @@ -84,7 +84,7 @@ will use to store all information about the attachment: 
     | 
|
| 
       84 
84 
     | 
    
         
             
            ```rb
         
     | 
| 
       85 
85 
     | 
    
         
             
            Sequel.migration do
         
     | 
| 
       86 
86 
     | 
    
         
             
              change do
         
     | 
| 
       87 
     | 
    
         
            -
                add_column :photos, :image_data, :text
         
     | 
| 
      
 87 
     | 
    
         
            +
                add_column :photos, :image_data, :text # or :jsonb
         
     | 
| 
       88 
88 
     | 
    
         
             
              end
         
     | 
| 
       89 
89 
     | 
    
         
             
            end
         
     | 
| 
       90 
90 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -97,7 +97,7 @@ $ rails generate migration add_image_data_to_photos image_data:text 
     | 
|
| 
       97 
97 
     | 
    
         
             
            ```rb
         
     | 
| 
       98 
98 
     | 
    
         
             
            class AddImageDataToPhotos < ActiveRecord::Migration
         
     | 
| 
       99 
99 
     | 
    
         
             
              def change
         
     | 
| 
       100 
     | 
    
         
            -
                add_column :photos, :image_data, :text
         
     | 
| 
      
 100 
     | 
    
         
            +
                add_column :photos, :image_data, :text # or :jsonb
         
     | 
| 
       101 
101 
     | 
    
         
             
              end
         
     | 
| 
       102 
102 
     | 
    
         
             
            end
         
     | 
| 
       103 
103 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -115,14 +115,14 @@ end 
     | 
|
| 
       115 
115 
     | 
    
         | 
| 
       116 
116 
     | 
    
         
             
            ```rb
         
     | 
| 
       117 
117 
     | 
    
         
             
            class Photo < Sequel::Model # ActiveRecord::Base
         
     | 
| 
       118 
     | 
    
         
            -
              include ImageUploader::Attachment 
     | 
| 
      
 118 
     | 
    
         
            +
              include ImageUploader::Attachment(:image) # adds an `image` virtual attribute
         
     | 
| 
       119 
119 
     | 
    
         
             
            end
         
     | 
| 
       120 
120 
     | 
    
         
             
            ```
         
     | 
| 
       121 
121 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
            Let's now add the form fields which will use this virtual attribute ( 
     | 
| 
       123 
     | 
    
         
            -
            ` 
     | 
| 
       124 
     | 
    
         
            -
            (2) a hidden field for retaining the uploaded file in case of 
     | 
| 
       125 
     | 
    
         
            -
            and for potential [direct uploads].
         
     | 
| 
      
 122 
     | 
    
         
            +
            Let's now add the form fields which will use this virtual attribute (NOT the
         
     | 
| 
      
 123 
     | 
    
         
            +
            `<attachment>_data` column attribute). We need (1) a file field for choosing
         
     | 
| 
      
 124 
     | 
    
         
            +
            files, and (2) a hidden field for retaining the uploaded file in case of
         
     | 
| 
      
 125 
     | 
    
         
            +
            validation errors and for potential [direct uploads].
         
     | 
| 
       126 
126 
     | 
    
         | 
| 
       127 
127 
     | 
    
         
             
            ```rb
         
     | 
| 
       128 
128 
     | 
    
         
             
            # with Rails form builder:
         
     | 
| 
         @@ -251,7 +251,7 @@ organize them in any way you like. 
     | 
|
| 
       251 
251 
     | 
    
         | 
| 
       252 
252 
     | 
    
         
             
            ### Uploading
         
     | 
| 
       253 
253 
     | 
    
         | 
| 
       254 
     | 
    
         
            -
            The main method of the uploader is  
     | 
| 
      
 254 
     | 
    
         
            +
            The main method of the uploader is `Shrine.upload`, which takes an [IO-like
         
     | 
| 
       255 
255 
     | 
    
         
             
            object][io abstraction] and a storage identifier on the input, and returns a
         
     | 
| 
       256 
256 
     | 
    
         
             
            representation of the [uploaded file] on the output.
         
     | 
| 
       257 
257 
     | 
    
         | 
| 
         @@ -259,8 +259,8 @@ representation of the [uploaded file] on the output. 
     | 
|
| 
       259 
259 
     | 
    
         
             
            MyUploader.upload(file, :store) #=> #<Shrine::UploadedFile>
         
     | 
| 
       260 
260 
     | 
    
         
             
            ```
         
     | 
| 
       261 
261 
     | 
    
         | 
| 
       262 
     | 
    
         
            -
            Internally this instantiates the uploader with the storage and calls 
     | 
| 
       263 
     | 
    
         
            -
             
     | 
| 
      
 262 
     | 
    
         
            +
            Internally this instantiates the uploader with the storage and calls
         
     | 
| 
      
 263 
     | 
    
         
            +
            `Shrine#upload`:
         
     | 
| 
       264 
264 
     | 
    
         | 
| 
       265 
265 
     | 
    
         
             
            ```rb
         
     | 
| 
       266 
266 
     | 
    
         
             
            uploader = MyUploader.new(:store)
         
     | 
| 
         @@ -269,13 +269,12 @@ uploader.upload(file) #=> #<Shrine::UploadedFile> 
     | 
|
| 
       269 
269 
     | 
    
         | 
| 
       270 
270 
     | 
    
         
             
            Some of the tasks performed by `#upload` include:
         
     | 
| 
       271 
271 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
            * any defined [file processing][on upload]
         
     | 
| 
       273 
272 
     | 
    
         
             
            * extracting [metadata]
         
     | 
| 
       274 
273 
     | 
    
         
             
            * generating [location]
         
     | 
| 
       275 
274 
     | 
    
         
             
            * uploading (this is where the [storage] is called)
         
     | 
| 
       276 
275 
     | 
    
         
             
            * closing the uploaded file
         
     | 
| 
       277 
276 
     | 
    
         | 
| 
       278 
     | 
    
         
            -
            The second argument is a  
     | 
| 
      
 277 
     | 
    
         
            +
            The second argument is a "context" hash which is forwarded to places like
         
     | 
| 
       279 
278 
     | 
    
         
             
            metadata extraction and location generation, but it has a few special options:
         
     | 
| 
       280 
279 
     | 
    
         | 
| 
       281 
280 
     | 
    
         
             
            ```rb
         
     | 
| 
         @@ -434,10 +433,10 @@ photo.image_attacher #=> #<Shrine::Attacher> 
     | 
|
| 
       434 
433 
     | 
    
         
             
            The `Shrine::Attacher` object can be instantiated and used directly:
         
     | 
| 
       435 
434 
     | 
    
         | 
| 
       436 
435 
     | 
    
         
             
            ```rb
         
     | 
| 
       437 
     | 
    
         
            -
            attacher = ImageUploader::Attacher. 
     | 
| 
      
 436 
     | 
    
         
            +
            attacher = ImageUploader::Attacher.from_model(photo, :image)
         
     | 
| 
       438 
437 
     | 
    
         | 
| 
       439 
438 
     | 
    
         
             
            attacher.assign(file) # equivalent to `photo.image = file`
         
     | 
| 
       440 
     | 
    
         
            -
            attacher. 
     | 
| 
      
 439 
     | 
    
         
            +
            attacher.file         # equivalent to `photo.image`
         
     | 
| 
       441 
440 
     | 
    
         
             
            attacher.url          # equivalent to `photo.image_url`
         
     | 
| 
       442 
441 
     | 
    
         
             
            ```
         
     | 
| 
       443 
442 
     | 
    
         | 
| 
         @@ -521,79 +520,72 @@ the [Extracting Metadata] guide for more details. 
     | 
|
| 
       521 
520 
     | 
    
         | 
| 
       522 
521 
     | 
    
         
             
            ## Processing
         
     | 
| 
       523 
522 
     | 
    
         | 
| 
       524 
     | 
    
         
            -
            Shrine allows you to process attached files  
     | 
| 
       525 
     | 
    
         
            -
             
     | 
| 
       526 
     | 
    
         
            -
             
     | 
| 
       527 
     | 
    
         
            -
             
     | 
| 
       528 
     | 
    
         
            -
            they're needed ("on-the-fly").
         
     | 
| 
      
 523 
     | 
    
         
            +
            Shrine allows you to process attached files up front or on-the-fly. For
         
     | 
| 
      
 524 
     | 
    
         
            +
            example, if your app is accepting image uploads, you can generate a predefined
         
     | 
| 
      
 525 
     | 
    
         
            +
            set of of thumbnails when the image is attached to a record, or you can have
         
     | 
| 
      
 526 
     | 
    
         
            +
            thumbnails generated dynamically as they're needed.
         
     | 
| 
       529 
527 
     | 
    
         | 
| 
       530 
     | 
    
         
            -
            For image processing it's recommended to use the **[ImageProcessing]** gem,
         
     | 
| 
      
 528 
     | 
    
         
            +
            For image processing, it's recommended to use the **[ImageProcessing]** gem,
         
     | 
| 
       531 
529 
     | 
    
         
             
            which is a high-level wrapper for processing with [ImageMagick] (via
         
     | 
| 
       532 
530 
     | 
    
         
             
            [MiniMagick]) or [libvips] (via [ruby-vips]).
         
     | 
| 
       533 
531 
     | 
    
         | 
| 
       534 
     | 
    
         
            -
            ### Processing on upload
         
     | 
| 
       535 
     | 
    
         
            -
             
     | 
| 
       536 
     | 
    
         
            -
            For processing "on upload", you can intercept when a cached file is being
         
     | 
| 
       537 
     | 
    
         
            -
            uploaded to permanent storage, and perform any file processing you might want.
         
     | 
| 
       538 
     | 
    
         
            -
            The [`processing`][processing plugin] plugin provides the promotion hook, while
         
     | 
| 
       539 
     | 
    
         
            -
            the [`versions`][versions plugin] plugin enables handling a hash of versions.
         
     | 
| 
       540 
     | 
    
         
            -
             
     | 
| 
       541 
532 
     | 
    
         
             
            ```sh
         
     | 
| 
       542 
     | 
    
         
            -
            $ brew install imagemagick
         
     | 
| 
      
 533 
     | 
    
         
            +
            $ brew install imagemagick vips
         
     | 
| 
       543 
534 
     | 
    
         
             
            ```
         
     | 
| 
      
 535 
     | 
    
         
            +
             
     | 
| 
      
 536 
     | 
    
         
            +
            ### Processing up front
         
     | 
| 
      
 537 
     | 
    
         
            +
             
     | 
| 
      
 538 
     | 
    
         
            +
            You can use the [`derivatives`][derivatives plugin] plugin to generate a set of
         
     | 
| 
      
 539 
     | 
    
         
            +
            pre-defined processed files:
         
     | 
| 
      
 540 
     | 
    
         
            +
             
     | 
| 
       544 
541 
     | 
    
         
             
            ```rb
         
     | 
| 
       545 
542 
     | 
    
         
             
            # Gemfile
         
     | 
| 
       546 
     | 
    
         
            -
            gem "image_processing", "~> 1. 
     | 
| 
      
 543 
     | 
    
         
            +
            gem "image_processing", "~> 1.8"
         
     | 
| 
      
 544 
     | 
    
         
            +
            ```
         
     | 
| 
      
 545 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 546 
     | 
    
         
            +
            Shrine.plugin :derivatives
         
     | 
| 
       547 
547 
     | 
    
         
             
            ```
         
     | 
| 
       548 
548 
     | 
    
         
             
            ```rb
         
     | 
| 
       549 
549 
     | 
    
         
             
            require "image_processing/mini_magick"
         
     | 
| 
       550 
550 
     | 
    
         | 
| 
       551 
551 
     | 
    
         
             
            class ImageUploader < Shrine
         
     | 
| 
       552 
     | 
    
         
            -
               
     | 
| 
       553 
     | 
    
         
            -
             
     | 
| 
       554 
     | 
    
         
            -
             
     | 
| 
       555 
     | 
    
         
            -
             
     | 
| 
       556 
     | 
    
         
            -
               
     | 
| 
       557 
     | 
    
         
            -
             
     | 
| 
       558 
     | 
    
         
            -
             
     | 
| 
       559 
     | 
    
         
            -
                 
     | 
| 
       560 
     | 
    
         
            -
                  pipeline = ImageProcessing::MiniMagick.source(original)
         
     | 
| 
       561 
     | 
    
         
            -
             
     | 
| 
       562 
     | 
    
         
            -
                  versions[:large]  = pipeline.resize_to_limit!(800, 800)
         
     | 
| 
       563 
     | 
    
         
            -
                  versions[:medium] = pipeline.resize_to_limit!(500, 500)
         
     | 
| 
       564 
     | 
    
         
            -
                  versions[:small]  = pipeline.resize_to_limit!(300, 300)
         
     | 
| 
       565 
     | 
    
         
            -
                end
         
     | 
| 
       566 
     | 
    
         
            -
             
     | 
| 
       567 
     | 
    
         
            -
                versions # return the hash of processed files
         
     | 
| 
      
 552 
     | 
    
         
            +
              Attacher.derivatives_processor do |original|
         
     | 
| 
      
 553 
     | 
    
         
            +
                magick = ImageProcessing::MiniMagick.source(original)
         
     | 
| 
      
 554 
     | 
    
         
            +
             
     | 
| 
      
 555 
     | 
    
         
            +
                {
         
     | 
| 
      
 556 
     | 
    
         
            +
                  large:  magick.resize_to_limit!(800, 800),
         
     | 
| 
      
 557 
     | 
    
         
            +
                  medium: magick.resize_to_limit!(500, 500),
         
     | 
| 
      
 558 
     | 
    
         
            +
                  small:  magick.resize_to_limit!(300, 300),
         
     | 
| 
      
 559 
     | 
    
         
            +
                }
         
     | 
| 
       568 
560 
     | 
    
         
             
              end
         
     | 
| 
       569 
561 
     | 
    
         
             
            end
         
     | 
| 
       570 
562 
     | 
    
         
             
            ```
         
     | 
| 
      
 563 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 564 
     | 
    
         
            +
            photo = Photo.new(image: file)
         
     | 
| 
      
 565 
     | 
    
         
            +
            photo.image_derivatives! # calls derivatives processor and uploads results
         
     | 
| 
      
 566 
     | 
    
         
            +
            photo.save
         
     | 
| 
      
 567 
     | 
    
         
            +
            ```
         
     | 
| 
       571 
568 
     | 
    
         | 
| 
       572 
     | 
    
         
            -
             
     | 
| 
       573 
     | 
    
         
            -
             
     | 
| 
       574 
     | 
    
         
            -
            [`Shrine::UploadedFile`][uploaded file] objects.
         
     | 
| 
      
 569 
     | 
    
         
            +
            If you're allowing the attached file to be updated later on, in your update
         
     | 
| 
      
 570 
     | 
    
         
            +
            route make sure to create derivatives for new attachments:
         
     | 
| 
       575 
571 
     | 
    
         | 
| 
       576 
572 
     | 
    
         
             
            ```rb
         
     | 
| 
       577 
     | 
    
         
            -
            photo  
     | 
| 
       578 
     | 
    
         
            -
             
     | 
| 
       579 
     | 
    
         
            -
            # {
         
     | 
| 
       580 
     | 
    
         
            -
            #   :original => #<Shrine::UploadedFile @data={"id"=>"9sd84.jpg", ...}>,
         
     | 
| 
       581 
     | 
    
         
            -
            #   :large    => #<Shrine::UploadedFile @data={"id"=>"lg043.jpg", ...}>,
         
     | 
| 
       582 
     | 
    
         
            -
            #   :medium   => #<Shrine::UploadedFile @data={"id"=>"kd9fk.jpg", ...}>,
         
     | 
| 
       583 
     | 
    
         
            -
            #   :small    => #<Shrine::UploadedFile @data={"id"=>"932fl.jpg", ...}>,
         
     | 
| 
       584 
     | 
    
         
            -
            # }
         
     | 
| 
      
 573 
     | 
    
         
            +
            photo.image_derivatives! if photo.image_changed?
         
     | 
| 
      
 574 
     | 
    
         
            +
            ```
         
     | 
| 
       585 
575 
     | 
    
         | 
| 
       586 
     | 
    
         
            -
             
     | 
| 
       587 
     | 
    
         
            -
             
     | 
| 
       588 
     | 
    
         
            -
             
     | 
| 
       589 
     | 
    
         
            -
            photo.image[:medium].mime_type #=> "image/jpeg"
         
     | 
| 
      
 576 
     | 
    
         
            +
            After the processed files are uploaded, their data is saved into the
         
     | 
| 
      
 577 
     | 
    
         
            +
            `<attachment>_data` column. You can then retrieve the derivatives as
         
     | 
| 
      
 578 
     | 
    
         
            +
            [`Shrine::UploadedFile`][uploaded file] objects:
         
     | 
| 
       590 
579 
     | 
    
         | 
| 
       591 
     | 
    
         
            -
             
     | 
| 
      
 580 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 581 
     | 
    
         
            +
            photo.image(:large)            #=> #<Shrine::UploadedFile ...>
         
     | 
| 
      
 582 
     | 
    
         
            +
            photo.image(:large).url        #=> "/uploads/store/lg043.jpg"
         
     | 
| 
      
 583 
     | 
    
         
            +
            photo.image(:large).size       #=> 5825949
         
     | 
| 
      
 584 
     | 
    
         
            +
            photo.image(:large).mime_type  #=> "image/jpeg"
         
     | 
| 
       592 
585 
     | 
    
         
             
            ```
         
     | 
| 
       593 
586 
     | 
    
         | 
| 
       594 
     | 
    
         
            -
             
     | 
| 
       595 
     | 
    
         
            -
             
     | 
| 
       596 
     | 
    
         
            -
            processing you want, see the [File Processing] guide for more details.
         
     | 
| 
      
 587 
     | 
    
         
            +
            For more details, see the [`derivatives`][derivatives plugin] plugin
         
     | 
| 
      
 588 
     | 
    
         
            +
            documentation and the [File Processing] guide.
         
     | 
| 
       597 
589 
     | 
    
         | 
| 
       598 
590 
     | 
    
         
             
            ### Processing on-the-fly
         
     | 
| 
       599 
591 
     | 
    
         | 
| 
         @@ -606,12 +598,9 @@ To set it up, we mount the Rack app in our router on a chosen path prefix, 
     | 
|
| 
       606 
598 
     | 
    
         
             
            configure the plugin with a secret key and that path prefix, and define
         
     | 
| 
       607 
599 
     | 
    
         
             
            processing we want to perform:
         
     | 
| 
       608 
600 
     | 
    
         | 
| 
       609 
     | 
    
         
            -
            ```sh
         
     | 
| 
       610 
     | 
    
         
            -
            $ brew install imagemagick
         
     | 
| 
       611 
     | 
    
         
            -
            ```
         
     | 
| 
       612 
601 
     | 
    
         
             
            ```rb
         
     | 
| 
       613 
602 
     | 
    
         
             
            # Gemfile
         
     | 
| 
       614 
     | 
    
         
            -
            gem "image_processing", "~> 1. 
     | 
| 
      
 603 
     | 
    
         
            +
            gem "image_processing", "~> 1.8"
         
     | 
| 
       615 
604 
     | 
    
         
             
            ```
         
     | 
| 
       616 
605 
     | 
    
         
             
            ```rb
         
     | 
| 
       617 
606 
     | 
    
         
             
            # config/routes.rb (Rails)
         
     | 
| 
         @@ -650,17 +639,19 @@ more details. 
     | 
|
| 
       650 
639 
     | 
    
         | 
| 
       651 
640 
     | 
    
         
             
            ## Validation
         
     | 
| 
       652 
641 
     | 
    
         | 
| 
       653 
     | 
    
         
            -
             
     | 
| 
       654 
     | 
    
         
            -
             
     | 
| 
       655 
     | 
    
         
            -
             
     | 
| 
      
 642 
     | 
    
         
            +
            The [`validation`][validation plugin] plugin allows performing validation for
         
     | 
| 
      
 643 
     | 
    
         
            +
            attached files. For common validations, the
         
     | 
| 
      
 644 
     | 
    
         
            +
            [`validation_helpers`][validation_helpers plugin] plugin provides useful
         
     | 
| 
      
 645 
     | 
    
         
            +
            validators for built in metadata:
         
     | 
| 
       656 
646 
     | 
    
         | 
| 
      
 647 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 648 
     | 
    
         
            +
            Shrine.plugin :validation_helpers
         
     | 
| 
      
 649 
     | 
    
         
            +
            ```
         
     | 
| 
       657 
650 
     | 
    
         
             
            ```rb
         
     | 
| 
       658 
651 
     | 
    
         
             
            class DocumentUploader < Shrine
         
     | 
| 
       659 
     | 
    
         
            -
              plugin :validation_helpers
         
     | 
| 
       660 
     | 
    
         
            -
             
     | 
| 
       661 
652 
     | 
    
         
             
              Attacher.validate do
         
     | 
| 
       662 
653 
     | 
    
         
             
                validate_max_size 5*1024*1024, message: "is too large (max is 5 MB)"
         
     | 
| 
       663 
     | 
    
         
            -
                 
     | 
| 
      
 654 
     | 
    
         
            +
                validate_mime_type %w[application/pdf]
         
     | 
| 
       664 
655 
     | 
    
         
             
              end
         
     | 
| 
       665 
656 
     | 
    
         
             
            end
         
     | 
| 
       666 
657 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -685,9 +676,9 @@ plugin provides a good default hierarchy, but you can also override 
     | 
|
| 
       685 
676 
     | 
    
         | 
| 
       686 
677 
     | 
    
         
             
            ```rb
         
     | 
| 
       687 
678 
     | 
    
         
             
            class ImageUploader < Shrine
         
     | 
| 
       688 
     | 
    
         
            -
              def generate_location(io,  
     | 
| 
       689 
     | 
    
         
            -
                type  =  
     | 
| 
       690 
     | 
    
         
            -
                style =  
     | 
| 
      
 679 
     | 
    
         
            +
              def generate_location(io, record: nil, derivative: nil, **)
         
     | 
| 
      
 680 
     | 
    
         
            +
                type  = record.class.name.downcase if record
         
     | 
| 
      
 681 
     | 
    
         
            +
                style = derivative ? "thumbs" : "originals"
         
     | 
| 
       691 
682 
     | 
    
         
             
                name  = super # the default unique identifier
         
     | 
| 
       692 
683 
     | 
    
         | 
| 
       693 
684 
     | 
    
         
             
                [type, style, name].compact.join("/")
         
     | 
| 
         @@ -706,7 +697,7 @@ uploads/ 
     | 
|
| 
       706 
697 
     | 
    
         | 
| 
       707 
698 
     | 
    
         
             
            Note that there should always be a random component in the location, so that
         
     | 
| 
       708 
699 
     | 
    
         
             
            the ORM dirty tracking is detected properly. Inside `#generate_location` you
         
     | 
| 
       709 
     | 
    
         
            -
            can also access the extracted metadata through ` 
     | 
| 
      
 700 
     | 
    
         
            +
            can also access the extracted metadata through the `:metadata` option.
         
     | 
| 
       710 
701 
     | 
    
         | 
| 
       711 
702 
     | 
    
         
             
            ## Direct uploads
         
     | 
| 
       712 
703 
     | 
    
         | 
| 
         @@ -851,29 +842,30 @@ resumable uploads from scratch, it includes a complete JavaScript example 
     | 
|
| 
       851 
842 
     | 
    
         | 
| 
       852 
843 
     | 
    
         
             
            ## Backgrounding
         
     | 
| 
       853 
844 
     | 
    
         | 
| 
       854 
     | 
    
         
            -
             
     | 
| 
       855 
     | 
    
         
            -
             
     | 
| 
       856 
     | 
    
         
            -
             
     | 
| 
       857 
     | 
    
         
            -
            library][Backgrounding Libraries].
         
     | 
| 
      
 845 
     | 
    
         
            +
            The [`backgrounding`][backgrounding plugin] allows you to move file promotion
         
     | 
| 
      
 846 
     | 
    
         
            +
            and deletion into a background job, using the backgrounding library [of your
         
     | 
| 
      
 847 
     | 
    
         
            +
            choice][Backgrounding Libraries]:
         
     | 
| 
       858 
848 
     | 
    
         | 
| 
       859 
849 
     | 
    
         
             
            ```rb
         
     | 
| 
       860 
850 
     | 
    
         
             
            Shrine.plugin :backgrounding
         
     | 
| 
       861 
     | 
    
         
            -
            Shrine::Attacher. 
     | 
| 
       862 
     | 
    
         
            -
            Shrine::Attacher. 
     | 
| 
      
 851 
     | 
    
         
            +
            Shrine::Attacher.promote_block { PromoteJob.perform_later(self.class, record, name, file_data) }
         
     | 
| 
      
 852 
     | 
    
         
            +
            Shrine::Attacher.destroy_block { DestroyJob.perform_later(self.class, data) }
         
     | 
| 
       863 
853 
     | 
    
         
             
            ```
         
     | 
| 
       864 
854 
     | 
    
         
             
            ```rb
         
     | 
| 
       865 
     | 
    
         
            -
            class PromoteJob
         
     | 
| 
       866 
     | 
    
         
            -
               
     | 
| 
       867 
     | 
    
         
            -
             
     | 
| 
       868 
     | 
    
         
            -
                 
     | 
| 
      
 855 
     | 
    
         
            +
            class PromoteJob < ActiveJob::Base
         
     | 
| 
      
 856 
     | 
    
         
            +
              def perform(attacher_class, record, name, file_data)
         
     | 
| 
      
 857 
     | 
    
         
            +
                attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
         
     | 
| 
      
 858 
     | 
    
         
            +
                attacher.atomic_promote
         
     | 
| 
      
 859 
     | 
    
         
            +
              rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
         
     | 
| 
      
 860 
     | 
    
         
            +
                # attachment has changed or the record has been deleted, nothing to do
         
     | 
| 
       869 
861 
     | 
    
         
             
              end
         
     | 
| 
       870 
862 
     | 
    
         
             
            end
         
     | 
| 
       871 
863 
     | 
    
         
             
            ```
         
     | 
| 
       872 
864 
     | 
    
         
             
            ```rb
         
     | 
| 
       873 
     | 
    
         
            -
            class  
     | 
| 
       874 
     | 
    
         
            -
               
     | 
| 
       875 
     | 
    
         
            -
             
     | 
| 
       876 
     | 
    
         
            -
                 
     | 
| 
      
 865 
     | 
    
         
            +
            class DestroyJob < ActiveJob::Base
         
     | 
| 
      
 866 
     | 
    
         
            +
              def perform(attacher_class, data)
         
     | 
| 
      
 867 
     | 
    
         
            +
                attacher = attacher_class.from_data(data)
         
     | 
| 
      
 868 
     | 
    
         
            +
                attacher.destroy
         
     | 
| 
       877 
869 
     | 
    
         
             
              end
         
     | 
| 
       878 
870 
     | 
    
         
             
            end
         
     | 
| 
       879 
871 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -926,6 +918,7 @@ Some plugins add their own instrumentation as well when they detect that the 
     | 
|
| 
       926 
918 
     | 
    
         
             
            | Plugin                | Instrumentation                         |
         
     | 
| 
       927 
919 
     | 
    
         
             
            | :-----                | :--------------                         |
         
     | 
| 
       928 
920 
     | 
    
         
             
            | `derivation_endpoint` | instruments file processing             |
         
     | 
| 
      
 921 
     | 
    
         
            +
            | `derivatives`         | instruments file processing             |
         
     | 
| 
       929 
922 
     | 
    
         
             
            | `determine_mime_type` | instruments analyzing MIME type         |
         
     | 
| 
       930 
923 
     | 
    
         
             
            | `store_dimensions`    | instruments extracting image dimensions |
         
     | 
| 
       931 
924 
     | 
    
         
             
            | `signature`           | instruments calculating signature       |
         
     | 
| 
         @@ -993,7 +986,7 @@ The gem is available as open source under the terms of the [MIT License]. 
     | 
|
| 
       993 
986 
     | 
    
         
             
            [io abstraction]: #io-abstraction
         
     | 
| 
       994 
987 
     | 
    
         
             
            [location]: #location
         
     | 
| 
       995 
988 
     | 
    
         
             
            [metadata]: #metadata
         
     | 
| 
       996 
     | 
    
         
            -
            [ 
     | 
| 
      
 989 
     | 
    
         
            +
            [up front]: #processing-up-front
         
     | 
| 
       997 
990 
     | 
    
         
             
            [on-the-fly]: #processing-on-the-fly
         
     | 
| 
       998 
991 
     | 
    
         
             
            [plugin system]: #plugin-system
         
     | 
| 
       999 
992 
     | 
    
         
             
            [simple upload]: #simple-direct-upload
         
     | 
| 
         @@ -1045,20 +1038,21 @@ The gem is available as open source under the terms of the [MIT License]. 
     | 
|
| 
       1045 
1038 
     | 
    
         
             
            [add_metadata plugin]: /doc/plugins/add_metadata.md#readme
         
     | 
| 
       1046 
1039 
     | 
    
         
             
            [backgrounding plugin]: /doc/plugins/backgrounding.md#readme
         
     | 
| 
       1047 
1040 
     | 
    
         
             
            [derivation_endpoint plugin]: /doc/plugins/derivation_endpoint.md#readme
         
     | 
| 
      
 1041 
     | 
    
         
            +
            [derivatives plugin]: /doc/plugins/derivatives.md#readme
         
     | 
| 
       1048 
1042 
     | 
    
         
             
            [determine_mime_type plugin]: /doc/plugins/determine_mime_type.md#readme
         
     | 
| 
       1049 
1043 
     | 
    
         
             
            [instrumentation plugin]: /doc/plugins/instrumentation.md#readme
         
     | 
| 
       1050 
1044 
     | 
    
         
             
            [hanami plugin]: https://github.com/katafrakt/hanami-shrine
         
     | 
| 
       1051 
1045 
     | 
    
         
             
            [mongoid plugin]: https://github.com/shrinerb/shrine-mongoid
         
     | 
| 
       1052 
1046 
     | 
    
         
             
            [presign_endpoint plugin]: /doc/plugins/presign_endpoint.md#readme
         
     | 
| 
       1053 
1047 
     | 
    
         
             
            [pretty_location plugin]: /doc/plugins/pretty_location.md#readme
         
     | 
| 
       1054 
     | 
    
         
            -
            [processing plugin]: /doc/plugins/processing.md#readme
         
     | 
| 
       1055 
1048 
     | 
    
         
             
            [rack_file plugin]: /doc/plugins/rack_file.md#readme
         
     | 
| 
      
 1049 
     | 
    
         
            +
            [rom plugin]: https://github.com/shrinerb/shrine-rom
         
     | 
| 
       1056 
1050 
     | 
    
         
             
            [sequel plugin]: /doc/plugins/sequel.md#readme
         
     | 
| 
       1057 
1051 
     | 
    
         
             
            [signature plugin]: /doc/plugins/signature.md#readme
         
     | 
| 
       1058 
1052 
     | 
    
         
             
            [store_dimensions plugin]: /doc/plugins/store_dimensions.md#readme
         
     | 
| 
       1059 
1053 
     | 
    
         
             
            [upload_endpoint plugin]: /doc/plugins/upload_endpoint.md#readme
         
     | 
| 
       1060 
1054 
     | 
    
         
             
            [validation_helpers plugin]: /doc/plugins/validation_helpers.md#readme
         
     | 
| 
       1061 
     | 
    
         
            -
            [ 
     | 
| 
      
 1055 
     | 
    
         
            +
            [validation plugin]: /doc/plugins/validation.md#readme
         
     | 
| 
       1062 
1056 
     | 
    
         | 
| 
       1063 
1057 
     | 
    
         
             
            <!-- Demos -->
         
     | 
| 
       1064 
1058 
     | 
    
         
             
            [rails demo]: https://github.com/erikdahlstrand/shrine-rails-example
         
     |