shrine 2.4.1 → 2.5.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93c5aa709ec2574b80b2a5d3b62f1f6d5b2cd312
4
- data.tar.gz: f6f13f2f4b24c6961a846634462c9d1aa2c16e7e
3
+ metadata.gz: 9bfafd65b7f7fe8b99b1c5f93ef378bfc3dc7eac
4
+ data.tar.gz: 698ed63662d4394c346f5ae2946e892de53d694e
5
5
  SHA512:
6
- metadata.gz: 712eaa97b81d90b928b0e7967740d79e0f4320af5b67a811e8a991f0e91e103568f70cc08683aa5e38fd3235021869a5c8634f08d67d961971edacc0aa9cb5b9
7
- data.tar.gz: fa531d81b429dbd94cfab836f50180aa6eed61e33eceaffe0b4d901398228cedfb19d9add18a0ebdb5c131d803b6c072a8e6972b9ea1c9cd9095f4da6e0f401f
6
+ metadata.gz: 2fcf2377c360c399342238366a1458f48f078d7b946b52af09b3e9acdbc8761e7b07270700f6a9b09b4195a43536947d6fc145da10c75667f88bd95dd9284edf
7
+ data.tar.gz: f293b1ba574687eae1edc8abad8fbdfbb2db2a4028470964f612ccf51a9e74a8dd810caf2e8dc6c784b9136fc6503ac168175d955404b923def1fd89eafc1a61
data/README.md CHANGED
@@ -18,6 +18,7 @@ Add Shrine to the Gemfile and write an initializer which sets up the storage and
18
18
  loads the ORM plugin:
19
19
 
20
20
  ```rb
21
+ # Gemfile
21
22
  gem "shrine"
22
23
  ```
23
24
 
@@ -32,6 +33,7 @@ Shrine.storages = {
32
33
 
33
34
  Shrine.plugin :sequel # :activerecord
34
35
  Shrine.plugin :cached_attachment_data # for forms
36
+ Shrine.plugin :rack_file # for non-Rails apps
35
37
  ```
36
38
 
37
39
  Next decide how you will name the attachment attribute on your model, and run a
@@ -47,7 +49,8 @@ end # end
47
49
  ```
48
50
 
49
51
  Now you can create an uploader class for the type of files you want to upload,
50
- and add the attachment attribute to your model which will accept files:
52
+ and add a virtual attribute for handling attachments using this uploader to
53
+ your model:
51
54
 
52
55
  ```rb
53
56
  class ImageUploader < Shrine
@@ -57,15 +60,13 @@ end
57
60
 
58
61
  ```rb
59
62
  class Photo < Sequel::Model # ActiveRecord::Base
60
- include ImageUploader[:image]
63
+ include ImageUploader[:image] # adds an `image` virtual attribute
61
64
  end
62
65
  ```
63
66
 
64
- Let's now add the form fields needed for attaching files. We need a file
65
- field for choosing files, and a hidden field for retaining the uploaded file
66
- in case of validation errors and [direct uploads]. Note that the file field
67
- needs to go *after* the hidden field, so that attaching a new file can always
68
- override whatever is in the hidden field.
67
+ Let's now add the form fields which will use this virtual attribute. We need
68
+ (1) a file field for choosing files, and (2) a hidden field for retaining the
69
+ uploaded file in case of validation errors and [direct uploads].
69
70
 
70
71
  ```erb
71
72
  <form action="/photos" method="post" enctype="multipart/form-data">
@@ -80,13 +81,14 @@ override whatever is in the hidden field.
80
81
  <% end %>
81
82
  ```
82
83
 
83
- Note the `enctype="multipart/form-data"` HTML attribute, which is required for
84
- submitting files through the form. The Rails form builder will automatically
85
- generate it for you when you add a file field.
84
+ Note that the file field needs to go *after* the hidden field, so that
85
+ selecting a new file can always override the cached file in the hidden field.
86
+ Also notice the `enctype="multipart/form-data"` HTML attribute, which is
87
+ required for submitting files through the form, though the Rails form builder
88
+ will automatically generate it for you.
86
89
 
87
90
  Now in your router/controller the attachment request parameter can be assigned
88
- to the model like any other attribute. Note that for non-Rails apps you will
89
- need to load the `rack_file` plugin which handles Rack's uploaded file hash.
91
+ to the model like any other attribute:
90
92
 
91
93
  ```rb
92
94
  post "/photos" do
@@ -95,65 +97,217 @@ post "/photos" do
95
97
  end
96
98
  ```
97
99
 
98
- Finally, you can use the URL of the attached file to display it:
100
+ Once a file is uploaded and attached to the record, you can retrieve a URL to
101
+ the uploaded file and display it:
99
102
 
100
103
  ```erb
101
104
  <img src="<%= @photo.image_url %>">
102
105
  ```
103
106
 
104
- ## Attachment
107
+ ## Storage
105
108
 
106
- When we assign an IO object to the record, Shrine will upload it to the
107
- registered `:cache` storage, which acts as a temporary storage, and write the
108
- location, storage, and metadata of the uploaded file to a single
109
- `<attachment>_data` column:
109
+ A "storage" in Shrine is an object responsible for managing files on a specific
110
+ storage service (filesystem, Amazon S3 etc), which implements a generic method
111
+ interface. Storages are configured directly and registered under a name in
112
+ `Shrine.storages`, so that they can be later used by uploaders.
110
113
 
111
114
  ```rb
112
- photo = Photo.new
113
- photo.image = File.open("waterfall.jpg")
114
- photo.image_data #=> '{"storage":"cache","id":"9260ea09d8effd.jpg","metadata":{...}}'
115
+ # Gemfile
116
+ gem "aws-sdk", "~> 2.1" # for Amazon S3 storage
117
+ ```
118
+ ```rb
119
+ require "shrine/storage/s3"
120
+
121
+ s3_options = {
122
+ access_key_id: "abc",
123
+ secret_access_key: "xyz",
124
+ region: "my-region",
125
+ bucket: "my-bucket",
126
+ }
115
127
 
116
- photo.image #=> #<Shrine::UploadedFile>
117
- photo.image_url #=> "/uploads/cache/9260ea09d8effd.jpg"
128
+ Shrine.storages = {
129
+ cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
130
+ store: Shrine::Storage::S3.new(prefix: "store", **s3_options),
131
+ }
118
132
  ```
119
133
 
120
- The Shrine attachment module added the following methods to the `Photo` model:
134
+ The above example sets up Amazon S3 storage both for temporary and permanent
135
+ storage, which allows for [direct uploads]. The `:cache` and `:store` names are
136
+ special only in terms that the attacher will automatically pick them up, but
137
+ you can also register more than two storages under different names.
121
138
 
122
- * `#image=` caches the file and saves the result into `image_data`
123
- * `#image` returns `Shrine::UploadedFile` instantiated from `image_data`
124
- * `#image_url` calls `image.url` if attachment is present, otherwise returns nil
125
- * `#image_attacher` – returns instance of `Shrine::Attacher` which handles attaching
139
+ Shrine ships with [FileSystem] and [S3] storage, take a look at their
140
+ documentation for more details on various features they support. There are also
141
+ [many more Shrine storages][external storages] shipping as external gems.
126
142
 
127
- In addition to assigning new files, you can also assign already cached files
128
- using their JSON representation:
143
+ ## Uploader
144
+
145
+ Uploaders are subclasses of `Shrine`, and are essentially wrappers around
146
+ storages. In addition to actually calling the underlying storage when they need
147
+ to, they also perform many generic tasks which aren't related to a particular
148
+ storage (like processing, extracting metadata, logging etc).
129
149
 
130
150
  ```rb
131
- photo.image = '{
132
- "storage": "cache",
133
- "id": "9260ea09d8effd.jpg",
134
- "metadata": { ... }
135
- }'
151
+ class ImageUploader < Shrine
152
+ # image attachent logic
153
+ end
154
+ ```
155
+ ```rb
156
+ uploader = ImageUploader.new(:store)
157
+ uploader #=> uploader for storage registered under `:store`
158
+ ```
159
+
160
+ It's common to create an uploader for each type of file that you want to handle
161
+ (image, video, audio, document etc), but you can structure them any way that
162
+ you like.
163
+
164
+ ### Uploading
165
+
166
+ The main method of the uploader is `#upload`, which takes an IO-like object on
167
+ the input, and returns a representation of the uploaded file on the output.
168
+
169
+ ```rb
170
+ uploaded_file = uploader.upload(file)
171
+ uploaded_file #=> #<Shrine::UploadedFile>
172
+ ```
173
+
174
+ Some of the tasks performed by `#upload` include:
175
+
176
+ * file processing (if defined)
177
+ * extracting metadata
178
+ * generating location
179
+ * uploading (this is where the storage is called)
180
+ * closing the file
181
+
182
+ ### IO abstraction
183
+
184
+ Shrine is able to upload any IO-like object that respond to `#read`, `#size`,
185
+ `#rewind`, `#eof?` and `#close`. This foremost includes all real IO objects
186
+ like File, Tempfile and StringIO.
187
+
188
+ When a file is uploaded to a Rails app, it will be represented by an
189
+ ActionDispatch::Http::UploadedFile object in the params. This is also an
190
+ IO-like object accepted by Shrine. In other Rack applications the uploaded file
191
+ will be represented as a Hash, but it can still be attached when [`rack_file`]
192
+ plugin is loaded.
193
+
194
+ Finally, the `Shrine::UploadedFile` object, returned by uploading, is itself an
195
+ IO-like object. This makes it incredibly easy to reupload a file from one
196
+ storage to another, and this is used by the attacher to reupload a file stored
197
+ on temporary storage to permanent storage.
198
+
199
+ ### Deleting
200
+
201
+ The uploader can also delete uploaded files via `#delete`. Internally this just
202
+ delegates to the uploaded file, but some plugins bring additional behaviour
203
+ (e.g. logging).
204
+
205
+ ```rb
206
+ uploaded_file = uploader.upload(file)
207
+ # ...
208
+ uploader.delete(uploaded_file)
209
+ ```
210
+
211
+ ## Uploaded file
212
+
213
+ The `Shrine::UploadedFile` object represents the file that was uploaded to the
214
+ storage. It contains the following information:
215
+
216
+ * `storage` – identifier of the storage the file was uploaded to
217
+ * `id` – the location of the file on the storage
218
+ * `metadata` – file metadata that was extracted during upload
219
+
220
+ ```rb
221
+ uploaded_file = uploader.upload(file)
222
+
223
+ uploaded_file.id #=> "949sdjg834.jpg"
224
+ uploaded_file.storage #=> #<Shrine::Storage::FileSystem>
225
+ uploaded_file.metadata #=> {...}
226
+
227
+ # It can be serialized into JSON and saved to a database column
228
+ uploaded_file.to_json #=> '{"id":"949sdjg834.jpg","storage":"store","metadata":{...}}'
229
+ ```
230
+
231
+ It comes with many convenient methods that delegate to the storage:
232
+
233
+ ```rb
234
+ uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/949sdjg834.jpg"
235
+ uploaded_file.download #=> #<Tempfile>
236
+ uploaded_file.exists? #=> true
237
+ uploaded_file.open { |io| io.read }
238
+ uploaded_file.delete
136
239
  ```
137
240
 
138
- This allows Shrine to retain uploaded files in case of validation errors, and
139
- handle [direct uploads], via the hidden form field.
241
+ It also implements the IO-like interface that conforms to Shrine's IO
242
+ abstraction, which allows it to be uploaded to other storages.
243
+
244
+ ```rb
245
+ uploaded_file.read # returns content of the uploaded file
246
+ uploaded_file.eof? # returns true if the whole IO was read
247
+ uploaded_file.rewind # rewinds the IO
248
+ uploaded_file.close # closes the IO
249
+ ```
250
+
251
+ ## Attachment
252
+
253
+ Storages, uploaders, and uploaded file objects are the main components for
254
+ managing files. Since most often you also want to *attach* the uploaded files
255
+ to database records, Shrine comes with a high-level attachment interface, which
256
+ uses these components internally.
257
+
258
+ Usually you're using an ORM for saving database records, in which case you can
259
+ load an additional plugin to automatically tie the attached files to record
260
+ lifecycle. But you can also use Shrine just with plain models.
261
+
262
+ ```rb
263
+ Shrine.plugin :sequel # :activerecord
264
+ ```
265
+
266
+ ```rb
267
+ class Photo < Sequel::Model # ActiveRecord::Base
268
+ include ImageUploader[:image] #
269
+ include ImageUploader.attachment(:image) # these are all equivalent
270
+ include ImageUploader::Attachment.new(:image) #
271
+ end
272
+ ```
273
+
274
+ You can choose whichever of these three syntaxes you prefer. In any case this
275
+ will create a `Shrine::Attachment` module with attachment methods for the
276
+ specified attribute, which then get added to your model when you include it:
277
+
278
+ * `#image=` – uploads the file to temporary storage and serializes the result into `image_data`
279
+ * `#image` – returns `Shrine::UploadedFile` instantiated from `image_data`
280
+ * `#image_url` – calls `url` on the attachment if it's present, otherwise returns nil
281
+ * `#image_attacher` – returns instance of `Shrine::Attacher` which handles the attaching
140
282
 
141
283
  The ORM plugin that we loaded adds appropriate callbacks, so when record is
142
- saved the attachment is uploaded to permanent storge (`:store`), and when
143
- record is destroyed the attachment is destroyed as well:
284
+ saved the attachment is uploaded to permanent storage, and when record is
285
+ deleted the attachment is deleted as well.
144
286
 
145
287
  ```rb
288
+ # no file is attached
289
+ photo.image #=> nil
290
+
291
+ # the assigned file is cached to temporary storage and written to `image_data` column
146
292
  photo.image = File.open("waterfall.jpg")
147
- photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
293
+ photo.image #=> #<Shrine::UploadedFile @data={...}>
294
+ photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
295
+ photo.image_data #=> '{"storage":"cache","id":"0sdfllasfi842.jpg","metadata":{...}}'
148
296
 
297
+ # the cached file is promoted to permanent storage and saved to `image_data` column
149
298
  photo.save
150
- photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
299
+ photo.image #=> #<Shrine::UploadedFile @data={...}>
300
+ photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
301
+ photo.image_data #=> '{"storage":"store","id":"l02kladf8jlda.jpg","metadata":{...}}'
151
302
 
303
+ # the attached file is deleted with the record
152
304
  photo.destroy
153
305
  photo.image.exists? #=> false
154
306
  ```
155
307
 
156
- The ORM plugin will also delete replaced attachments:
308
+ If there is already a file attached, and the attachment is overriden (either
309
+ with a new file or no file), the previous attachment will get deleted when the
310
+ record gets saved.
157
311
 
158
312
  ```rb
159
313
  photo.update(image: new_file) # changes the attachment and deletes previous
@@ -161,138 +315,148 @@ photo.update(image: new_file) # changes the attachment and deletes previous
161
315
  photo.update(image: nil) # removes the attachment and deletes previous
162
316
  ```
163
317
 
164
- In all these examples we used `image` as the name of the attachment, but we can
165
- create attachment modules for any kind of attachments:
318
+ In addition to assigning raw files, you can also assign a JSON representation
319
+ of files that are already uploaded to the temporary storage. This allows Shrine
320
+ to retain cached files in case of validation errors, and handle [direct
321
+ uploads], via the hidden form field.
166
322
 
167
323
  ```rb
168
- class VideoUploader < Shrine
169
- # video attachment logic
170
- end
171
- ```
172
- ```rb
173
- class Movie < Sequel::Model
174
- include VideoUploader[:video] # uses "video_data" column
175
- end
324
+ photo.image = '{
325
+ "storage": "cache",
326
+ "id": "9260ea09d8effd.jpg",
327
+ "metadata": { ... }
328
+ }'
176
329
  ```
177
330
 
178
- ### Attacher
331
+ ## Attacher
179
332
 
180
- The model attachment interface under-the-hood just delegates to a
181
- `Shrine::Attacher` object. If whether you don't want to add additional methods
182
- to your model, prefer explicitness over callbacks, or use Shrine with custom
183
- models, you can use `Shrine::Attacher` directly:
333
+ The model attachment attributes and callbacks just delegate the behaviour
334
+ to a `Shrine::Attacher` object.
184
335
 
185
336
  ```rb
186
- attacher = ImageUploader::Attacher.new(photo, :image) # equivalent to `photo.image_attacher`
187
-
188
- attacher.cache #=> #<ImageUploader @storage_key=:cache>
189
- attacher.store #=> #<ImageUploader @storage_key=:store>
337
+ attacher = ImageUploader::Attacher.new(photo, :image) # returned by `photo.image_attacher`
190
338
 
191
339
  attacher.assign(file) # equivalent to `photo.image = file`
192
340
  attacher.get # equivalent to `photo.image`
193
341
  attacher.url # equivalent to `photo.image_url`
194
-
195
- attacher.finalize # promotes cached file to store, deletes old attachment (after save callback)
196
- attacher.destroy # deletes attachment (after destory callback)
197
342
  ```
198
343
 
199
- See [Using Attacher] guide for more details.
344
+ The attacher is what drives attaching files to models, and it functions
345
+ independently from models' attachment interface. This means that you can use it
346
+ as an alternative, in case you prefer not to add additional attributes to the
347
+ model, or prefer explicitness over callbacks. It's also useful when you need
348
+ something more advanced which isn't available through the attachment
349
+ attributes.
200
350
 
201
- ### Multiple files
351
+ Whenever the attacher uploads or deletes files, it sends a `context` hash
352
+ which includes `:record`, `:name`, and `:action` keys, so that you can perform
353
+ processing or generate location differently depending on this information. See
354
+ [Context] section for more details.
202
355
 
203
- Sometimes we want to allow users to upload multiple files at once. This can be
204
- achieved with by adding a `multiple` HTML attribute to the file field: `<input
205
- type="file" multiple>`.
356
+ For more information about `Shrine::Attacher`, see [Using Attacher] guide.
206
357
 
207
- Shrine doesn't accept multiple files on single a attachment attribute, but you
208
- can instead attach each file to a separate database record, which is a much
209
- more flexible solution.
358
+ ## Plugin system
210
359
 
211
- The best way is to [directly upload][direct uploads] selected files, and then
212
- send the data of uploaded files as nested attributes for associated records.
213
- Alternatively you can send all selected files at once, and then transform them
214
- into nested association attributes in the controller.
360
+ By default Shrine comes with a small core which provides only the essential
361
+ functionality. All additional features are available via [plugins], which also
362
+ ship with Shrine. This way you can choose exactly what and how much Shrine does
363
+ for you, and you load the code only for features that you use.
215
364
 
216
- ## Uploader
365
+ ```rb
366
+ Shrine.plugin :logging # adds logging
367
+ ```
217
368
 
218
- "Uploaders" are subclasses of `Shrine`, and this is where we define all our
219
- attachment logic. Uploader objects act as a wrappers around a storage; they
220
- don't know anything about models, and are stateless.
369
+ Plugins add behaviour by extending Shrine core classes via module inclusion, and
370
+ many of them also accept configuration options. The plugin system respects
371
+ inheritance, so you can choose to load a plugin globally or per uploader.
221
372
 
222
373
  ```rb
223
- class DocumentUploader < Shrine
224
- # document uploading logic
374
+ class ImageUploader < Shrine
375
+ plugin :store_dimensions # extract image dimensions only for this uploader and its descendants
225
376
  end
226
377
  ```
378
+
379
+ ## Metadata
380
+
381
+ Shrine automatically extracts available file metadata and saves them to the
382
+ `Shrine::UploadedFile`. You can access them through the `#metadata` hash or via
383
+ metadata methods:
384
+
227
385
  ```rb
228
- uploader = DocumentUploader.new(:store)
229
- uploaded_file = uploader.upload(File.open("resume.pdf"))
230
- uploaded_file #=> #<Shrine::UploadedFile>
231
- uploaded_file.to_json #=> '{"storage":"store","id":"0sdfllasfi842.pdf","metadata":{...}}'
232
- ```
386
+ uploaded_file.metadata #=>
387
+ # {
388
+ # "filename" => "matrix.mp4",
389
+ # "mime_type" => "video/mp4",
390
+ # "size" => 345993,
391
+ # }
233
392
 
234
- The `Shrine#upload` method does the following:
393
+ uploaded_file.original_filename #=> "matrix.mp4"
394
+ uploaded_file.extension #=> "mp4"
395
+ uploaded_file.mime_type #=> "video/mp4"
396
+ uploaded_file.size #=> 345993
397
+ ```
235
398
 
236
- * calls processing
237
- * extracts metadata
238
- * generates unique location
239
- * uploads file(s) (this is where the storage is called)
240
- * closes uploaded file(s)
399
+ ### MIME type
241
400
 
242
- Shrine requires the input for uploading to be an IO-like object. So, `File`,
243
- `Tempfile` and `StringIO` instances are all valid inputs. The object doesn't
244
- have to be an actual IO, it's enough that it responds to: `#read(*args)`,
245
- `#size`, `#eof?`, `#rewind` and `#close`. `ActionDispatch::Http::UploadedFile`
246
- is one such object, as well as `Shrine::UploadedFile` itself.
401
+ By default "mime_type" will be inherited from `#content_type` of the uploaded
402
+ file, which is set from the "Content-Type" request header, but this header is
403
+ determined by the browser solely based on the file extension. This means that
404
+ by default Shrine's "mime_type" is **not guaranteed** to hold the actual MIME
405
+ type of the file.
247
406
 
248
- The result of uploading is a `Shrine::UploadedFile` object, which represents
249
- the uploaded file on the storage, and is defined by its underlying data hash.
407
+ However, if you load the `determine_mime_type` plugin, that will make Shrine
408
+ always extract the MIME type from **file content** .
250
409
 
251
410
  ```rb
252
- uploaded_file.url #=> "uploads/938kjsdf932.mp4"
253
- uploaded_file.metadata #=> {...}
254
- uploaded_file.download #=> #<Tempfile:/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/20151004-74201-1t2jacf.mp4>
255
- uploaded_file.exists? #=> true
256
- uploaded_file.open { |io| io.read }
257
- uploaded_file.delete
258
- # ...
411
+ Shrine.plugin :determine_mime_type
259
412
  ```
260
-
261
- This is the same object that is returned when we access the attachment through
262
- the record:
263
-
264
413
  ```rb
265
- photo.image #=> #<Shrine::UploadedFile>
414
+ File.write("image.png", "<?php ... ?>") # PHP file with a .png extension
415
+ photo = Photo.create(image: File.open("image.png"))
416
+ photo.image.mime_type #=> "text/x-php"
266
417
  ```
267
418
 
268
- ### Plugins
419
+ By the default the UNIX [`file`] utility is used, but you can also choose a
420
+ different analyzer, see plugin's documentation for more details.
269
421
 
270
- Shrine comes with a small core which provides only the essential functionality,
271
- and any additional features are available via plugins. This way you can choose
272
- exactly what and how much Shrine does for you. See the [website] for a complete
273
- list of plugins.
422
+ ### Custom metadata
274
423
 
275
- The plugin system respects inheritance, so you can choose to load a plugin
276
- globally or only for a specific uploader.
424
+ In addition to the built-in metadata, you can also extract and store completely
425
+ custom metadata with the `add_metadata` plugin. For example, if we're uploading
426
+ videos, we could store additional video-specific metadata:
277
427
 
278
428
  ```rb
279
- Shrine.plugin :logging # enables logging for all uploaders
429
+ require "streamio-ffmpeg"
280
430
 
281
- class ImageUploader < Shrine
282
- plugin :backup # stores backups only for this uploader and its descendants
431
+ class VideoUploader < Shrine
432
+ plugin :add_metadata
433
+
434
+ add_metadata do |io, context|
435
+ movie = FFMPEG::Movie.new(io.path)
436
+
437
+ { "duration" => movie.duration,
438
+ "bitrate" => movie.bitrate,
439
+ "resolution" => movie.resolution,
440
+ "frame_rate" => movie.frame_rate }
441
+ end
283
442
  end
284
443
  ```
444
+ ```rb
445
+ video.metadata["duration"] #=> 7.5
446
+ video.metadata["bitrate"] #=> 481
447
+ video.metadata["resolution"] #=> "640x480"
448
+ video.metadata["frame_rate"] #=> 16.72
449
+ ```
285
450
 
286
451
  ## Processing
287
452
 
288
- Shrine allows you to perform file processing in functional style; you receive
289
- the original file as the input, and return processed files as the output.
453
+ You can have Shrine perform file processing before uploading to storage. It's
454
+ generally best to process files prior to uploading to permanent storage,
455
+ because at that point the selected file has been succesfully validated, and
456
+ this part can be moved into a background job.
290
457
 
291
- Processing can be performed whenever a file is uploaded. On attaching this
292
- happens twice; first the raw file is cached to temporary storage ("cache"
293
- action), then when the record is saved the cached file is "promoted" to
294
- permanent storage ("store" action). We generally want to process on the "store"
295
- action, because it happens after file validations and can be backgrounded.
458
+ This promote phase is called `:store`, and we can use the `processing` plugin
459
+ to define processing for that phase:
296
460
 
297
461
  ```rb
298
462
  class ImageUploader < Shrine
@@ -304,11 +468,16 @@ class ImageUploader < Shrine
304
468
  end
305
469
  ```
306
470
 
307
- Ok, now how do we do the actual processing? Well, Shrine actually doesn't ship
471
+ Now, how do we do the actual processing? Well, Shrine actually doesn't ship
308
472
  with any file processing functionality, because that is a generic problem that
309
473
  belongs in separate libraries. If the type of files you're uploading are
310
474
  images, I created the [image_processing] gem which you can use with Shrine:
311
475
 
476
+ ```rb
477
+ # Gemfile
478
+ gem "image_processing"
479
+ gem "mini_magick", ">= 4.3.5"
480
+ ```
312
481
  ```rb
313
482
  require "image_processing/mini_magick"
314
483
 
@@ -317,23 +486,31 @@ class ImageUploader < Shrine
317
486
  plugin :processing
318
487
 
319
488
  process(:store) do |io, context|
320
- resize_to_limit(io.download, 700, 700)
489
+ resize_to_limit!(io.download, 800, 800)
321
490
  end
322
491
  end
323
492
  ```
324
493
 
325
494
  Here the `io` is a cached `Shrine::UploadedFile`, so we need to download it to
326
- a `File`, since this is what image_processing gem recognizes.
495
+ a file, since file processing tools usually work with files on the filesystem.
496
+
497
+ Shrine treats file processing as a functional transformation; you are given the
498
+ original file, and how you're going to perform processing is entirely up to
499
+ you, you only need to return the processed files at the end of the block. Then
500
+ instead of uploading the original file, Shrine will continue to upload the
501
+ files that the processing block returned.
327
502
 
328
503
  ### Versions
329
504
 
330
505
  Sometimes we want to generate multiple files as the result of processing. If
331
506
  we're uploading images, we might want to store various thumbnails alongside the
332
- original image. If we're uploading videos, we might want to save a screenshot
333
- or transcode it into different formats.
507
+ original image. If we're uploading videos, we might want to save screenshots
508
+ or transcode the video into different formats.
334
509
 
335
- To save multiple files, we just need to load the `versions` plugin, and then in
336
- `#process` we can return a Hash of files:
510
+ To be able to save multiple files, we just need to load the `versions` plugin,
511
+ and then in processing block we can return a Hash of files. It is recommended
512
+ to also load the `delete_raw` plugin, so that processed files are automatically
513
+ deleted after uploading.
337
514
 
338
515
  ```rb
339
516
  require "image_processing/mini_magick"
@@ -341,45 +518,60 @@ require "image_processing/mini_magick"
341
518
  class ImageUploader < Shrine
342
519
  include ImageProcessing::MiniMagick
343
520
  plugin :processing
344
- plugin :versions
521
+ plugin :versions # enable Shrine to handle a hash of files
522
+ plugin :delete_raw # delete processed files after uploading
345
523
 
346
524
  process(:store) do |io, context|
347
- size_700 = resize_to_limit(io.download, 700, 700)
348
- size_500 = resize_to_limit(size_700, 500, 500)
349
- size_300 = resize_to_limit(size_500, 300, 300)
525
+ original = io.download
526
+
527
+ size_800 = resize_to_limit!(original, 800, 800)
528
+ size_500 = resize_to_limit(size_800, 500, 500)
529
+ size_300 = resize_to_limit(size_500, 300, 300)
350
530
 
351
- {large: size_700, medium: size_500, small: size_300}
531
+ {original: io, large: size_800, medium: size_500, small: size_300}
352
532
  end
353
533
  end
354
534
  ```
355
535
 
356
- By defining processing on instance-level Shrine gives you a lot of flexibility.
357
- You could choose the processing order which yields the best performance, even
358
- add parallelization, and when processing logic gets complex you could extract
359
- everything into a PORO class. It is recommended to load the `delete_raw` plugin
360
- so that processed files are automatically deleted after uploading.
361
-
362
- Each version will be saved to the attachment column, and the attachment getter
363
- will simply return a Hash of `Shrine::UploadedFile` objects:
536
+ After these files have been uploaded, their data will all be saved to the
537
+ `<attachment>_data` column. The attachment getter will then read them as a Hash
538
+ of `Shrine::UploadedFile` objects.
364
539
 
365
540
  ```rb
366
- photo.image #=> {large: ..., medium: ..., small: ...}
541
+ photo.image_data #=>
542
+ # '{
543
+ # "original": {"id":"9sd84.jpg", "storage":"store", "metadata":{...}},
544
+ # "large": {"id":"lg043.jpg", "storage":"store", "metadata":{...}},
545
+ # "medium": {"id":"kd9fk.jpg", "storage":"store", "metadata":{...}},
546
+ # "small": {"id":"932fl.jpg", "storage":"store", "metadata":{...}}
547
+ # }'
548
+
549
+ photo.image #=>
550
+ # {
551
+ # :original => #<Shrine::UploadedFile @data={"id"=>"9sd84.jpg", ...}>,
552
+ # :large => #<Shrine::UploadedFile @data={"id"=>"lg043.jpg", ...}>,
553
+ # :medium => #<Shrine::UploadedFile @data={"id"=>"kd9fk.jpg", ...}>,
554
+ # :small => #<Shrine::UploadedFile @data={"id"=>"932fl.jpg", ...}>,
555
+ # }
556
+
557
+ photo.image[:medium] #=> #<Shrine::UploadedFile>
558
+ photo.image[:medium].url #=> "/uploads/store/lg043.jpg"
559
+ photo.image[:medium].size #=> 5825949
560
+ photo.image[:medium].mime_type #=> "image/jpeg"
561
+ ```
367
562
 
368
- # With the store_dimensions plugin (requires fastimage gem)
369
- photo.image[:large].width #=> 700
370
- photo.image[:medium].width #=> 500
371
- photo.image[:small].width #=> 300
563
+ The `versions` plugin also expands `#<attachment>_url` to accept version names:
372
564
 
373
- # The plugin expands this method to accept version names.
565
+ ```rb
374
566
  photo.image_url(:large) #=> "..."
375
567
  ```
376
568
 
377
569
  ### Custom processing
378
570
 
379
571
  Your processing tool doesn't have to be in any way designed for Shrine
380
- ([image_processing] is a generic library), you only need to return processed
381
- files as IO objects, e.g. `File` objects. Here's an example of processing a
382
- video with [ffmpeg]:
572
+ ([image_processing] that we saw earlier is a generic library), the only thing
573
+ that you need to do is return processed files as some kind of IO objects. Here
574
+ is an example of transcoding a video using [ffmpeg]:
383
575
 
384
576
  ```rb
385
577
  require "streamio-ffmpeg"
@@ -387,6 +579,7 @@ require "streamio-ffmpeg"
387
579
  class VideoUploader < Shrine
388
580
  plugin :processing
389
581
  plugin :versions
582
+ plugin :delete_raw
390
583
 
391
584
  process(:store) do |io, context|
392
585
  mov = io.download
@@ -404,286 +597,136 @@ class VideoUploader < Shrine
404
597
  end
405
598
  ```
406
599
 
407
- ### Triggering processing
408
-
409
- Whenever a file is uploaded by the attacher, an additional `:action` parameter
410
- is added to the context, which holds a symbol name describing what the file was
411
- uploaded for. For example, for caching files `action: :cache` will be sent, for
412
- promoting `action: :store`, while for backing up attacher sends `action:
413
- :backup`.
414
-
415
- The argument to the `process` declaration is the name of that action. When
416
- uploading via the uploader, you can add `:action` option with the value of the
417
- processing block you want performed before uploading.
418
-
419
- ```rb
420
- uploader = ImageUploader.new(:store)
421
- uploader.upload(file, action: :store) # performs processing defined under ":store"
422
- ```
423
-
424
- You can also define and call processing for a custom action:
425
-
426
- ```rb
427
- class ImageUploader < Shrine
428
- process(:my_action) { |io, context| special_processing(io) }
429
- end
430
- ```
431
- ```rb
432
- uploader.upload(file, action: :my_action)
433
- ```
600
+ ## Context
434
601
 
435
- Finally, you can also call defined processing directly, without uploading the
436
- results, using `Shrine#process`.
602
+ The `#upload` (and `#delete`) methods accept a hash of options as the second
603
+ argument, which is forwarded to all other tasks like processing, extracting
604
+ metadata and generating location.
437
605
 
438
606
  ```rb
439
- uploader.process(file, action: :my_action) # returns processed files without uploading
607
+ uploader.upload(file, {foo: "bar"}) # context hash is forwarded to all tasks around upload
440
608
  ```
441
609
 
442
- ## Context
443
-
444
- You may have noticed the `context` variable floating around as the second
445
- argument for processing. This variable is present all the way from input file
446
- to uploaded file, and can contain useful information depending on the situation:
610
+ Some options are actually recognized by Shrine, like `:location` and
611
+ `:upload_options`, and some are added by plugins. However, most options are
612
+ there just to provide you context, for more flexibility in performing tasks and
613
+ better logging.
447
614
 
448
- * `context[:record]` -- the model instance
449
- * `context[:name]` -- attachment name on the model
450
- * `context[:action]` -- identifier for the action being performed (`:cache`, `:store`, `:recache`, `:backup`, ...)
451
- * `context[:version]` -- version name of the IO in the argument
452
- * ...
615
+ The attacher automatically includes additional `context` information for each
616
+ upload and delete:
453
617
 
454
- The `context` is useful for doing conditional processing, validation,
455
- generating location etc, and it is also used by some plugins internally.
618
+ * `context[:record]` model instance where the file is attached
619
+ * `context[:name]` name of the attachment attribute on the model
620
+ * `context[:action]` – identifier for the action being performed (`:cache`, `:store`, `:recache`, `:backup`, ...)
456
621
 
457
622
  ```rb
458
623
  class VideoUploader < Shrine
459
624
  process(:store) do |io, context|
460
- trim_video(io, 300) if context[:record].guest?
625
+ trim_video(io, 300) if context[:record].user.free_plan?
461
626
  end
462
627
  end
463
628
  ```
464
629
 
465
- The context is just a hash that is passed to the uploader methods. If you're
466
- using the uploader directly, you can pass the context directly:
467
-
468
- ```rb
469
- uploader.upload(file, {foo: "bar"}) # passing context hash directly
470
- ```
471
-
472
- ## Metadata
473
-
474
- Shrine automatically extracts and stores available file metadata:
475
-
476
- ```rb
477
- photo = Photo.create(image: image)
478
-
479
- photo.image.metadata #=>
480
- # {
481
- # "filename" => "nature.jpg",
482
- # "mime_type" => "image/jpeg",
483
- # "size" => 345993,
484
- # }
485
-
486
- photo.image.original_filename #=> "nature.jpg"
487
- photo.image.extension #=> "jpg"
488
- photo.image.mime_type #=> "image/jpeg"
489
- photo.image.size #=> 345993
490
- ```
491
-
492
- ### MIME type
493
-
494
- By default, "mime_type" is inherited from `#content_type` of the uploaded file,
495
- which is set from the "Content-Type" request header, which is determined by the
496
- browser solely based on the file extension. This means that by default Shrine's
497
- "mime_type" is *not* guaranteed to hold the actual MIME type of the file.
498
-
499
- To help with that Shrine provides the `determine_mime_type` plugin, which by
500
- default uses the UNIX [file] utility to determine the actual MIME type:
501
-
502
- ```rb
503
- Shrine.plugin :determine_mime_type
504
- ```
505
- ```rb
506
- File.write("image.jpg", "<?php ... ?>") # PHP file with a .jpg extension
507
- photo = Photo.create(image: File.open("image.jpg"))
508
- photo.image.mime_type #=> "text/x-php"
509
- ```
510
-
511
- ### Custom metadata
512
-
513
- You can also extract and store completely custom metadata with the
514
- `add_metadata` plugin:
515
-
516
- ```rb
517
- require "mini_magick"
518
-
519
- class ImageUploader < Shrine
520
- plugin :add_metadata
521
-
522
- add_metadata :exif do |io, context|
523
- MiniMagick::Image.new(io.path).exif
524
- end
525
- end
526
- ```
527
- ```rb
528
- photo.image.metadata["exif"]
529
- # or
530
- photo.image.exif
531
- ```
532
-
533
630
  ## Validation
534
631
 
535
- Validations are registered inside a `Attacher.validate` block, and you can load
536
- the `validation_helpers` plugin to get some convenient file validation methods:
632
+ Shrine can perform file validations for files assigned to the model. The
633
+ validations are registered inside a `Attacher.validate` block, and you can load
634
+ the `validation_helpers` plugin to get convenient file validation methods:
537
635
 
538
636
  ```rb
539
- class VideoUploader < Shrine
637
+ class DocumentUploader < Shrine
540
638
  plugin :validation_helpers
541
639
 
542
640
  Attacher.validate do
543
- validate_max_size 50*1024*1024, message: "is too large (max is 50 MB)"
544
- validate_mime_type_inclusion ["video/mp4"]
641
+ validate_max_size 5*1024*1024, message: "is too large (max is 5 MB)"
642
+ validate_mime_type_inclusion ["application/pdf"]
545
643
  end
546
644
  end
547
645
  ```
548
646
 
549
647
  ```rb
550
- trailer = Trailer.new
551
- trailer.video = File.open("matrix.mp4")
552
- trailer.valid? #=> false
553
- trailer.errors.to_hash #=> {video: ["is too large (max is 50 MB)"]}
648
+ user = User.new
649
+ user.cv = File.open("cv.pdf")
650
+ user.valid? #=> false
651
+ user.errors.to_hash #=> {cv: ["is too large (max is 5 MB)"]}
554
652
  ```
555
653
 
556
654
  You can also do custom validations:
557
655
 
558
656
  ```rb
559
- class VideoUploader < Shrine
657
+ class DocumentUploader < Shrine
560
658
  Attacher.validate do
561
- errors << "is longer than 5 minutes" if get.duration > 300
659
+ errors << "has more than 3 pages" if get.metadata["pages"] > 3
562
660
  end
563
661
  end
564
662
  ```
565
663
 
664
+ When file validations fail, Shrine will by default keep the invalid cached file
665
+ assigned to the model instance. If you want the invalid file to be deassigned,
666
+ you can load the `remove_invalid` plugin.
667
+
566
668
  The `Attacher.validate` block is executed in context of a `Shrine::Attacher`
567
669
  instance:
568
670
 
569
671
  ```rb
570
- class VideoUploader < Shrine
672
+ class DocumentUploader < Shrine
571
673
  Attacher.validate do
572
674
  self #=> #<Shrine::Attacher>
573
675
 
574
676
  get #=> #<Shrine::UploadedFile>
575
- record # the model instance
576
- errors # array of error messages for this file
677
+ record #=> #<User>
678
+ name #=> :cv
577
679
  end
578
680
  end
579
681
  ```
580
682
 
581
- ## Locations
582
-
583
- Before Shrine uploads a file, it generates a random location for it. By
584
- default the hierarchy is flat, all files are stored in the root of the storage.
585
- If you want that each attachment has its own directory, you can load the
586
- `pretty_location` plugin:
587
-
588
- ```rb
589
- Shrine.plugin :pretty_location
590
- ```
591
- ```rb
592
- photo = Photo.create(image: File.open("nature.jpg"))
593
- photo.image.id #=> "photo/34/image/34krtreds2df.jpg"
594
- ```
683
+ ## Location
595
684
 
596
- If you want to generate locations on your own, you can override
597
- `Shrine#generate_location`:
685
+ Before Shrine uploads a file, it generates a random location for it. By default
686
+ the hierarchy is flat; all files are stored in the root directory of the
687
+ storage. You can change how the location is generated by overriding
688
+ `#generate_location`:
598
689
 
599
690
  ```rb
600
691
  class ImageUploader < Shrine
601
692
  def generate_location(io, context)
602
- if context[:record]
603
- "#{context[:record].class}/#{super}"
604
- else
605
- super
606
- end
693
+ type = context[:record].class.name.downcase if context[:record]
694
+ style = context[:version] == :original ? "originals" : "thumbs" if context[:version]
695
+ name = super # the default unique identifier
696
+
697
+ [type, style, name].compact.join("/")
607
698
  end
608
699
  end
609
700
  ```
610
-
611
- Note that there should always be a random component in the location, so that
612
- dirty tracking is detected properly; you can use `Shrine#generate_uid`. Inside
613
- `#generate_location` you can access the extracted metadata through
614
- `context[:metadata]`.
615
-
616
- When using the uploader directly, it's possible to bypass `#generate_location`
617
- by passing a `:location`:
618
-
619
- ```rb
620
- uploader = MyUploader.new(:store)
621
- file = File.open("nature.jpg")
622
- uploader.upload(file, location: "some/specific/location.jpg")
623
- ```
624
-
625
- ## Storage
626
-
627
- "Storages" are objects which know how to manage files on a particular service.
628
- Other than [FileSystem], Shrine also ships with Amazon [S3] storage:
629
-
630
- ```rb
631
- gem "aws-sdk", "~> 2.1"
632
- ```
633
- ```rb
634
- require "shrine/storage/s3"
635
-
636
- Shrine.storages[:store] = Shrine::Storage::S3.new(
637
- access_key_id: "<ACCESS_KEY_ID>", # "xyz"
638
- secret_access_key: "<SECRET_ACCESS_KEY>", # "abc"
639
- region: "<REGION>", # "eu-west-1"
640
- bucket: "<BUCKET>", # "my-bucket"
641
- )
642
- ```
643
-
644
- ```rb
645
- photo = Photo.new(image: File.open("image.png"))
646
- photo.image_url #=> "/uploads/cache/j4k343ui12ls9.png"
647
- photo.save
648
- photo.image_url #=> "https://my-bucket.s3.amazonaws.com/0943sf8gfk13.png"
649
701
  ```
650
-
651
- Note that any options passed to `image_url` will be forwarded to the underlying
652
- storage, see the documentation of the storage that you're using for which URL
653
- options it supports.
654
-
655
- You can see the full documentation for [FileSystem] and [S3] storages. There
656
- are also many other Shrine storages available, see [External] section on the
657
- website.
658
-
659
- ### Upload options
660
-
661
- Many storages accept additional upload options, which you can pass via the
662
- `upload_options` plugin, or manually when uploading:
663
-
664
- ```rb
665
- uploader = MyUploader.new(:store)
666
- uploader.upload(file, upload_options: {acl: "private"})
702
+ uploads/
703
+ photos/
704
+ originals/
705
+ la98lda74j3g.jpg
706
+ thumbs/
707
+ 95kd8kafg80a.jpg
708
+ ka8agiaf9gk4.jpg
667
709
  ```
668
710
 
669
- ### Clearing cache
711
+ Note that there should always be a random component in the location, so that
712
+ any ORM dirty tracking is detected properly. Inside `#generate_location` you
713
+ can also access the extracted metadata through `context[:metadata]`.
670
714
 
671
- From time to time you'll want to clean your temporary storage from old files.
672
- Amazon S3 provides [a built-in solution][s3 lifecycle], and for FileSystem you
673
- can put something like this in your Rake task:
715
+ When uploading single files, it's possible to bypass `#generate_location` via
716
+ the uploader, by specifying `:location`:
674
717
 
675
718
  ```rb
676
- file_system = Shrine.storages[:cache]
677
- file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
719
+ uploader.upload(file, location: "some/specific/location.mp4")
678
720
  ```
679
721
 
680
722
  ## Direct uploads
681
723
 
682
- Shrine comes with a `direct_upload` plugin for asynchronous uploads to your app
683
- or an external service like Amazon S3. It provides a [Roda] endpoint which you
684
- can mount in your app:
724
+ Shrine comes with a `direct_upload` plugin that can be used for client-side
725
+ asynchronous uploads to your app or an external service like Amazon S3. It
726
+ provides a [Roda] app which you can mount in your app:
685
727
 
686
728
  ```rb
729
+ # Gemfile
687
730
  gem "roda"
688
731
  ```
689
732
  ```rb
@@ -695,25 +738,26 @@ Rails.application.routes.draw do
695
738
  end
696
739
  ```
697
740
 
698
- This endpoint provides the following routes:
741
+ The above setup will provide the following endpoints:
699
742
 
700
743
  * `POST /images/cache/upload` - for direct uploads to your app
701
744
  * `GET /images/cache/presign` - for direct uploads to external service (e.g. Amazon S3)
702
745
 
703
- These routes can be used to asynchronously start caching the file the moment
704
- the user selects it, using JavaScript file upload libraries like
705
- [jQuery-File-Upload], [Dropzone] or [FineUploader].
746
+ Now when the user selects a file, the client can immediately start uploading
747
+ the file asynchronously using one of these endpoints. For JavaScript you can
748
+ use generic file upload libraries like [jQuery-File-Upload], [Dropzone] or
749
+ [FineUploader].
706
750
 
707
- See the [direct_upload] plugin documentation and [Direct Uploads to S3] guide
708
- for more details, as well as the [Roda][roda_demo] and [Rails][rails_demo]
709
- demo apps which implement multiple uploads directly to S3.
751
+ See the [direct_upload] plugin documentation and [Direct Uploads to S3][direct uploads]
752
+ guide for more details, as well as the [Roda][roda_demo] and
753
+ [Rails][rails_demo] demo apps which implement multiple uploads directly to S3.
710
754
 
711
755
  ## Backgrounding
712
756
 
713
- Shrine is the first file upload library designed for backgrounding support.
757
+ Shrine is the first file attachment library designed for backgrounding support.
714
758
  Moving phases of managing file attachments to background jobs is essential for
715
759
  scaling and good user experience, and Shrine provides a `backgrounding` plugin
716
- which makes it really easy to plug in your favourite backgrounding library:
760
+ which makes it easy to plug in your favourite backgrounding library:
717
761
 
718
762
  ```rb
719
763
  Shrine.plugin :backgrounding
@@ -738,23 +782,71 @@ end
738
782
  ```
739
783
 
740
784
  The above puts all promoting (uploading cached file to permanent storage) and
741
- deleting of files into a background Sidekiq job. Obviously instead of Sidekiq
742
- you can use [any other backgrounding library][backgrounding libraries].
785
+ deleting of files into background jobs using Sidekiq. Obviously instead of
786
+ Sidekiq you can use [any other backgrounding library][backgrounding libraries].
743
787
 
744
- The main advantages of Shrine's backgrounding support over other file upload
788
+ The main advantages of Shrine's backgrounding support over other file attachment
745
789
  libraries are:
746
790
 
747
- * **User experience** – After starting the background job, Shrine will save the
791
+ * **User experience** – Before starting the background job, Shrine will save the
748
792
  record with the cached attachment so that it can be immediately shown to the
749
793
  user. With other file upload libraries users cannot see the file until the
750
794
  background job has finished.
751
- * **Simplicity** – Instead of writing the workers for you, Shrine allows you
752
- to use your own workers in a very simple way. Also, no extra columns are
753
- required.
754
- * **Generality** – The above solution will automatically work for all uploaders,
795
+ * **Simplicity** – Instead of shipping with workers for you, Shrine allows you
796
+ to write your own workers and plug them in very easily. And no extra
797
+ columns are required.
798
+ * **Generality** – This setup will automatically be used for all uploaders,
755
799
  types of files and models.
756
- * **Safety** – All of Shrine's code has been designed to take delayed storing
757
- into account, and concurrent requests are handled well.
800
+ * **Safety** – All of Shrine's features have been designed to take delayed
801
+ storing into account, and concurrent requests are handled as well.
802
+
803
+ ## Clearing cache
804
+
805
+ From time to time you'll want to clean your temporary storage from old files.
806
+ Amazon S3 provides [a built-in solution][S3 lifecycle], and for FileSystem you
807
+ can run something like this periodically:
808
+
809
+ ```rb
810
+ file_system = Shrine.storages[:cache]
811
+ file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
812
+ ```
813
+
814
+ ## Logging
815
+
816
+ Shrine ships with the `logging` which automatically logs processing, uploading,
817
+ and deleting of files. This can be very helpful for debugging and performance
818
+ monitoring.
819
+
820
+ ```rb
821
+ Shrine.plugin :logging
822
+ ```
823
+ ```
824
+ 2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s)
825
+ 2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s)
826
+ 2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)
827
+ ```
828
+
829
+ ## Settings
830
+
831
+ Each uploader can store generic settings in the `opts` hash, which can be
832
+ accessed in other uploader actions. You can store there anything that you find
833
+ convenient.
834
+
835
+ ```rb
836
+ Shrine.opts[:type] = "file"
837
+
838
+ class DocumentUploader < Shrine; end
839
+ class ImageUploader < Shrine
840
+ opts[:type] = "image"
841
+ end
842
+
843
+ DocumentUploader.opts[:type] #=> "file"
844
+ ImageUploader.opts[:type] #=> "image"
845
+ ```
846
+
847
+ Because `opts` is cloned in subclasses, overriding settings works with
848
+ inheritance. The `opts` hash is used internally by plugins to store
849
+ configuration.
758
850
 
759
851
  ## On-the-fly processing
760
852
 
@@ -780,10 +872,10 @@ be retried, while the previous chunks remain uploaded.
780
872
  [Tus][tus] is an open protocol for resumable file uploads, which enables the
781
873
  client and the server to achieve reliable file uploads, even on unstable
782
874
  networks, with the possibility to resume the upload even after the browser is
783
- closed or the device shut down. You can use a client library like
875
+ closed or the device are shut down. You can use a client library like
784
876
  [tus-js-client] to upload the file to [tus-ruby-server], and attach the
785
877
  uploaded file to a record using [shrine-url]. See [shrine-tus-demo] for an
786
- example integration.
878
+ example of complete implementation.
787
879
 
788
880
  Another option might be to do chunked uploads directly to your storage service,
789
881
  if the storage service supports it (e.g. Amazon S3 or Google Cloud Storage).
@@ -792,8 +884,8 @@ if the storage service supports it (e.g. Amazon S3 or Google Cloud Storage).
792
884
 
793
885
  Shrine was heavily inspired by [Refile] and [Roda]. From Refile it borrows the
794
886
  idea of "backends" (here named "storages"), attachment interface, and direct
795
- uploads. From Roda it borrows the implementation of an extensible [plugin
796
- system].
887
+ uploads. From Roda it borrows the implementation of an extensible plugin
888
+ system.
797
889
 
798
890
  ## Similar libraries
799
891
 
@@ -802,45 +894,47 @@ system].
802
894
  * Dragonfly
803
895
  * Refile
804
896
 
897
+ ## Code of Conduct
898
+
899
+ Everyone interacting in the Shrine project’s codebases, issue trackers, and
900
+ mailing lists is expected to follow the [Shrine code of conduct][CoC].
901
+
805
902
  ## License
806
903
 
807
904
  The gem is available as open source under the terms of the [MIT License].
808
905
 
809
- [image_processing]: https://github.com/janko-m/image_processing
810
- [fastimage]: https://github.com/sdsykes/fastimage
811
- [file]: http://linux.die.net/man/1/file
812
- [image bombs]: https://www.bamsoftware.com/hacks/deflate.html
813
- [aws-sdk]: https://github.com/aws/aws-sdk-ruby
814
- [jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
815
- [Dropzone]: https://github.com/enyo/dropzone
816
- [FineUploader]: https://github.com/FineUploader/fine-uploader
817
- [Roda]: https://github.com/jeremyevans/roda
818
- [Refile]: https://github.com/refile/refile
819
- [plugin system]: http://twin.github.io/the-plugin-system-of-sequel-and-roda/
820
- [MIT License]: http://opensource.org/licenses/MIT
821
- [ships with over 35 plugins]: http://shrinerb.com#plugins
822
906
  [motivation]: https://twin.github.io/better-file-uploads-with-shrine-motivation/
823
907
  [FileSystem]: http://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
824
908
  [S3]: http://shrinerb.com/rdoc/classes/Shrine/Storage/S3.html
825
- [External]: http://shrinerb.com#external
826
- [`Shrine::UploadedFile`]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/Base/FileMethods.html
827
- [direct uploads]: #direct-uploads
909
+ [direct uploads]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
910
+ [external storages]: http://shrinerb.com/#external
911
+ [`rack_file`]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/RackFile.html
912
+ [Using Attacher]: http://shrinerb.com/rdoc/files/doc/attacher_md.html
913
+ [plugins]: http://shrinerb.com/#plugins
914
+ [`file`]: http://linux.die.net/man/1/file
915
+ [backgrounding]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/Backgrounding.html
916
+ [Context]: https://github.com/janko-m/shrine#context
917
+ [image_processing]: https://github.com/janko-m/image_processing
828
918
  [ffmpeg]: https://github.com/streamio/streamio-ffmpeg
919
+ [jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
920
+ [Dropzone]: https://github.com/enyo/dropzone
921
+ [FineUploader]: https://github.com/FineUploader/fine-uploader
829
922
  [direct_upload]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/DirectUpload.html
830
923
  [Cloudinary]: https://github.com/janko-m/shrine-cloudinary
831
924
  [Imgix]: https://github.com/janko-m/shrine-imgix
832
925
  [Uploadcare]: https://github.com/janko-m/shrine-uploadcare
833
926
  [Attache]: https://github.com/choonkeat/attache
834
- [roda_demo]: /demo
835
- [rails_demo]: https://github.com/erikdahlstrand/shrine-rails-example
836
- [Direct Uploads to S3]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
837
- [website]: http://shrinerb.com
838
- [backgrounding libraries]: https://github.com/janko-m/shrine/wiki/Backgrounding-libraries
927
+ [Dragonfly]: http://markevans.github.io/dragonfly/
839
928
  [tus]: http://tus.io
840
929
  [tus-ruby-server]: https://github.com/janko-m/tus-ruby-server
841
930
  [tus-js-client]: https://github.com/tus/tus-js-client
842
931
  [shrine-tus-demo]: https://github.com/janko-m/shrine-tus-demo
843
932
  [shrine-url]: https://github.com/janko-m/shrine-url
844
- [s3 lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
845
- [Dragonfly]: http://markevans.github.io/dragonfly/
846
- [Using Attacher]: http://shrinerb.com/rdoc/files/doc/attacher_md.html
933
+ [Roda]: https://github.com/jeremyevans/roda
934
+ [Refile]: https://github.com/refile/refile
935
+ [MIT License]: http://opensource.org/licenses/MIT
936
+ [CoC]: https://github.com/janko-m/shrine/blob/master/CODE_OF_CONDUCT.md
937
+ [roda_demo]: https://github.com/janko-m/shrine/tree/master/demo
938
+ [rails_demo]: https://github.com/erikdahlstrand/shrine-rails-example
939
+ [backgrounding libraries]: https://github.com/janko-m/shrine/wiki/Backgrounding-libraries
940
+ [S3 lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html