shrine 1.4.2 → 2.0.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +236 -234
  3. data/doc/changing_location.md +6 -4
  4. data/doc/creating_storages.md +4 -4
  5. data/doc/design.md +223 -0
  6. data/doc/migrating_storage.md +6 -11
  7. data/doc/regenerating_versions.md +22 -40
  8. data/lib/shrine.rb +60 -77
  9. data/lib/shrine/plugins/activerecord.rb +37 -14
  10. data/lib/shrine/plugins/background_helpers.rb +1 -0
  11. data/lib/shrine/plugins/backgrounding.rb +49 -37
  12. data/lib/shrine/plugins/backup.rb +6 -4
  13. data/lib/shrine/plugins/cached_attachment_data.rb +5 -5
  14. data/lib/shrine/plugins/data_uri.rb +9 -9
  15. data/lib/shrine/plugins/default_storage.rb +4 -4
  16. data/lib/shrine/plugins/default_url.rb +7 -1
  17. data/lib/shrine/plugins/default_url_options.rb +1 -1
  18. data/lib/shrine/plugins/delete_promoted.rb +2 -2
  19. data/lib/shrine/plugins/delete_raw.rb +4 -4
  20. data/lib/shrine/plugins/determine_mime_type.rb +50 -43
  21. data/lib/shrine/plugins/direct_upload.rb +10 -20
  22. data/lib/shrine/plugins/download_endpoint.rb +16 -13
  23. data/lib/shrine/plugins/dynamic_storage.rb +4 -12
  24. data/lib/shrine/plugins/included.rb +6 -19
  25. data/lib/shrine/plugins/keep_files.rb +4 -4
  26. data/lib/shrine/plugins/logging.rb +4 -4
  27. data/lib/shrine/plugins/migration_helpers.rb +37 -34
  28. data/lib/shrine/plugins/moving.rb +19 -32
  29. data/lib/shrine/plugins/parallelize.rb +5 -5
  30. data/lib/shrine/plugins/pretty_location.rb +2 -6
  31. data/lib/shrine/plugins/remote_url.rb +31 -43
  32. data/lib/shrine/plugins/remove_attachment.rb +5 -5
  33. data/lib/shrine/plugins/remove_invalid.rb +1 -1
  34. data/lib/shrine/plugins/restore_cached_data.rb +4 -10
  35. data/lib/shrine/plugins/sequel.rb +46 -21
  36. data/lib/shrine/plugins/store_dimensions.rb +19 -20
  37. data/lib/shrine/plugins/upload_options.rb +11 -9
  38. data/lib/shrine/plugins/validation_helpers.rb +3 -3
  39. data/lib/shrine/plugins/versions.rb +18 -3
  40. data/lib/shrine/storage/file_system.rb +9 -11
  41. data/lib/shrine/storage/linter.rb +1 -7
  42. data/lib/shrine/storage/s3.rb +25 -19
  43. data/lib/shrine/version.rb +3 -3
  44. data/shrine.gemspec +13 -3
  45. metadata +28 -9
  46. data/lib/shrine/plugins/delete_uploaded.rb +0 -3
  47. data/lib/shrine/plugins/keep_location.rb +0 -46
  48. data/lib/shrine/plugins/restore_cached.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0c096588cbb135f27c0646661a10884af2d130aa
4
- data.tar.gz: cb699c483ecd3e9309dbdcbddfd1c7fb919664b8
3
+ metadata.gz: 25b8acff9f68f0cc11cbadc0eeb39a5bdcf839d1
4
+ data.tar.gz: 666390819abcf036b20f46ecf04b1a30fef68a28
5
5
  SHA512:
6
- metadata.gz: 62d92df4fb94a72f6f6f4e31331ac50b116f0536da7594240d517bb9fa7553cc73e3ff53221b4dc68dbe95fb67e957f6ca34d7f62abb0034889fbfb09498e011
7
- data.tar.gz: f01073235dce4eaa882c5b419e162bcfe1ef327754978ae55a6f47b0c7687c4331d9143485dd32696729f83e8087c56e60c9c3c584128416f322efa158c7393b
6
+ metadata.gz: be25b88ca4629b8a1e052ed442b5530d1661a0f23a4d3d7cb0cb2e6f6538379c1a46a22e921091a3a718755e1f4378bd97a25312bde436134cdd3ac0ba487194
7
+ data.tar.gz: cf90025e05a3f0790bde9ba0763a66e75c6aba48a1b5586a4a814947693154d1a345ae90c447341f349cf9e3abf29a62e2fe6ab211993fc1685ad38cef479b33
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Shrine
2
2
 
3
- Shrine is a toolkit for file uploads in Ruby applications.
3
+ Shrine is a toolkit for handling file uploads in Ruby applications.
4
4
 
5
5
  If you're new, you're encouraged to read the [introductory blog post] which
6
6
  explains the motivation behind Shrine.
@@ -10,7 +10,7 @@ explains the motivation behind Shrine.
10
10
  - Documentation: [shrinerb.com](http://shrinerb.com)
11
11
  - Source: [github.com/janko-m/shrine](https://github.com/janko-m/shrine)
12
12
  - Bugs: [github.com/janko-m/shrine/issues](https://github.com/janko-m/shrine/issues)
13
- - Help & Dicussion: [groups.google.com/group/ruby-shrine](https://groups.google.com/forum/#!forum/ruby-shrine)
13
+ - Help & Discussion: [groups.google.com/group/ruby-shrine](https://groups.google.com/forum/#!forum/ruby-shrine)
14
14
 
15
15
  ## Installation
16
16
 
@@ -18,11 +18,11 @@ explains the motivation behind Shrine.
18
18
  gem "shrine"
19
19
  ```
20
20
 
21
- Shrine has been tested on MRI 2.1, MRI 2.2, MRI 2.3, JRuby and Rubinius.
21
+ Shrine has been tested on MRI 2.1, MRI 2.2, MRI 2.3 and JRuby.
22
22
 
23
23
  ## Basics
24
24
 
25
- Here's a basic example showing how file upload works in Shrine:
25
+ Here's an example showing how basic file upload works in Shrine:
26
26
 
27
27
  ```rb
28
28
  require "shrine"
@@ -34,7 +34,6 @@ uploader = Shrine.new(:file_system)
34
34
 
35
35
  uploaded_file = uploader.upload(File.open("movie.mp4"))
36
36
  uploaded_file #=> #<Shrine::UploadedFile>
37
- uploaded_file.url #=> "/uploads/9260ea09d8effd.mp4"
38
37
  uploaded_file.data #=>
39
38
  # {
40
39
  # "storage" => "file_system",
@@ -43,27 +42,25 @@ uploaded_file.data #=>
43
42
  # }
44
43
  ```
45
44
 
46
- First we add the storage we want to use to Shrine's registry. Storages are
47
- simple Ruby classes which perform the actual uploads. We instantiate a `Shrine`
48
- with the storage name, and when we call `#upload` Shrine does the following:
45
+ Let's see what's going on here:
49
46
 
50
- * extracts metadata from the file
51
- * generates a unique location for the file
52
- * uploads the file using the underlying storage
53
- * closes the file
54
- * returns a `Shrine::UploadedFile` with relevant data
47
+ First we registered the storage we want to use under a name. Storages are plain
48
+ Ruby classes which encapsulate file management on a particular service. We can
49
+ then instantiate `Shrine` as a wrapper around that storage. A call to `upload`
50
+ uploads the given file to the underlying storage.
55
51
 
56
- The argument to `Shrine#upload` needs to be an IO-like object. So, `File`,
57
- `Tempfile` and `StringIO` are all valid arguments. But the object doesn't have
58
- to be an actual IO, it's enough that it responds to these 5 methods:
59
- `#read(*args)`, `#size`, `#eof?`, `#rewind` and `#close`.
60
- `ActionDispatch::Http::UploadedFile` is one such object.
52
+ The argument to `upload` needs to be an IO-like object. So, `File`, `Tempfile`
53
+ and `StringIO` are all valid arguments. The object doesn't have to be an actual
54
+ IO, though, it's enough that it responds to these 5 methods: `#read(*args)`,
55
+ `#size`, `#eof?`, `#rewind` and `#close`. `ActionDispatch::Http::UploadedFile`
56
+ is one such object, as well as `Shrine::UploadedFile` itself.
61
57
 
62
- The returned object is a [`Shrine::UploadedFile`], which represents the file
63
- that was uploaded, and we can do a lot with it:
58
+ The result of uploading is a `Shrine::UploadedFile` object, which represents
59
+ the uploaded file on the storage. It is defined solely by its data hash. We can
60
+ do a lot with it:
64
61
 
65
62
  ```rb
66
- uploaded_file.url #=> "/uploads/938kjsdf932.mp4"
63
+ uploaded_file.url #=> "uploads/938kjsdf932.mp4"
67
64
  uploaded_file.metadata #=> {...}
68
65
  uploaded_file.read #=> "..."
69
66
  uploaded_file.exists? #=> true
@@ -72,16 +69,15 @@ uploaded_file.delete
72
69
  # ...
73
70
  ```
74
71
 
75
- To read about the metadata that is stored with the uploaded file, see the
76
- [metadata](#metadata) section.
77
-
78
72
  ## Attachment
79
73
 
80
- In web applications, instead of managing files directly, we rather want to
81
- treat them as "attachments" to records and tie them to their lifecycle. In
82
- Shrine we do this by generating and including "attachment" modules.
74
+ In web applications we usually want work with files on a higher level. We want
75
+ to treat them as "attachments" to records, by persisting their information to a
76
+ database column and tying their lifecycle to the record. For this Shrine offers
77
+ a higher-level attachment interface.
83
78
 
84
- Firstly we need to assign the special `:cache` and `:store` storages:
79
+ First we need to register temporary and permanent storage which will be used
80
+ internally:
85
81
 
86
82
  ```rb
87
83
  require "shrine"
@@ -93,88 +89,90 @@ Shrine.storages = {
93
89
  }
94
90
  ```
95
91
 
96
- These storages will by default be used for caching and storing attachments, but
97
- you can use additional storages with the `default_storage` plugin. Next we
98
- should create an uploader specific to the type of files we're uploading:
92
+ The `:cache` and `:store` are only special in terms that they will be used
93
+ automatically (but that can be changed with the default_storage plugin). Next,
94
+ we create an uploader class specific to the type of attachment we want, so that
95
+ later we can have different uploading logic for different attachment types.
99
96
 
100
97
  ```rb
101
98
  class ImageUploader < Shrine
102
- # your logic for uploading files
99
+ # your logic for uploading images
103
100
  end
104
101
  ```
105
102
 
106
- Now if we assume that we have a "User" model, and we want our users to have an
107
- "avatar", we can generate and include an "attachment" module:
103
+ Finally, to add an attachment to a model, we generate a named "attachment"
104
+ module using the uploader and include it:
108
105
 
109
106
  ```rb
110
- class User
111
- include ImageUploader[:avatar] # requires "avatar_data" attribute
107
+ class Photo
108
+ include ImageUploader[:image] # requires "image_data" attribute
112
109
  end
113
110
  ```
114
111
 
115
- Now our model has gained special methods for attaching avatars:
112
+ Now our model has gained special methods for attaching files:
116
113
 
117
114
  ```rb
118
- user = User.new
119
- user.avatar = File.open("avatar.jpg") # uploads the file to cache
120
- user.avatar #=> #<Shrine::UploadedFile>
121
- user.avatar_url #=> "/uploads/9260ea09d8effd.jpg"
122
- user.avatar_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
115
+ photo = Photo.new
116
+ photo.image = File.open("nature.jpg") # uploads the file to cache
117
+ photo.image #=> #<Shrine::UploadedFile>
118
+ photo.image_url #=> "/uploads/cache/9260ea09d8effd.jpg"
119
+ photo.image_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
123
120
  ```
124
121
 
125
- The attachment module has added `#avatar`, `#avatar=` and `#avatar_url`
126
- methods to our User, using regular module inclusion.
122
+ The attachment module has added `#image`, `#image=` and `#image_url`
123
+ methods to our `Photo`, using regular module inclusion.
127
124
 
128
125
  ```rb
129
- Shrine[:avatar] #=> #<Shrine::Attachment(avatar)>
130
- Shrine[:avatar].is_a?(Module) #=> true
131
- Shrine[:avatar].instance_methods #=> [:avatar=, :avatar, :avatar_url, :avatar_attacher]
126
+ Shrine[:image] #=> #<Shrine::Attachment(image)>
127
+ Shrine[:image].is_a?(Module) #=> true
128
+ Shrine[:image].instance_methods #=> [:image=, :image, :image_url, :image_attacher]
132
129
 
133
130
  Shrine[:document] #=> #<Shrine::Attachment(document)>
134
131
  Shrine[:document].instance_methods #=> [:document=, :document, :document_url, :document_attacher]
135
132
 
136
- # If you prefer to be more explicit, you can use the expanded forms
137
- Shrine.attachment(:avatar)
133
+ # Expanded forms
134
+ Shrine.attachment(:image)
138
135
  Shrine::Attachment.new(:document)
139
136
  ```
140
137
 
141
- * `#avatar=` – caches the file and saves a JSON representation into `avatar_data`
142
- * `#avatar` – returns a `Shrine::UploadedFile` based on the data from `avatar_data`
143
- * `#avatar_url` – calls `avatar.url` if attachment is present, otherwise returns nil.
138
+ * `#image=` – caches the file and saves JSON data into `image_data`
139
+ * `#image` – returns a `Shrine::UploadedFile` based on data from `image_data`
140
+ * `#image_url` – calls `image.url` if attachment is present, otherwise returns nil.
144
141
 
145
- This is how you would typically create the form for a `@user`:
142
+ This is how you would typically create the form for a `@photo`:
146
143
 
147
144
  ```erb
148
- <form action="/users" method="post" enctype="multipart/form-data">
149
- <input name="user[avatar]" type="hidden" value="<%= @user.avatar_data %>">
150
- <input name="user[avatar]" type="file">
145
+ <form action="/photos" method="post" enctype="multipart/form-data">
146
+ <input name="photo[image]" type="hidden" value="<%= @photo.image_data %>">
147
+ <input name="photo[image]" type="file">
151
148
  </form>
152
149
  ```
153
150
 
154
151
  The "file" field is for file upload, while the "hidden" field is to make the
155
152
  file persist in case of validation errors, and for direct uploads. This code
156
- works because `#avatar=` also accepts already cached files via their JSON
153
+ works because `#image=` also accepts an already cached file via its JSON
157
154
  representation:
158
155
 
159
156
  ```rb
160
- user.avatar = '{"id":"9jsdf02kd", "storage":"cache", "metadata": {...}}'
157
+ photo.image = '{"id":"9jsdf02kd", "storage":"cache", "metadata": {...}}'
161
158
  ```
162
159
 
163
160
  ### ORM
164
161
 
165
- Your models probably won't be POROs, so Shrine ships with plugins for
166
- Sequel and ActiveRecord ORMs. Shrine uses the `<attachment>_data` column
167
- for storing attachments, so you'll need to add it in a migration:
162
+ Even though you can use Shrine's attachment interface with plain Ruby objects,
163
+ it's much more common to use it with an ORM. Shrine ships with plugins for
164
+ Sequel and ActiveRecord ORMs. It uses the `<attachment>_data` column for
165
+ storing data for uploaded files, so you'll need to add it in a migration.
168
166
 
169
167
  ```rb
170
- add_column :users, :avatar_data, :text # or a JSON column
168
+ add_column :movies, :video_data, :text # or a JSON column
171
169
  ```
172
170
  ```rb
173
171
  Shrine.plugin :sequel # or :activerecord
174
172
  ```
175
173
  ```rb
176
- class User < Sequel::Model
177
- include ImageUploader[:avatar]
174
+ class Movie < Sequel::Model
175
+ include VideoUploader[:video]
178
176
  end
179
177
  ```
180
178
 
@@ -182,77 +180,61 @@ In addition to getters and setters, the ORM plugins add the appropriate
182
180
  callbacks:
183
181
 
184
182
  ```rb
185
- user.avatar = File.open("avatar.jpg")
186
- user.avatar.storage_key #=> "cache"
187
- user.save
188
- user.avatar.storage_key #=> "store"
189
- user.destroy
190
- user.avatar.exists? #=> false
191
- ```
192
-
193
- *NOTE: The record will first be saved with the cached attachment, and afterwards
194
- (in an "after commit" hook) updated with the stored attachment. This is done so
195
- that processing/storing isn't performed inside a database transaction. If you're
196
- doing processing, there will be a bried period of time when the record will exist
197
- with an unprocessed attachment, so you may need to account for that.*
198
-
199
- ## Direct uploads
183
+ movie.video = File.open("video.mp4")
184
+ movie.video_url #=> "/uploads/cache/0sdfllasfi842.mp4"
200
185
 
201
- Shrine comes with a `direct_upload` plugin which provides a [Roda] endpoint
202
- that accepts file uploads. This allows you to asynchronously start caching the
203
- file the moment the user selects it (e.g. using the [jQuery-File-Upload] JS
204
- library), which gives a nice experience to the user.
186
+ movie.save
187
+ movie.video_url #=> "/uploads/store/l02kladf8jlda.mp4"
205
188
 
206
- ```rb
207
- Shrine.plugin :direct_upload # Provides a Roda endpoint
208
- ```
209
- ```rb
210
- Rails.application.routes.draw do
211
- mount ImageUploader::UploadEndpoint => "/attachments/images"
212
- end
213
- ```
214
- ```js
215
- $('[type="file"]').fileupload({
216
- url: '/attachments/images/cache/upload',
217
- paramName: 'file',
218
- add: function(e, data) { /* Disable the submit button */ },
219
- progress: function(e, data) { /* Add a nice progress bar */ },
220
- done: function(e, data) { /* Fill in the hidden field with the result */ }
221
- });
189
+ movie.destroy
190
+ movie.video.exists? #=> false
222
191
  ```
223
192
 
224
- The plugin also provides a route that can be used for doing direct S3 uploads.
225
- See the documentation of the plugin for more details, as well as the [example
226
- app] to see how easy it is to implement multiple uploads directly to S3.
193
+ First the raw file is cached to temporary storage on assignment, then on saving
194
+ the cached file is uploaded to permanent storage. Destroying the record
195
+ destroys the attachment.
196
+
197
+ *NOTE: The record will first be saved with the cached attachment, and
198
+ afterwards (in an "after commit" hook) updated with the stored attachment. This
199
+ is done so that processing/storing isn't performed inside a database
200
+ transaction. If you're doing processing, there will be a period of time when
201
+ the record will be saved with an unprocessed attachment, so you may need to
202
+ account for that.*
227
203
 
228
204
  ## Processing
229
205
 
230
206
  Whenever a file is uploaded, `Shrine#process` is called, and this is where
231
207
  you're expected to define your processing.
232
208
 
209
+ ```rb
210
+ class ImageUploader < Shrine
211
+ def process(io, context)
212
+ # ...
213
+ end
214
+ end
215
+ ```
216
+
217
+ Shrine's uploaders are stateless; the `#process` method is simply a function
218
+ which takes an input `io` and returns processed file(s) as output. Since it's
219
+ called for each upload, attaching the file will call it twice, first when
220
+ raw file is cached to temporary storage on assignment, then when cached file
221
+ is uploaded to permanent storage on saving. We usually want to process in the
222
+ latter phase (after file validations):
223
+
233
224
  ```rb
234
225
  class ImageUploader < Shrine
235
226
  def process(io, context)
236
227
  if context[:phase] == :store
237
- # processing...
228
+ # ...
238
229
  end
239
230
  end
240
231
  end
241
232
  ```
242
233
 
243
- The `io` is the file being uploaded, and `context` we'll leave for later. You
244
- may be wondering why we need this conditional. Well, when an attachment is
245
- assigned and saved, an "upload" actually happens two times. First the file is
246
- "uploaded" to cache on assignment, and then the cached file is reuploaded to
247
- store on save. You could theoretically do processing in both phases, depending
248
- on your preferences (although it's generally not recommended to process on
249
- caching, because it happens before file validations; use the `recache` plugin
250
- instead).
251
-
252
234
  Ok, now how do we do the actual processing? Well, Shrine actually doesn't ship
253
235
  with any file processing functionality, because that is a generic problem that
254
- belongs in a separate gem. If the type of files you're uploading are images, I
255
- created the [image_processing] gem which you can use with Shrine:
236
+ belongs in separate libraries. If the type of files you're uploading are
237
+ images, I created the [image_processing] gem which you can use with Shrine:
256
238
 
257
239
  ```rb
258
240
  require "image_processing/mini_magick"
@@ -268,20 +250,14 @@ class ImageUploader < Shrine
268
250
  end
269
251
  ```
270
252
 
271
- Notice that we needed to call `io.download`. This is because the original file
272
- was already stored to cache, and now this cached file is being uploaded to
273
- store. The cached file is an instance of `Shrine::UploadedFile`, but for
274
- processing we need to work with actual files, so we first need to download it.
275
-
276
- In general, processing works in a way that if `#process` returns a file, Shrine
277
- continues storing that file, otherwise if nil is returned, Shrine continues
278
- storing the original file.
253
+ Since here `io` is a cached `Shrine::UploadedFile`, we need to download it to
254
+ a file, as image_processing only accepts real files.
279
255
 
280
256
  ### Versions
281
257
 
282
258
  If you're uploading images, often you'll want to store various thumbnails
283
- alongside your original image. For that you just need to load the `versions`
284
- plugin, and now in `#process` you can return a Hash of versions:
259
+ alongside your original image. You can do that by loading the versions plugin,
260
+ and in `#process` simply returning a Hash of versions:
285
261
 
286
262
  ```rb
287
263
  require "image_processing/mini_magick"
@@ -302,25 +278,24 @@ class ImageUploader < Shrine
302
278
  end
303
279
  ```
304
280
 
305
- As you see, instead of a complex class-level DSL, Shrine provides a very simple
306
- instance-level interface where you're in complete control over processing. The
307
- processed files are Ruby Tempfiles and they should eventually get deleted by
308
- themselves, but you can also use the `moving` plugin to delete them immediately
309
- after upload.
281
+ Being able to define processing on instance level provides a lot of flexibility,
282
+ allowing things like choosing the order or adding parallelization. It is
283
+ recommended to use the delete_raw plugin for automatically deleting processed
284
+ files after uploading.
310
285
 
311
- Now when you access the stored attachment, a Hash of versions will be returned
312
- instead:
286
+ The attachment getter will simply return the processed attachment as a Hash of
287
+ versions:
313
288
 
314
289
  ```rb
315
- user.avatar.class #=> Hash
290
+ photo.image.class #=> Hash
316
291
 
317
292
  # With the store_dimensions plugin
318
- user.avatar[:large].width #=> 700
319
- user.avatar[:medium].width #=> 500
320
- user.avatar[:small].width #=> 300
293
+ photo.image[:large].width #=> 700
294
+ photo.image[:medium].width #=> 500
295
+ photo.image[:small].width #=> 300
321
296
 
322
297
  # The plugin expands this method to accept version names.
323
- user.avatar_url(:large) #=> "..."
298
+ photo.image_url(:large) #=> "..."
324
299
  ```
325
300
 
326
301
  ## Context
@@ -337,28 +312,28 @@ class ImageUploader < Shrine
337
312
  end
338
313
  ```
339
314
  ```rb
340
- user = User.new
341
- user.avatar = File.open("avatar.jpg") # "cache"
342
- user.save # "store"
315
+ photo = Photo.new
316
+ photo.image = File.open("image.jpg") # "cache"
317
+ photo.save # "store"
343
318
  ```
344
319
  ```
345
- {:name=>:avatar, :record=>#<User:0x007fe1627f1138>, :phase=>:cache}
346
- {:name=>:avatar, :record=>#<User:0x007fe1627f1138>, :phase=>:store}
320
+ {:name=>:image, :record=>#<Photo:0x007fe1627f1138>, :phase=>:cache}
321
+ {:name=>:image, :record=>#<Photo:0x007fe1627f1138>, :phase=>:store}
347
322
  ```
348
323
 
349
- The `:name` is the name of the attachment, in this case "avatar". The `:record`
350
- is the model instance, in this case instance of `User`. Lastly, the `:phase`;
351
- by default the two main phases of attaching are "cache" and "store", but some
352
- plugins add more of them, and there are different ones for deleting files.
324
+ The `:name` is the name of the attachment, in this case "image". The `:record`
325
+ is the model instance, in this case instance of `Photo`. Lastly, the `:phase`
326
+ is a symbol which indicates the purpose of the upload (by default there are
327
+ only `:cache` and `:store`, but some plugins add more of them).
353
328
 
354
- Context is really useful for doing conditional processing and validation, since
355
- we have access to the record and attachment name. In general the context is
356
- used deeply in Shrine for various purposes.
329
+ Context is useful for doing conditional processing and validation, since we
330
+ have access to the record and attachment name, and it is also used by some
331
+ plugins internally.
357
332
 
358
333
  ## Validations
359
334
 
360
- Validations are registered by calling `Shrine::Attacher.validate`, and are best
361
- done with the `validation_helpers` plugin:
335
+ Validations are registered by calling `Attacher.validate`, and are best done
336
+ with the validation_helpers plugin:
362
337
 
363
338
  ```rb
364
339
  class DocumentUploader < Shrine
@@ -366,7 +341,7 @@ class DocumentUploader < Shrine
366
341
 
367
342
  Attacher.validate do
368
343
  # Evaluated inside an instance of Shrine::Attacher.
369
- if record.guest?
344
+ if record.resume?
370
345
  validate_max_size 10*1024*1024, message: "is too large (max is 10 MB)"
371
346
  validate_mime_type_inclusion ["application/pdf"]
372
347
  end
@@ -375,72 +350,53 @@ end
375
350
  ```
376
351
 
377
352
  ```rb
378
- user = User.new
379
- user.resume = File.open("resume.pdf")
380
- user.valid? #=> false
381
- user.errors.to_hash #=> {resume: ["is too large (max is 2 MB)"]}
353
+ document = Document.new(resume: true)
354
+ document.file = File.open("resume.pdf")
355
+ document.valid? #=> false
356
+ document.errors.to_hash #=> {file: ["is too large (max is 2 MB)"]}
382
357
  ```
383
358
 
384
359
  ## Metadata
385
360
 
386
- By default Shrine extracts and stores general file metadata:
361
+ Shrine automatically extracts and stores general file metadata:
387
362
 
388
363
  ```rb
389
- class UsersController < ApplicationController
390
- def create
391
- user = User.create(params[:user])
392
- user.avatar.metadata #=>
393
- # {
394
- # "filename" => "my_avatar.jpg",
395
- # "mime_type" => "image/jpeg",
396
- # "size" => 345993,
397
- # }
364
+ photo = Photo.create(image: image)
365
+ photo.image.metadata #=>
366
+ # {
367
+ # "filename" => "nature.jpg",
368
+ # "mime_type" => "image/jpeg",
369
+ # "size" => 345993,
370
+ # }
398
371
 
399
- user.avatar.original_filename #=> "my_avatar.jpg"
400
- user.avatar.mime_type #=> "image/jpeg"
401
- user.avatar.size #=> 345993
402
- end
403
- end
372
+ photo.image.original_filename #=> "nature.jpg"
373
+ photo.image.extension #=> "jpg"
374
+ photo.image.mime_type #=> "image/jpeg"
375
+ photo.image.size #=> 345993
404
376
  ```
405
377
 
406
378
  ### MIME type
407
379
 
408
380
  By default, "mime_type" is inherited from `#content_type` of the uploaded file,
409
- which holds the value of the "Content-Type" header added by the browser solely
410
- based on the extension of the uploaded file. This means that by default
411
- Shrine's "mime_type" is *not* guaranteed to hold the actual MIME type of the
412
- file.
381
+ which is set from the "Content-Type" request header, which is determined by the
382
+ browser solely based on the file extension. This means that by default Shrine's
383
+ "mime_type" is *not* guaranteed to hold the actual MIME type of the file.
413
384
 
414
- To help with that Shrine provides the `determine_mime_type` plugin, which by
385
+ To help with that Shrine provides the determine_mime_type plugin, which by
415
386
  default uses the UNIX [file] utility to determine the actual MIME type:
416
387
 
417
388
  ```rb
418
389
  Shrine.plugin :determine_mime_type
419
390
  ```
420
391
  ```rb
421
- user = User.create(avatar: File.open("image.mp4")) # image with a .mp4 extension
422
- user.avatar.mime_type #=> "image/png"
423
- ```
424
-
425
- ### Dimensions
426
-
427
- If you're uploading images and you want to store dimensions, you can use the
428
- `store_dimensions` plugin which extracts dimensions using the [fastimage] gem.
429
-
430
- ```rb
431
- ImageUploader.plugin :store_dimensions
432
- ```
433
- ```rb
434
- user = User.create(avatar: File.open("image.jpg"))
435
- user.avatar.width #=> 400
436
- user.avatar.height #=> 500
392
+ File.write("image.jpg", "<?php ... ?>") # PHP file with a .jpg extension
393
+ photo = Photo.create(image: File.open("image.jpg"))
394
+ photo.image.mime_type #=> "text/x-php"
437
395
  ```
438
396
 
439
- The fastimage gem has built-in protection against [image bombs].
440
-
441
397
  ### Custom metadata
442
398
 
443
- You can also extract and store custom metadata, by overriding
399
+ You can also extract and store custom metadata by overriding
444
400
  `Shrine#extract_metadata`:
445
401
 
446
402
  ```rb
@@ -453,49 +409,55 @@ class ImageUploader < Shrine
453
409
  end
454
410
  ```
455
411
 
456
- Note that if you're extracting custom metadata from Ruby, you should always
457
- rewind the file afterwards.
412
+ Note that you should always rewind the `io` after reading from it.
458
413
 
459
414
  ## Locations
460
415
 
461
- By default files will all be put in the same folder. If you want that each
462
- attachment has its own directory, you can use the `pretty_location` plugin:
416
+ Before Shrine uploads a file, it generates a random location for it. By
417
+ default the hierarchy is flat, all files are stored in the root of the storage.
418
+ If you want that each attachment has its own directory, you can load the
419
+ pretty_location plugin:
463
420
 
464
421
  ```rb
465
422
  Shrine.plugin :pretty_location
466
423
  ```
467
424
  ```rb
468
- user = User.create(avatar: File.open("avatar.jpg"))
469
- user.avatar.id #=> "user/34/avatar/34krtreds2df.jpg"
425
+ photo = Photo.create(image: File.open("nature.jpg"))
426
+ photo.image.id #=> "photo/34/image/34krtreds2df.jpg"
470
427
  ```
471
428
 
472
- If you want to generate your own locations, simply override
429
+ If you want to generate locations on your own, simply override
473
430
  `Shrine#generate_location`:
474
431
 
475
432
  ```rb
476
433
  class ImageUploader < Shrine
477
434
  def generate_location(io, context)
478
- "#{context[:record].class}/#{super}"
435
+ if context[:record]
436
+ "#{context[:record].class}/#{super}"
437
+ else
438
+ super
439
+ end
479
440
  end
480
441
  end
481
442
  ```
482
443
 
483
- Note that there should always be a random component in the location, otherwise
484
- dirty tracking won't be detected properly (you can use `Shrine#generate_uid`).
485
- Inside this method you can access the extracted metadata through
444
+ Note that there should always be a random component in the location, for dirty
445
+ tracking to be detected properly (you can use `Shrine#generate_uid`). Inside
446
+ `#generate_location` you can access the extracted metadata through
486
447
  `context[:metadata]`.
487
448
 
488
449
  When using the uploader directly, it's possible to bypass `#generate_location`
489
- by passing in `:location`:
450
+ by passing a `:location`:
490
451
 
491
452
  ```rb
492
- file = File.open("avatar.jpg")
493
- Shrine.new(:store).upload(file, location: "some/specific/location.jpg")
453
+ uploader = Shrine.new(:store)
454
+ file = File.open("nature.jpg")
455
+ uploader.upload(file, location: "some/specific/location.jpg")
494
456
  ```
495
457
 
496
458
  ## Storage
497
459
 
498
- Other than [FileSystem], Shrine also ships with [S3] storage:
460
+ Other than [FileSystem], Shrine also ships with Amazon [S3] storage:
499
461
 
500
462
  ```rb
501
463
  gem "aws-sdk", "~> 2.1"
@@ -507,42 +469,71 @@ Shrine.storages[:store] = Shrine::Storage::S3.new(
507
469
  access_key_id: "<ACCESS_KEY_ID>", # "xyz"
508
470
  secret_access_key: "<SECRET_ACCESS_KEY>", # "abc"
509
471
  region: "<REGION>", # "eu-west-1"
510
- bucket: "<BUCKET>", # "my-app"
472
+ bucket: "<BUCKET>", # "my-bucket"
511
473
  )
512
474
  ```
513
475
 
514
476
  ```rb
515
- user = User.new(avatar: File.open(:avatar))
516
- user.avatar.url #=> "/uploads/j4k343ui12ls9.jpg"
517
- user.save
518
- user.avatar.url #=> "https://my-bucket.s3-eu-west-1.amazonaws.com/0943sf8gfk13.jpg"
477
+ movie = Movie.new(video: File.open("video.mp4"))
478
+ movie.video_url #=> "/uploads/cache/j4k343ui12ls9.jpg"
479
+ movie.save
480
+ movie.video_url #=> "https://my-bucket.s3-eu-west-1.amazonaws.com/0943sf8gfk13.mp4"
519
481
  ```
520
482
 
521
- If you're using S3 both for cache and store, saving the record will avoid
522
- reuploading the file by issuing an S3 COPY command instead. Also, the
523
- `versions` plugin takes advantage of S3's MULTI DELETE capabilities, so
524
- versions are deleted with a single HTTP request.
483
+ If you're using S3 both for cache and store, uploading a cached file to store
484
+ will simply do an S3 COPY request instead of downloading and reuploading the
485
+ file. Also, the versions plugin takes advantage of S3's MULTI DELETE
486
+ capabilities, so versions are deleted with a single HTTP request.
525
487
 
526
488
  See the full documentation for [FileSystem] and [S3] storages. There are also
527
489
  many other Shrine storages available, see the [Plugins & Storages] section.
528
490
 
529
- ### Clearing cache
491
+ ## Upload options
530
492
 
531
- You will want to periodically clean your cache storage. Amazon S3 provides [a
532
- built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
533
- and for FileSystem you can put something like this in your Rake task:
493
+ Many storages accept additional upload options, which you can pass via the
494
+ upload_options plugin, or manually when uploading:
534
495
 
535
496
  ```rb
536
- file_system = Shrine.storages[:cache]
537
- file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
497
+ uploader = Shrine.new(:store)
498
+ uploader.upload(file, upload_options: {acl: "private"})
499
+ ```
500
+
501
+ ## Direct uploads
502
+
503
+ Shrine comes with a direct_upload plugin which provides a [Roda] endpoint that
504
+ accepts file uploads. This allows you to asynchronously start caching the file
505
+ the moment the user selects it via AJAX (e.g. using the [jQuery-File-Upload] JS
506
+ library).
507
+
508
+ ```rb
509
+ Shrine.plugin :direct_upload # Provides a Roda endpoint
510
+ ```
511
+ ```rb
512
+ Rails.application.routes.draw do
513
+ mount VideoUploader::UploadEndpoint => "/attachments/videos"
514
+ end
515
+ ```
516
+ ```js
517
+ $('[type="file"]').fileupload({
518
+ url: '/attachments/videos/cache/upload',
519
+ paramName: 'file',
520
+ add: function(e, data) { /* Disable the submit button */ },
521
+ progress: function(e, data) { /* Add a nice progress bar */ },
522
+ done: function(e, data) { /* Fill in the hidden field with the result */ }
523
+ });
538
524
  ```
539
525
 
526
+ The plugin also provides a route that can be used for doing direct S3 uploads.
527
+ See the documentation of the plugin for more details, as well as the
528
+ [Roda](https://github.com/janko-m/shrine-example)/[Rails](https://github.com/erikdahlstrand/shrine-rails-example)
529
+ example app which demonstrates multiple uploads directly to S3.
530
+
540
531
  ## Background jobs
541
532
 
542
- Shrine is the first uploading library designed from day one to be used with
543
- background jobs. Backgrounding parts of file upload is essential for scaling
544
- and good user experience, and Shrine provides a `backgrounding` plugin which
545
- makes it really easy to plug in your backgrounding library:
533
+ Shrine is the first file upload library designed for backgrounding support.
534
+ Moving phases of managing attachments to background jobs is essential for
535
+ scaling and good user experience, and Shrine provides a backgrounding plugin
536
+ which makes it really easy to plug in your favourite backgrounding library:
546
537
 
547
538
  ```rb
548
539
  Shrine.plugin :backgrounding
@@ -567,8 +558,8 @@ end
567
558
  ```
568
559
 
569
560
  The above puts all promoting (moving to store) and deleting of files into a
570
- background Sidekiq job. Obviously instead of Sidekiq you can just as well use
571
- any other backgrounding library.
561
+ background Sidekiq job. Obviously instead of Sidekiq you can use any other
562
+ backgrounding library.
572
563
 
573
564
  The main advantages of Shrine's backgrounding support over other file upload
574
565
  libraries are:
@@ -583,14 +574,25 @@ libraries are:
583
574
  * **Generality** – The above solution will automatically work for all uploaders,
584
575
  types of files and models.
585
576
  * **Safety** – All of Shrine's code has been designed to take delayed storing
586
- into account, so concurrency issues should be nonexistent.
577
+ into account, and concurrent requests are handled well.
578
+
579
+ ## Clearing cache
580
+
581
+ You will want to periodically clean your temporary storage. Amazon S3 provides
582
+ [a built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
583
+ and for FileSystem you can put something like this in your Rake task:
584
+
585
+ ```rb
586
+ file_system = Shrine.storages[:cache]
587
+ file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
588
+ ```
587
589
 
588
590
  ## Plugins
589
591
 
590
592
  Shrine comes with a small core which provides only the essential functionality,
591
- and all additional features are available via plugins. This way you can choose
592
- exactly how much Shrine does for you. Shrine itself [ships with over 35
593
- plugins], most of which I haven't managed to cover here.
593
+ and any additional features are available via plugins. This way you can choose
594
+ exactly what and how much Shrine does for you. Shrine itself [ships with over
595
+ 35 plugins], most of which I didn't cover here.
594
596
 
595
597
  The plugin system respects inheritance, so you can choose which plugins will
596
598
  be applied to which uploaders: