shrine 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of shrine might be problematic. Click here for more details.

Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +143 -84
  3. data/doc/carrierwave.md +187 -47
  4. data/doc/direct_s3.md +57 -39
  5. data/doc/paperclip.md +183 -91
  6. data/doc/refile.md +148 -124
  7. data/doc/regenerating_versions.md +2 -3
  8. data/lib/shrine.rb +26 -28
  9. data/lib/shrine/plugins/activerecord.rb +22 -31
  10. data/lib/shrine/plugins/add_metadata.rb +1 -1
  11. data/lib/shrine/plugins/backgrounding.rb +19 -7
  12. data/lib/shrine/plugins/backup.rb +2 -2
  13. data/lib/shrine/plugins/cached_attachment_data.rb +1 -1
  14. data/lib/shrine/plugins/copy.rb +52 -0
  15. data/lib/shrine/plugins/data_uri.rb +1 -1
  16. data/lib/shrine/plugins/default_storage.rb +2 -2
  17. data/lib/shrine/plugins/default_url.rb +1 -1
  18. data/lib/shrine/plugins/default_url_options.rb +1 -1
  19. data/lib/shrine/plugins/delete_promoted.rb +1 -1
  20. data/lib/shrine/plugins/delete_raw.rb +1 -1
  21. data/lib/shrine/plugins/determine_mime_type.rb +3 -2
  22. data/lib/shrine/plugins/direct_upload.rb +36 -24
  23. data/lib/shrine/plugins/download_endpoint.rb +3 -3
  24. data/lib/shrine/plugins/dynamic_storage.rb +2 -2
  25. data/lib/shrine/plugins/hooks.rb +1 -1
  26. data/lib/shrine/plugins/included.rb +3 -4
  27. data/lib/shrine/plugins/keep_files.rb +1 -1
  28. data/lib/shrine/plugins/logging.rb +1 -1
  29. data/lib/shrine/plugins/module_include.rb +1 -1
  30. data/lib/shrine/plugins/moving.rb +10 -5
  31. data/lib/shrine/plugins/multi_delete.rb +2 -2
  32. data/lib/shrine/plugins/parallelize.rb +2 -2
  33. data/lib/shrine/plugins/parsed_json.rb +1 -1
  34. data/lib/shrine/plugins/pretty_location.rb +1 -1
  35. data/lib/shrine/plugins/processing.rb +11 -9
  36. data/lib/shrine/plugins/rack_file.rb +1 -1
  37. data/lib/shrine/plugins/recache.rb +14 -4
  38. data/lib/shrine/plugins/remote_url.rb +1 -1
  39. data/lib/shrine/plugins/remove_attachment.rb +3 -4
  40. data/lib/shrine/plugins/remove_invalid.rb +1 -1
  41. data/lib/shrine/plugins/restore_cached_data.rb +11 -4
  42. data/lib/shrine/plugins/sequel.rb +34 -45
  43. data/lib/shrine/plugins/store_dimensions.rb +1 -1
  44. data/lib/shrine/plugins/upload_options.rb +2 -2
  45. data/lib/shrine/plugins/validation_helpers.rb +7 -8
  46. data/lib/shrine/plugins/versions.rb +31 -30
  47. data/lib/shrine/storage/file_system.rb +16 -12
  48. data/lib/shrine/storage/s3.rb +36 -2
  49. data/lib/shrine/version.rb +1 -1
  50. data/shrine.gemspec +9 -8
  51. metadata +11 -9
@@ -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 frees your
4
- server from accepting file uploads. If on saving the record you need to do some
5
- file processing, you can kick that into a background job using the
6
- `backgrounding` plugin. If you're not doing any processing and your permanent
7
- storage is also S3, saving the record will perform an S3 COPY request from
8
- cache to store, without any downloading and uploading (which is both fast and
9
- memory-efficient).
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
- First thing that you need to do is enable CORS on your S3 bucket. You can do
25
- that by clicking on "Properties > Permissions > Add CORS Configuration", and
26
- then just follow the Amazon documentation on how to write a CORS file.
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
- Shrine's JSON representation of an uploaded file looks like this:
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
- The `id`, `storage` fields are optional, while the `metadata` values are
50
- optional (`metadata.size` is only required to later upload that file to a
51
- non-S3 storage). After uploading the file to S3, you need to construct this
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 request the presign from the
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 => "/image"
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 [Dropzone]. After the upload you should
95
- create a JSON representation of the uploaded file, which you can write to
96
- the hidden attachment field:
97
-
98
- ```js
99
- var image = {
100
- id: key.match(/cache\/(.+)/)[1], // we have to remove the prefix part
101
- storage: 'cache',
102
- metadata: {
103
- size: data.files[0].size,
104
- filename: data.files[0].name.match(/[^\/\\]+$/)[0], // IE returns full path
105
- mime_type: data.files[0].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
- It's generally a good idea to disable the submit button until the file is
113
- uploaded, as well as display a progress bar. See the [example app] for a
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(60, data) # tells a Sidekiq worker to perform in 1 minute
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
- [example app]: https://github.com/janko-m/shrine-example
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
@@ -1,158 +1,251 @@
1
1
  # Shrine for Paperclip Users
2
2
 
3
- This guide is aimed at helping Paperclip users transition to Shrine. We will
4
- first generally mention what are the key differences. Afterwards there is a
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
- ## Uploaders
9
-
10
- While in Paperclip you write your uploading logic as a list of options inside
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
- ```rb
15
- class ImageUploader < Shrine
16
- plugin :validation_helpers
10
+ ## Storages
17
11
 
18
- Attacher.validate do
19
- validate_mime_type_inclusion [/^image/]
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
- def process(io, context)
23
- # processing
24
- end
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
- Unlike Paperclip, in Shrine you can use these uploaders directly if you have
29
- to do some lower-level logic. First you need to register storages, and then
30
- you can instantiate uploaders with a specific storage:
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::FileSystem.new("public", prefix: "uploads/store"),
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 = Shrine.new(:cache)
74
+ uploader = ImageUploader.new(:store)
42
75
  uploaded_file = uploader.upload(File.open("nature.jpg"))
43
- uploaded_file.path #=> "/uploads/cache/s9ffdkfd02kd.jpg"
44
- uploaded_file.original_filename #=> "nature.jpg"
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
- Unlike Paperclip, in Shrine you define and perform processing on
50
- instance-level, which gives a lot of flexibility. As the result you can return
51
- a single file or a hash of versions:
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
- require "image_processing/mini_magick" # part of the "image_processing" gem
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
- thumbnail = resize_to_limit(io.download, 300, 300)
63
- {original: io, thumbnail: thumbnail}
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
- #### Regenerating versions
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
- Shrine doesn't have a built-in way of regenerating versions, because that's
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
- ### Logging
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
- In Paperclip you enable logging by setting `Paperclip.options[:log] = true`.
78
- Shrine also provides logging with the `logging` plugin:
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
- Shrine.plugin :logging
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
- ## Attachments
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
- The uploaders can then integrate with models by generating attachment modules
87
- which are included into the models. Shrine ships with plugins for Sequel and
88
- ActiveRecord ORMs, so you first have to load the one for your ORM:
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 :sequel # If you're using Sequel
92
- Shrine.plugin :activerecord # If you're using ActiveRecord
165
+ Shrine.plugin :determine_mime_type
93
166
  ```
94
167
 
95
- Now you use your uploaders to generate "attachment modules", which you can then
96
- include in your models:
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
- class User < Sequel::Model
100
- include ImageUploader[:avatar] # adds `avatar`, `avatar=` and `avatar_url` methods
101
- end
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
- Unlike in Paperclip which requires you to have 4 `<attachment>_*` columns, in
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
- The attachments use `:store` for storing the files, and `:cache` for caching.
109
- The latter is something Paperclip doesn't do, but caching before storing is
110
- really great because the file then persists on validation errors, and also in
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
- ### Validations
195
+ ```rb
196
+ Shrine.plugin :activerecord # if you're using ActiveRecord
197
+ Shrine.plugin :sequel # if you're using Sequel
198
+ ```
115
199
 
116
- In Shrine validations are done inside uploader classes, and validation methods
117
- are provided by the `validation_helpers` plugin:
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 ImageUploader < Shrine
121
- plugin :validation_helpers
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
- For presence validation you should use the one provided by your ORM:
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
- class User < Sequel::Model
134
- include ImageUploader[:avatar]
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
- def validate
137
- validates_presence [:avatar]
138
- end
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
- #### MIME type spoofing
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
- Shrine.plugin :determine_mime_type
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
- By default the UNIX [file] utility is used, but you can choose other analyzers.
154
- Unlike Paperclip, you won't get any errors if the MIME type is "spoofed",
155
- instead it's better if you simply validate allowed MIME types.
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
- [Regenerating versions]: http://shrinerb.com/rdoc/files/doc/regenerating_versions_md.html
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