shrine 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
 
    
        data/doc/direct_s3.md
    CHANGED
    
    | 
         @@ -1,13 +1,30 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Direct Uploads to S3
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            Shrine gives you the ability to upload files directly to S3, which  
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            Shrine gives you the ability to upload files directly to Amazon S3, which is
         
     | 
| 
      
 4 
     | 
    
         
            +
            beneficial for several use cases:
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            * accepting uploads is resource-intensive for the server, and delegating it to
         
     | 
| 
      
 7 
     | 
    
         
            +
              an external service makes scaling easier
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            * if both temporary and permanent storage are S3, promoting an S3 file to
         
     | 
| 
      
 10 
     | 
    
         
            +
              permanent storage will simply issue an S3 copy request, without any
         
     | 
| 
      
 11 
     | 
    
         
            +
              downloading and reuploading
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            * with multiple servers it's generally not possible to cache files to the disk,
         
     | 
| 
      
 14 
     | 
    
         
            +
              unless you're using a distibuted filesystem that's shared between servers
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            * Heroku restricts file uploads to disk, allowing you to save files only in
         
     | 
| 
      
 17 
     | 
    
         
            +
              the temporary folder, which gets wiped out between deploys
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            * Heroku has a 30-second request limit, so if the client has a slow connection
         
     | 
| 
      
 20 
     | 
    
         
            +
              and/or your files are larger, uploads to your app can easily hit that limit
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            You can start by setting both temporary and permanent storage to S3 with
         
     | 
| 
      
 23 
     | 
    
         
            +
            different prefixes (or even buckets):
         
     | 
| 
       10 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 26 
     | 
    
         
            +
            gem "aws-sdk", "~> 2.1"
         
     | 
| 
      
 27 
     | 
    
         
            +
            ```
         
     | 
| 
       11 
28 
     | 
    
         
             
            ```rb
         
     | 
| 
       12 
29 
     | 
    
         
             
            require "shrine/storage/s3"
         
     | 
| 
       13 
30 
     | 
    
         | 
| 
         @@ -21,9 +38,10 @@ Shrine.storages = { 
     | 
|
| 
       21 
38 
     | 
    
         | 
| 
       22 
39 
     | 
    
         
             
            ## Enabling CORS
         
     | 
| 
       23 
40 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
            that by clicking on "Properties > 
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
            In order to be able upload files directly to your S3 bucket, you need enable
         
     | 
| 
      
 42 
     | 
    
         
            +
            CORS. You can do that in the AWS S3 Console by clicking on "Properties >
         
     | 
| 
      
 43 
     | 
    
         
            +
            Permissions > Add CORS Configuration", and then just follow the Amazon
         
     | 
| 
      
 44 
     | 
    
         
            +
            documentation on how to write a CORS file.
         
     | 
| 
       27 
45 
     | 
    
         | 
| 
       28 
46 
     | 
    
         
             
            http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html
         
     | 
| 
       29 
47 
     | 
    
         | 
| 
         @@ -32,7 +50,8 @@ DNS propagation. 
     | 
|
| 
       32 
50 
     | 
    
         | 
| 
       33 
51 
     | 
    
         
             
            ## File hash
         
     | 
| 
       34 
52 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
      
 53 
     | 
    
         
            +
            After direct S3 uploads we'll need to manually construct Shrine's
         
     | 
| 
      
 54 
     | 
    
         
            +
            representation of an uploaded file:
         
     | 
| 
       36 
55 
     | 
    
         | 
| 
       37 
56 
     | 
    
         
             
            ```rb
         
     | 
| 
       38 
57 
     | 
    
         
             
            {
         
     | 
| 
         @@ -46,10 +65,9 @@ Shrine's JSON representation of an uploaded file looks like this: 
     | 
|
| 
       46 
65 
     | 
    
         
             
            }
         
     | 
| 
       47 
66 
     | 
    
         
             
            ```
         
     | 
| 
       48 
67 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
            JSON, and then you can assign it to the hidden attachment field in the form.
         
     | 
| 
      
 68 
     | 
    
         
            +
            * `id` – location of the file on S3 (minus the `:prefix`)
         
     | 
| 
      
 69 
     | 
    
         
            +
            * `storage` – direct uploads typically use the `:cache` storage
         
     | 
| 
      
 70 
     | 
    
         
            +
            * `metadata` – hash of metadata extracted from the file
         
     | 
| 
       53 
71 
     | 
    
         | 
| 
       54 
72 
     | 
    
         
             
            ## Strategy A (dynamic)
         
     | 
| 
       55 
73 
     | 
    
         | 
| 
         @@ -57,17 +75,20 @@ JSON, and then you can assign it to the hidden attachment field in the form. 
     | 
|
| 
       57 
75 
     | 
    
         
             
            * Single or multiple file uploads
         
     | 
| 
       58 
76 
     | 
    
         
             
            * Some JavaScript needed
         
     | 
| 
       59 
77 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
            When the user selects the file, we dynamically  
     | 
| 
      
 78 
     | 
    
         
            +
            When the user selects the file, we dynamically fetch the presign from the
         
     | 
| 
       61 
79 
     | 
    
         
             
            server, and use this information to start uploading the file to S3. The
         
     | 
| 
       62 
     | 
    
         
            -
            direct_upload plugin gives us this presign route, so we just need to mount it
         
     | 
| 
      
 80 
     | 
    
         
            +
            `direct_upload` plugin gives us this presign route, so we just need to mount it
         
     | 
| 
       63 
81 
     | 
    
         
             
            in our application:
         
     | 
| 
       64 
82 
     | 
    
         | 
| 
      
 83 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 84 
     | 
    
         
            +
            gem "roda"
         
     | 
| 
      
 85 
     | 
    
         
            +
            ```
         
     | 
| 
       65 
86 
     | 
    
         
             
            ```rb
         
     | 
| 
       66 
87 
     | 
    
         
             
            plugin :direct_upload
         
     | 
| 
       67 
88 
     | 
    
         
             
            ```
         
     | 
| 
       68 
89 
     | 
    
         
             
            ```rb
         
     | 
| 
       69 
90 
     | 
    
         
             
            Rails.application.routes.draw do
         
     | 
| 
       70 
     | 
    
         
            -
              mount ImageUploader::UploadEndpoint => "/ 
     | 
| 
      
 91 
     | 
    
         
            +
              mount ImageUploader::UploadEndpoint => "/images"
         
     | 
| 
       71 
92 
     | 
    
         
             
            end
         
     | 
| 
       72 
93 
     | 
    
         
             
            ```
         
     | 
| 
       73 
94 
     | 
    
         | 
| 
         @@ -91,27 +112,24 @@ necessary request parameters: 
     | 
|
| 
       91 
112 
     | 
    
         
             
            ```
         
     | 
| 
       92 
113 
     | 
    
         | 
| 
       93 
114 
     | 
    
         
             
            For uploading to S3 you'll probably want to use a JavaScript file upload
         
     | 
| 
       94 
     | 
    
         
            -
            library like [jQuery-File-Upload] or [ 
     | 
| 
       95 
     | 
    
         
            -
            create a JSON representation of the uploaded file, which you 
     | 
| 
       96 
     | 
    
         
            -
            the hidden attachment field:
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
            ``` 
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
              id:  
     | 
| 
       101 
     | 
    
         
            -
              storage:  
     | 
| 
       102 
     | 
    
         
            -
              metadata: {
         
     | 
| 
       103 
     | 
    
         
            -
                size: 
     | 
| 
       104 
     | 
    
         
            -
                filename: 
     | 
| 
       105 
     | 
    
         
            -
                mime_type:  
     | 
| 
      
 115 
     | 
    
         
            +
            library like [jQuery-File-Upload], [Dropzone] or [FineUploader]. After the
         
     | 
| 
      
 116 
     | 
    
         
            +
            upload you should create a JSON representation of the uploaded file, which you
         
     | 
| 
      
 117 
     | 
    
         
            +
            can write to the hidden attachment field:
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
            ```html
         
     | 
| 
      
 120 
     | 
    
         
            +
            <input type='hidden' name='photo[image]' value='{
         
     | 
| 
      
 121 
     | 
    
         
            +
              "id": "302858ldg9agjad7f3ls.jpg",
         
     | 
| 
      
 122 
     | 
    
         
            +
              "storage": "cache",
         
     | 
| 
      
 123 
     | 
    
         
            +
              "metadata": {
         
     | 
| 
      
 124 
     | 
    
         
            +
                "size": 943483,
         
     | 
| 
      
 125 
     | 
    
         
            +
                "filename": "nature.jpg",
         
     | 
| 
      
 126 
     | 
    
         
            +
                "mime_type": "image/jpeg",
         
     | 
| 
       106 
127 
     | 
    
         
             
              }
         
     | 
| 
       107 
     | 
    
         
            -
            }
         
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
            $('input[type=file]').prev().val(JSON.stringify(image))
         
     | 
| 
      
 128 
     | 
    
         
            +
            }'>
         
     | 
| 
       110 
129 
     | 
    
         
             
            ```
         
     | 
| 
       111 
130 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
            working implementation of multiple direct S3 uploads.
         
     | 
| 
      
 131 
     | 
    
         
            +
            See the [demo app] for an example JavaScript implementation of multiple direct
         
     | 
| 
      
 132 
     | 
    
         
            +
            S3 uploads.
         
     | 
| 
       115 
133 
     | 
    
         | 
| 
       116 
134 
     | 
    
         
             
            ## Strategy B (static)
         
     | 
| 
       117 
135 
     | 
    
         | 
| 
         @@ -163,7 +181,7 @@ caching the file doesn't touch your application. When the cached file is stored, 
     | 
|
| 
       163 
181 
     | 
    
         
             
            Shrine's default behaviour is to simply copy over cached file's metadata.
         
     | 
| 
       164 
182 
     | 
    
         | 
| 
       165 
183 
     | 
    
         
             
            If you want to extract metadata on the server before storing, you can just
         
     | 
| 
       166 
     | 
    
         
            -
            load the restore_cached_data plugin.
         
     | 
| 
      
 184 
     | 
    
         
            +
            load the `restore_cached_data` plugin.
         
     | 
| 
       167 
185 
     | 
    
         | 
| 
       168 
186 
     | 
    
         
             
            ```rb
         
     | 
| 
       169 
187 
     | 
    
         
             
            plugin :restore_cached_data
         
     | 
| 
         @@ -201,12 +219,12 @@ backgrounding library to perform the job with a delay: 
     | 
|
| 
       201 
219 
     | 
    
         
             
            Shrine.plugin :backgrounding
         
     | 
| 
       202 
220 
     | 
    
         | 
| 
       203 
221 
     | 
    
         
             
            Shrine::Attacher.promote do |data|
         
     | 
| 
       204 
     | 
    
         
            -
              PromoteJob.perform_in( 
     | 
| 
      
 222 
     | 
    
         
            +
              PromoteJob.perform_in(3, data) # tells a Sidekiq worker to perform in 3 seconds
         
     | 
| 
       205 
223 
     | 
    
         
             
            end
         
     | 
| 
       206 
224 
     | 
    
         
             
            ```
         
     | 
| 
       207 
225 
     | 
    
         | 
| 
       208 
226 
     | 
    
         
             
            [`Aws::S3::PresignedPost`]: http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Bucket.html#presigned_post-instance_method
         
     | 
| 
       209 
     | 
    
         
            -
            [ 
     | 
| 
      
 227 
     | 
    
         
            +
            [demo app]: https://github.com/janko-m/shrine/tree/master/demo
         
     | 
| 
       210 
228 
     | 
    
         
             
            [Dropzone]: https://github.com/enyo/dropzone
         
     | 
| 
       211 
229 
     | 
    
         
             
            [jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
         
     | 
| 
       212 
230 
     | 
    
         
             
            [Amazon S3 Data Consistency Model]: http://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyMode
         
     | 
    
        data/doc/paperclip.md
    CHANGED
    
    | 
         @@ -1,158 +1,251 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Shrine for Paperclip Users
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            This guide is aimed at helping Paperclip users transition to Shrine 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            complete reference of Paperclip's interface and what is the equivalent in
         
     | 
| 
       6 
     | 
    
         
            -
            Shrine.
         
     | 
| 
      
 3 
     | 
    
         
            +
            This guide is aimed at helping Paperclip users transition to Shrine, and it
         
     | 
| 
      
 4 
     | 
    
         
            +
            consists of three parts:
         
     | 
| 
       7 
5 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
            your models, in Shrine you instead have "uploader" classes where you put all
         
     | 
| 
       12 
     | 
    
         
            -
            your uploading logic.
         
     | 
| 
      
 6 
     | 
    
         
            +
            1. Explanation of the key differences in design between Paperclip and Shrine
         
     | 
| 
      
 7 
     | 
    
         
            +
            2. Instructions how to migrate and existing app that uses Paperclip to Shrine
         
     | 
| 
      
 8 
     | 
    
         
            +
            3. Extensive reference of Paperclip's interface with Shrine equivalents
         
     | 
| 
       13 
9 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
            class ImageUploader < Shrine
         
     | 
| 
       16 
     | 
    
         
            -
              plugin :validation_helpers
         
     | 
| 
      
 10 
     | 
    
         
            +
            ## Storages
         
     | 
| 
       17 
11 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            While in Paperclip you configure storage in the model, a Shrine storage is just
         
     | 
| 
      
 13 
     | 
    
         
            +
            a class which you configure individually:
         
     | 
| 
       21 
14 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
               
     | 
| 
      
 15 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 16 
     | 
    
         
            +
            class Photo < ActiveRecord::Base
         
     | 
| 
      
 17 
     | 
    
         
            +
              has_attached_file :image,
         
     | 
| 
      
 18 
     | 
    
         
            +
                storage: :s3,
         
     | 
| 
      
 19 
     | 
    
         
            +
                s3_credentials: {
         
     | 
| 
      
 20 
     | 
    
         
            +
                  bucket:            "my-bucket",
         
     | 
| 
      
 21 
     | 
    
         
            +
                  access_key_id:     "abc",
         
     | 
| 
      
 22 
     | 
    
         
            +
                  secret_access_key: "xyz",
         
     | 
| 
      
 23 
     | 
    
         
            +
                },
         
     | 
| 
      
 24 
     | 
    
         
            +
                s3_host_alias: "http://abc123.cloudfront.net",
         
     | 
| 
       25 
25 
     | 
    
         
             
            end
         
     | 
| 
       26 
26 
     | 
    
         
             
            ```
         
     | 
| 
      
 27 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 28 
     | 
    
         
            +
            Shrine.storages[:store] = Shrine::Storage::S3.new(
         
     | 
| 
      
 29 
     | 
    
         
            +
              bucket:            "my-bucket",
         
     | 
| 
      
 30 
     | 
    
         
            +
              access_key_id:     "abc",
         
     | 
| 
      
 31 
     | 
    
         
            +
              secret_access_key: "xyz",
         
     | 
| 
      
 32 
     | 
    
         
            +
              host:              "http://abc123.cloudfront.net",
         
     | 
| 
      
 33 
     | 
    
         
            +
            )
         
     | 
| 
      
 34 
     | 
    
         
            +
            ```
         
     | 
| 
       27 
35 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
            Paperclip doesn't have a concept of "temporary" storage, so it cannot retain
         
     | 
| 
      
 37 
     | 
    
         
            +
            uploaded files in case of validation errors, and [direct S3 uploads] cannot be
         
     | 
| 
      
 38 
     | 
    
         
            +
            implemented in a safe way. Shrine conceptually separates a "temporary" and
         
     | 
| 
      
 39 
     | 
    
         
            +
            "permanent" storage:
         
     | 
| 
       31 
40 
     | 
    
         | 
| 
       32 
41 
     | 
    
         
             
            ```rb
         
     | 
| 
       33 
     | 
    
         
            -
            require "shrine/storage/file_system"
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
42 
     | 
    
         
             
            Shrine.storages = {
         
     | 
| 
       36 
43 
     | 
    
         
             
              cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
         
     | 
| 
       37 
     | 
    
         
            -
              store: Shrine::Storage:: 
     | 
| 
      
 44 
     | 
    
         
            +
              store: Shrine::Storage::S3.new(bucket: "my-bucket", **s3_options),
         
     | 
| 
       38 
45 
     | 
    
         
             
            }
         
     | 
| 
       39 
46 
     | 
    
         
             
            ```
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            ## Uploaders
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            While in Paperclip you define all your uploading logic inside your models,
         
     | 
| 
      
 51 
     | 
    
         
            +
            Shrine takes a more object-oriented approach and lets you define uploading logic
         
     | 
| 
      
 52 
     | 
    
         
            +
            inside "uploader" classes:
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 55 
     | 
    
         
            +
            class Photo < ActiveRecord::Base
         
     | 
| 
      
 56 
     | 
    
         
            +
              has_attached_file :image
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
| 
      
 58 
     | 
    
         
            +
            ```
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 61 
     | 
    
         
            +
            class ImageUploader < Shrine
         
     | 
| 
      
 62 
     | 
    
         
            +
              # ...
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            class Photo < ActiveRecord::Base
         
     | 
| 
      
 66 
     | 
    
         
            +
              include ImageUploader[:image]
         
     | 
| 
      
 67 
     | 
    
         
            +
            end
         
     | 
| 
      
 68 
     | 
    
         
            +
            ```
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            Among other things, this allows you to use uploader classes standalone, which
         
     | 
| 
      
 71 
     | 
    
         
            +
            gives you more power:
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
       40 
73 
     | 
    
         
             
            ```rb
         
     | 
| 
       41 
     | 
    
         
            -
            uploader =  
     | 
| 
      
 74 
     | 
    
         
            +
            uploader = ImageUploader.new(:store)
         
     | 
| 
       42 
75 
     | 
    
         
             
            uploaded_file = uploader.upload(File.open("nature.jpg"))
         
     | 
| 
       43 
     | 
    
         
            -
            uploaded_file 
     | 
| 
       44 
     | 
    
         
            -
            uploaded_file. 
     | 
| 
      
 76 
     | 
    
         
            +
            uploaded_file     #=> #<Shrine::UploadedFile>
         
     | 
| 
      
 77 
     | 
    
         
            +
            uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
         
     | 
| 
       45 
78 
     | 
    
         
             
            ```
         
     | 
| 
       46 
79 
     | 
    
         | 
| 
       47 
80 
     | 
    
         
             
            ### Processing
         
     | 
| 
       48 
81 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
            instance-level 
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
      
 82 
     | 
    
         
            +
            In contrast to Paperclip's static options, in Shrine you define and perform
         
     | 
| 
      
 83 
     | 
    
         
            +
            processing on instance-level. The result of processing can be a single file
         
     | 
| 
      
 84 
     | 
    
         
            +
            or a hash of versions:
         
     | 
| 
       52 
85 
     | 
    
         | 
| 
       53 
86 
     | 
    
         
             
            ```rb
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 87 
     | 
    
         
            +
            class Photo < ActiveRecord::Base
         
     | 
| 
      
 88 
     | 
    
         
            +
              has_attached_file :image,
         
     | 
| 
      
 89 
     | 
    
         
            +
                styles: {
         
     | 
| 
      
 90 
     | 
    
         
            +
                  large:  "800x800>",
         
     | 
| 
      
 91 
     | 
    
         
            +
                  medium: "500x500>",
         
     | 
| 
      
 92 
     | 
    
         
            +
                  small:  "300x300>",
         
     | 
| 
      
 93 
     | 
    
         
            +
                }
         
     | 
| 
      
 94 
     | 
    
         
            +
            end
         
     | 
| 
      
 95 
     | 
    
         
            +
            ```
         
     | 
| 
       55 
96 
     | 
    
         | 
| 
      
 97 
     | 
    
         
            +
            ```rb
         
     | 
| 
       56 
98 
     | 
    
         
             
            class ImageUploader < Shrine
         
     | 
| 
       57 
99 
     | 
    
         
             
              include ImageProcessing::MiniMagick
         
     | 
| 
       58 
100 
     | 
    
         
             
              plugin :processing
         
     | 
| 
       59 
101 
     | 
    
         
             
              plugin :versions
         
     | 
| 
       60 
102 
     | 
    
         | 
| 
       61 
103 
     | 
    
         
             
              process(:store) do |io, context|
         
     | 
| 
       62 
     | 
    
         
            -
                 
     | 
| 
       63 
     | 
    
         
            -
                 
     | 
| 
      
 104 
     | 
    
         
            +
                size_800 = resize_to_limit(io.download, 800, 800)
         
     | 
| 
      
 105 
     | 
    
         
            +
                size_500 = resize_to_limit(size_800,    500, 500)
         
     | 
| 
      
 106 
     | 
    
         
            +
                size_300 = resize_to_limit(size_500,    300, 300)
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                {large: size_800, medium: size_500, small: size_300}
         
     | 
| 
       64 
109 
     | 
    
         
             
              end
         
     | 
| 
       65 
110 
     | 
    
         
             
            end
         
     | 
| 
       66 
111 
     | 
    
         
             
            ```
         
     | 
| 
       67 
112 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
      
 113 
     | 
    
         
            +
            This allows you to fully optimize processing, because you can easily specify
         
     | 
| 
      
 114 
     | 
    
         
            +
            which files are processed from which, and even add parallelization.
         
     | 
| 
       69 
115 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
            very individual and depends on what versions you want regenerated, what ORM are
         
     | 
| 
       72 
     | 
    
         
            -
            you using, how many records there are in your database etc. The [Regenerating
         
     | 
| 
       73 
     | 
    
         
            -
            versions] guide provides some useful tips on this task.
         
     | 
| 
      
 116 
     | 
    
         
            +
            #### Reprocessing versions
         
     | 
| 
       74 
117 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
      
 118 
     | 
    
         
            +
            Shrine doesn't have a built-in way of regenerating versions, because that has
         
     | 
| 
      
 119 
     | 
    
         
            +
            to be written and optimized differently depending on whether you're adding or
         
     | 
| 
      
 120 
     | 
    
         
            +
            removing a version, what ORM are you using, how many records there are in the
         
     | 
| 
      
 121 
     | 
    
         
            +
            database etc. The [Reprocessing versions] guide provides some useful tips on
         
     | 
| 
      
 122 
     | 
    
         
            +
            this task.
         
     | 
| 
       76 
123 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
      
 124 
     | 
    
         
            +
            ### Validations
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
            Validations are also defined inside the uploader on the instance-level, which
         
     | 
| 
      
 127 
     | 
    
         
            +
            allows you to do conditional validations:
         
     | 
| 
       79 
128 
     | 
    
         | 
| 
       80 
129 
     | 
    
         
             
            ```rb
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
      
 130 
     | 
    
         
            +
            class Photo < ActiveRecord::Base
         
     | 
| 
      
 131 
     | 
    
         
            +
              has_attached_file :image
         
     | 
| 
      
 132 
     | 
    
         
            +
              validates_attachment :image,
         
     | 
| 
      
 133 
     | 
    
         
            +
                content_type: {content_type: %w[image/jpeg image/png image/gif]},
         
     | 
| 
      
 134 
     | 
    
         
            +
                size: {in: 0..10.megabytes}
         
     | 
| 
      
 135 
     | 
    
         
            +
            end
         
     | 
| 
       82 
136 
     | 
    
         
             
            ```
         
     | 
| 
       83 
137 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
      
 138 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 139 
     | 
    
         
            +
            class ImageUploader < Shrine
         
     | 
| 
      
 140 
     | 
    
         
            +
              plugin :validation_helpers
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
              Attacher.validate do
         
     | 
| 
      
 143 
     | 
    
         
            +
                validate_mime_type_inclusion %w[image/jpeg image/gif image/png]
         
     | 
| 
      
 144 
     | 
    
         
            +
                validate_max_size 10*1024*1024 unless record.admin?
         
     | 
| 
      
 145 
     | 
    
         
            +
              end
         
     | 
| 
      
 146 
     | 
    
         
            +
            end
         
     | 
| 
      
 147 
     | 
    
         
            +
            ```
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
            #### MIME type spoofing
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
            Paperclip detects MIME type spoofing, in the way that it extracts the MIME type
         
     | 
| 
      
 152 
     | 
    
         
            +
            from file contents using the `file` command and MimeMagic, compares it to the
         
     | 
| 
      
 153 
     | 
    
         
            +
            value that the `mime-types` gem determined from file extension, and raises a
         
     | 
| 
      
 154 
     | 
    
         
            +
            validation error if these two values mismatch.
         
     | 
| 
       85 
155 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
      
 156 
     | 
    
         
            +
            However, this turned out to be very problematic, leading to a lot of valid
         
     | 
| 
      
 157 
     | 
    
         
            +
            files being classified as "spoofed", because of the differences of MIME
         
     | 
| 
      
 158 
     | 
    
         
            +
            type databases between the `mime-types` gem, `file` command, and MimeMagic.
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
            Shrine takes a different approach here. By default it will extract MIME
         
     | 
| 
      
 161 
     | 
    
         
            +
            type from file extension, but it has a plugin for determining MIME type from
         
     | 
| 
      
 162 
     | 
    
         
            +
            file contents, which by default uses the `file` command:
         
     | 
| 
       89 
163 
     | 
    
         | 
| 
       90 
164 
     | 
    
         
             
            ```rb
         
     | 
| 
       91 
     | 
    
         
            -
            Shrine.plugin : 
     | 
| 
       92 
     | 
    
         
            -
            Shrine.plugin :activerecord # If you're using ActiveRecord
         
     | 
| 
      
 165 
     | 
    
         
            +
            Shrine.plugin :determine_mime_type
         
     | 
| 
       93 
166 
     | 
    
         
             
            ```
         
     | 
| 
       94 
167 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 168 
     | 
    
         
            +
            However, it doesn't try to compare this value with the one from file extension,
         
     | 
| 
      
 169 
     | 
    
         
            +
            it just means that now this value will be used for your MIME type validations.
         
     | 
| 
      
 170 
     | 
    
         
            +
            With this approach you can still prevent malicious files from being attached,
         
     | 
| 
      
 171 
     | 
    
         
            +
            but without the possibility of false negatives.
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            ### Logging
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
            In Paperclip you enable logging by setting `Paperclip.options[:log] = true`,
         
     | 
| 
      
 176 
     | 
    
         
            +
            however, this only logs ImageMagick commands. Shrine has full logging support,
         
     | 
| 
      
 177 
     | 
    
         
            +
            which measures processing, uploading and deleting individually, along with
         
     | 
| 
      
 178 
     | 
    
         
            +
            context for debugging:
         
     | 
| 
       97 
179 
     | 
    
         | 
| 
       98 
180 
     | 
    
         
             
            ```rb
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
      
 181 
     | 
    
         
            +
            Shrine.plugin :logging
         
     | 
| 
      
 182 
     | 
    
         
            +
            ```
         
     | 
| 
      
 183 
     | 
    
         
            +
            ```
         
     | 
| 
      
 184 
     | 
    
         
            +
            2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s)
         
     | 
| 
      
 185 
     | 
    
         
            +
            2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s)
         
     | 
| 
      
 186 
     | 
    
         
            +
            2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)
         
     | 
| 
       102 
187 
     | 
    
         
             
            ```
         
     | 
| 
       103 
188 
     | 
    
         | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
            Shrine you only need to have an `<attachment>_data` text column, and all
         
     | 
| 
       106 
     | 
    
         
            -
            information will be stored there (in the above case `avatar_data`).
         
     | 
| 
      
 189 
     | 
    
         
            +
            ## Attachments
         
     | 
| 
       107 
190 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
            backgrounding you can show the users the cached version before the file is
         
     | 
| 
       112 
     | 
    
         
            -
            finished storing.
         
     | 
| 
      
 191 
     | 
    
         
            +
            While Paperclip is designed to only integrate with ActiveRecord, Shrine is
         
     | 
| 
      
 192 
     | 
    
         
            +
            designed to be completely generic and integrate with any ORM. It ships with
         
     | 
| 
      
 193 
     | 
    
         
            +
            plugins for ActiveRecord and Sequel:
         
     | 
| 
       113 
194 
     | 
    
         | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
      
 195 
     | 
    
         
            +
            ```rb
         
     | 
| 
      
 196 
     | 
    
         
            +
            Shrine.plugin :activerecord # if you're using ActiveRecord
         
     | 
| 
      
 197 
     | 
    
         
            +
            Shrine.plugin :sequel       # if you're using Sequel
         
     | 
| 
      
 198 
     | 
    
         
            +
            ```
         
     | 
| 
       115 
199 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
      
 200 
     | 
    
         
            +
            Instead of giving you class methods for defining attachments, in Shrine you
         
     | 
| 
      
 201 
     | 
    
         
            +
            generate attachment modules which you simply include in your models, which
         
     | 
| 
      
 202 
     | 
    
         
            +
            gives your models similar set of methods that Paperclip gives:
         
     | 
| 
       118 
203 
     | 
    
         | 
| 
       119 
204 
     | 
    
         
             
            ```rb
         
     | 
| 
       120 
     | 
    
         
            -
            class  
     | 
| 
       121 
     | 
    
         
            -
               
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
              Attacher.validate do
         
     | 
| 
       124 
     | 
    
         
            -
                validate_max_size 5*1024*1024
         
     | 
| 
       125 
     | 
    
         
            -
                validate_mime_type_inclusion [/^image/]
         
     | 
| 
       126 
     | 
    
         
            -
              end
         
     | 
| 
      
 205 
     | 
    
         
            +
            class Photo < Sequel::Model
         
     | 
| 
      
 206 
     | 
    
         
            +
              include ImageUploader[:image]
         
     | 
| 
       127 
207 
     | 
    
         
             
            end
         
     | 
| 
       128 
208 
     | 
    
         
             
            ```
         
     | 
| 
       129 
209 
     | 
    
         | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
      
 210 
     | 
    
         
            +
            ### Attachment column
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
            Unlike in Paperclip which requires you to have 4 `<attachment>_*` columns, in
         
     | 
| 
      
 213 
     | 
    
         
            +
            Shrine you only need to have a single `<attachment>_data` text column (in the
         
     | 
| 
      
 214 
     | 
    
         
            +
            above case `image_data`), and all information will be stored there.
         
     | 
| 
       131 
215 
     | 
    
         | 
| 
       132 
216 
     | 
    
         
             
            ```rb
         
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
      
 217 
     | 
    
         
            +
            photo.image_data #=>
         
     | 
| 
      
 218 
     | 
    
         
            +
            # {
         
     | 
| 
      
 219 
     | 
    
         
            +
            #   "storage" => "store",
         
     | 
| 
      
 220 
     | 
    
         
            +
            #   "id" => "photo/1/image/0d9o8dk42.png",
         
     | 
| 
      
 221 
     | 
    
         
            +
            #   "metadata" => {
         
     | 
| 
      
 222 
     | 
    
         
            +
            #     "filename"  => "nature.png",
         
     | 
| 
      
 223 
     | 
    
         
            +
            #     "size"      => 49349138,
         
     | 
| 
      
 224 
     | 
    
         
            +
            #     "mime_type" => "image/png"
         
     | 
| 
      
 225 
     | 
    
         
            +
            #   }
         
     | 
| 
      
 226 
     | 
    
         
            +
            # }
         
     | 
| 
       135 
227 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
            end
         
     | 
| 
      
 228 
     | 
    
         
            +
            photo.image.original_filename #=> "nature.png"
         
     | 
| 
      
 229 
     | 
    
         
            +
            photo.image.size              #=> 49349138
         
     | 
| 
      
 230 
     | 
    
         
            +
            photo.image.mime_type         #=> "image/png"
         
     | 
| 
       140 
231 
     | 
    
         
             
            ```
         
     | 
| 
       141 
232 
     | 
    
         | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
            By default Shrine will extract the MIME type from the `Content-Type` header of
         
     | 
| 
       145 
     | 
    
         
            -
            the uploaded file, which is solely determined from the file extension, so it's
         
     | 
| 
       146 
     | 
    
         
            -
            prone to spoofing. Shrine provides the `determine_mime_type` plugin which
         
     | 
| 
       147 
     | 
    
         
            -
            determines the MIME type from the file *contents* instead:
         
     | 
| 
      
 233 
     | 
    
         
            +
            Unlike Paperclip, Shrine will store this information for each processed
         
     | 
| 
      
 234 
     | 
    
         
            +
            version, making them first-class citizens:
         
     | 
| 
       148 
235 
     | 
    
         | 
| 
       149 
236 
     | 
    
         
             
            ```rb
         
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
      
 237 
     | 
    
         
            +
            photo.image[:original]       #=> #<Shrine::UploadedFile>
         
     | 
| 
      
 238 
     | 
    
         
            +
            photo.image[:original].width #=> 800
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
            photo.image[:thumb]          #=> #<Shrine::UploadedFile>
         
     | 
| 
      
 241 
     | 
    
         
            +
            photo.image[:thumb].width    #=> 300
         
     | 
| 
       151 
242 
     | 
    
         
             
            ```
         
     | 
| 
       152 
243 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
      
 244 
     | 
    
         
            +
            Also, since Paperclip stores only the filename, it has to recalculate the full
         
     | 
| 
      
 245 
     | 
    
         
            +
            location each time it wants to generate the URL. That makes it really difficult
         
     | 
| 
      
 246 
     | 
    
         
            +
            to move files to a new location, because changing how the location is generated
         
     | 
| 
      
 247 
     | 
    
         
            +
            will now cause incorrect URLs to be generated for all existing files. Shrine
         
     | 
| 
      
 248 
     | 
    
         
            +
            calculates the whole location only once and saves it to the column.
         
     | 
| 
       156 
249 
     | 
    
         | 
| 
       157 
250 
     | 
    
         
             
            ### Hooks/Callbacks
         
     | 
| 
       158 
251 
     | 
    
         | 
| 
         @@ -312,8 +405,6 @@ In Shrine attachments will automatically use `:cache` and `:store` storages 
     | 
|
| 
       312 
405 
     | 
    
         
             
            which you have to register:
         
     | 
| 
       313 
406 
     | 
    
         | 
| 
       314 
407 
     | 
    
         
             
            ```rb
         
     | 
| 
       315 
     | 
    
         
            -
            require "shrine/storage/file_system"
         
     | 
| 
       316 
     | 
    
         
            -
             
     | 
| 
       317 
408 
     | 
    
         
             
            Shrine.storages = {
         
     | 
| 
       318 
409 
     | 
    
         
             
              cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
         
     | 
| 
       319 
410 
     | 
    
         
             
              store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
         
     | 
| 
         @@ -411,4 +502,5 @@ Shrine doesn't have an equivalent to this, but the [Regenerating versions] 
     | 
|
| 
       411 
502 
     | 
    
         
             
            guide provides some useful tips on how to do this.
         
     | 
| 
       412 
503 
     | 
    
         | 
| 
       413 
504 
     | 
    
         
             
            [file]: http://linux.die.net/man/1/file
         
     | 
| 
       414 
     | 
    
         
            -
            [ 
     | 
| 
      
 505 
     | 
    
         
            +
            [Reprocessing versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
         
     | 
| 
      
 506 
     | 
    
         
            +
            [direct S3 uploads]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
         
     |