shrine 2.1.1 → 2.2.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: c148e8d3123fe3f364f245ae7c1a91e56518b346
4
- data.tar.gz: ddade737ebcd5ea39fb221ad4a056abccaa7bcaf
3
+ metadata.gz: 06b571aff1653427247e09a1ec98f9e11a10eb34
4
+ data.tar.gz: cbdd5d75713e42ea5aa449eb1c0a604b2eae35a9
5
5
  SHA512:
6
- metadata.gz: c162582c8920a960ab86bb4ceb67a2a7339517928834faf44aa9c33bbb5909e5ae35a999132a70a5c3e371a71b6282e6e4b6d7f9de9b1bd4731e0025cd56c652
7
- data.tar.gz: 56a1f1440afd717c0f37d0c36aea2b2d261e0857c2c575094110b17a6932951ec17547caf1205c6231dd148ec7fa3be8b82d940d7fc5573690715fc217de7c52
6
+ metadata.gz: 44c88e4f5bb848bc8854cde7ff4eb0fd3209f3b829998cad60377bb975816d94fce356ed8728f1ff86152523aa0e735e07dd6c9d1e008000170d312565e799d1
7
+ data.tar.gz: 056383f1b82fb43ecd62d3a43923c9864323c8c70ace4532ee35e293d94c74ae86b190d83f2870b4b44f7ee005d7688e443bb783b34cc38a8976bfdb5b6c5fe3
data/README.md CHANGED
@@ -59,7 +59,7 @@ class Photo < Sequel::Model # ActiveRecord::Base
59
59
  end
60
60
  ```
61
61
 
62
- Finally, you can add the attachment fields to your form:
62
+ And add attachment fields to the Photo form:
63
63
 
64
64
  ```erb
65
65
  <form action="/photos" method="post" enctype="multipart/form-data">
@@ -75,220 +75,132 @@ Finally, you can add the attachment fields to your form:
75
75
  <% end %>
76
76
  ```
77
77
 
78
- ----------
78
+ Now when a Photo is created with the image attached, you can get the URL to
79
+ the image:
79
80
 
80
- ## Basics
81
-
82
- Here's an example showing how basic file upload works in Shrine:
83
-
84
- ```rb
85
- require "shrine"
86
- require "shrine/storage/file_system"
87
-
88
- Shrine.storages[:file_system] = Shrine::Storage::FileSystem.new("uploads")
89
-
90
- uploader = Shrine.new(:file_system)
91
-
92
- uploaded_file = uploader.upload(File.open("movie.mp4"))
93
- uploaded_file #=> #<Shrine::UploadedFile>
94
- uploaded_file.data #=>
95
- # {
96
- # "storage" => "file_system",
97
- # "id" => "9260ea09d8effd.mp4",
98
- # "metadata" => {...},
99
- # }
100
- ```
101
-
102
- Let's see what's going on here:
103
-
104
- First we registered the storage we want to use under a name. Storages are plain
105
- Ruby classes which encapsulate file management on a particular service. We can
106
- then instantiate `Shrine` as a wrapper around that storage. A call to `upload`
107
- uploads the given file to the underlying storage.
108
-
109
- The argument to `upload` needs to be an IO-like object. So, `File`, `Tempfile`
110
- and `StringIO` are all valid arguments. The object doesn't have to be an actual
111
- IO, though, it's enough that it responds to these 5 methods: `#read(*args)`,
112
- `#size`, `#eof?`, `#rewind` and `#close`. `ActionDispatch::Http::UploadedFile`
113
- is one such object, as well as `Shrine::UploadedFile` itself.
114
-
115
- The result of uploading is a `Shrine::UploadedFile` object, which represents
116
- the uploaded file on the storage. It is defined solely by its data hash. We can
117
- do a lot with it:
118
-
119
- ```rb
120
- uploaded_file.url #=> "uploads/938kjsdf932.mp4"
121
- uploaded_file.metadata #=> {...}
122
- uploaded_file.read #=> "..."
123
- uploaded_file.exists? #=> true
124
- uploaded_file.download #=> #<Tempfile:/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/20151004-74201-1t2jacf.mp4>
125
- uploaded_file.delete
126
- # ...
81
+ ```erb
82
+ <img src="<%= @photo.image_url %>">
127
83
  ```
128
84
 
129
85
  ## Attachment
130
86
 
131
- In web applications we usually want work with files on a higher level. We want
132
- to treat them as "attachments" to records, by persisting their information to a
133
- database column and tying their lifecycle to the record. For this Shrine offers
134
- a higher-level attachment interface.
135
-
136
- First we need to register temporary and permanent storage which will be used
137
- internally:
87
+ When we assign an IO-like object to the record, Shrine will upload it to the
88
+ registered `:cache` storage, which acts as a temporary storage, and write the
89
+ location/storage/metadata of the uploaded file to a single `<attachment>_data`
90
+ column:
138
91
 
139
92
  ```rb
140
- require "shrine"
141
- require "shrine/storage/file_system"
93
+ photo = Photo.new
94
+ photo.image = File.open("waterfall.jpg")
95
+ photo.image_data #=> '{"storage":"cache","id":"9260ea09d8effd.jpg","metadata":{...}}'
142
96
 
143
- Shrine.storages = {
144
- cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
145
- store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
146
- }
97
+ photo.image #=> #<Shrine::UploadedFile>
98
+ photo.image_url #=> "/uploads/cache/9260ea09d8effd.jpg"
147
99
  ```
148
100
 
149
- The `:cache` and `:store` are only special in terms that they will be used
150
- automatically (but that can be changed with the default_storage plugin). Next,
151
- we create an uploader class specific to the type of attachment we want, so that
152
- later we can have different uploading logic for different attachment types.
101
+ The Shrine attachment module added the following methods to the `Photo` model:
153
102
 
154
- ```rb
155
- class ImageUploader < Shrine
156
- # your logic for uploading images
157
- end
158
- ```
103
+ * `#image=` – caches the file and saves the result into `image_data`
104
+ * `#image` returns `Shrine::UploadedFile` instantiated from `image_data`
105
+ * `#image_url` calls `image.url` if attachment is present, otherwise returns nil
106
+ * `#image_attacher` - instance of `Shrine::Attacher` which handles attaching
159
107
 
160
- Finally, to add an attachment to a model, we generate a named "attachment"
161
- module using the uploader and include it:
108
+ In addition to assigning new files, you can also assign already uploaded files:
162
109
 
163
110
  ```rb
164
- class Photo
165
- include ImageUploader[:image] # requires "image_data" attribute
166
- end
111
+ photo.image = '{"storage":"cache","id":"9260ea09d8effd.jpg","metadata":{...}}'
167
112
  ```
168
113
 
169
- Now our model has gained special methods for attaching files:
170
-
171
- ```rb
172
- photo = Photo.new
173
- photo.image = File.open("nature.jpg") # uploads the file to cache
174
- photo.image #=> #<Shrine::UploadedFile>
175
- photo.image_url #=> "/uploads/cache/9260ea09d8effd.jpg"
176
- photo.image_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
177
- ```
114
+ This allows Shrine to retain uploaded files in case of validation errors, and
115
+ handle [direct uploads], via the hidden form field.
178
116
 
179
- The attachment module has added `#image`, `#image=` and `#image_url`
180
- methods to our `Photo`, using regular module inclusion.
117
+ The ORM plugin that we loaded will upload the attachment to permanent storage
118
+ (`:store`) when the record is saved, and delete the attachment when record
119
+ is destroyed:
181
120
 
182
121
  ```rb
183
- Shrine[:image] #=> #<Shrine::Attachment(image)>
184
- Shrine[:image].is_a?(Module) #=> true
185
- Shrine[:image].instance_methods #=> [:image=, :image, :image_url, :image_attacher]
122
+ photo.image = File.open("waterfall.jpg")
123
+ photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
186
124
 
187
- Shrine[:document] #=> #<Shrine::Attachment(document)>
188
- Shrine[:document].instance_methods #=> [:document=, :document, :document_url, :document_attacher]
125
+ photo.save
126
+ photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
189
127
 
190
- # Expanded forms
191
- Shrine.attachment(:image)
192
- Shrine::Attachment.new(:document)
128
+ photo.destroy
129
+ photo.image.exists? #=> false
193
130
  ```
194
131
 
195
- * `#image=` caches the file and saves JSON data into `image_data`
196
- * `#image` returns a `Shrine::UploadedFile` based on data from `image_data`
197
- * `#image_url` – calls `image.url` if attachment is present, otherwise returns nil.
198
-
199
- This is how you should create a form for a `@photo`:
132
+ In these examples we used `image` as the name of the attachment, but we can
133
+ create attachment modules for any kind of attachments:
200
134
 
201
135
  ```rb
202
- Shrine.plugin :cached_attachment_data
136
+ class VideoUploader < Shrine
137
+ # video attachment logic
138
+ end
203
139
  ```
204
- ```erb
205
- <form action="/photos" method="post" enctype="multipart/form-data">
206
- <input name="photo[image]" type="hidden" value="<%= @photo.cached_image_data %>">
207
- <input name="photo[image]" type="file">
208
- </form>
140
+ ```rb
141
+ class Movie < Sequel::Model
142
+ include VideoUploader[:video] # uses "video_data" column
143
+ end
209
144
  ```
210
145
 
211
- The "file" field is for file upload, while the "hidden" field is to make the
212
- file persist in case of validation errors, and for direct uploads. Note that
213
- the hidden field should always be *before* the file field.
146
+ ## Uploader
214
147
 
215
- This code works because `#image=` also accepts an already cached file via its
216
- JSON representation (which is what `#cached_image_data` returns):
148
+ "Uploaders" are subclasses of `Shrine`, and this is where we define all our
149
+ attachment logic. Uploaders act as a wrappers around a storage, delegating all
150
+ service-specific logic to the storage. They don't know anything about models
151
+ and are stateless; they are only in charge of uploading, processing and
152
+ deleting files.
217
153
 
218
154
  ```rb
219
- photo.image = '{"id":"9jsdf02kd", "storage":"cache", "metadata": {...}}'
155
+ uploader = DocumentUploader.new(:store)
156
+ uploaded_file = uploader.upload(File.open("resume.pdf"))
157
+ uploaded_file #=> #<Shrine::UploadedFile>
158
+ uploaded_file.to_json #=> '{"storage":"store","id":"0sdfllasfi842.pdf","metadata":{...}}'
220
159
  ```
221
160
 
222
- ### ORM
161
+ Shrine requires the input for uploading to be an IO-like object. So, `File`,
162
+ `Tempfile` and `StringIO` instances are all valid inputs. The object doesn't
163
+ have to be an actual IO, it's enough that it responds to: `#read(*args)`,
164
+ `#size`, `#eof?`, `#rewind` and `#close`. `ActionDispatch::Http::UploadedFile`
165
+ is one such object, as well as `Shrine::UploadedFile` itself.
223
166
 
224
- Even though you can use Shrine's attachment interface with plain Ruby objects,
225
- it's much more common to use it with an ORM. Shrine ships with plugins for
226
- Sequel and ActiveRecord ORMs. It uses the `<attachment>_data` column for
227
- storing data for uploaded files, so you'll need to add it in a migration.
167
+ The result of uploading is a `Shrine::UploadedFile` object, which represents
168
+ the uploaded file on the storage, and is defined by its underlying data hash.
228
169
 
229
170
  ```rb
230
- Shrine.plugin :sequel # :activerecord
231
- ```
232
- ```rb
233
- add_column :movies, :video_data, :text
234
- ```
235
- ```rb
236
- class Movie < Sequel::Model
237
- include VideoUploader[:video]
238
- end
171
+ uploaded_file.url #=> "uploads/938kjsdf932.mp4"
172
+ uploaded_file.metadata #=> {...}
173
+ uploaded_file.download #=> #<Tempfile:/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/20151004-74201-1t2jacf.mp4>
174
+ uploaded_file.exists? #=> true
175
+ uploaded_file.open { |io| ... }
176
+ uploaded_file.delete
177
+ # ...
239
178
  ```
240
179
 
241
- In addition to getters and setters, the ORM plugins add the appropriate
242
- callbacks:
180
+ This is the same object that is returned when we access the attachment through
181
+ the record:
243
182
 
244
183
  ```rb
245
- movie.video = File.open("video.mp4")
246
- movie.video_url #=> "/uploads/cache/0sdfllasfi842.mp4"
247
-
248
- movie.save
249
- movie.video_url #=> "/uploads/store/l02kladf8jlda.mp4"
250
-
251
- movie.destroy
252
- movie.video.exists? #=> false
184
+ photo.image #=> #<Shrine::UploadedFile>
253
185
  ```
254
186
 
255
- First the raw file is cached to temporary storage on assignment, then on saving
256
- the cached file is uploaded to permanent storage. Destroying the record
257
- destroys the attachment.
258
-
259
- *NOTE: The record will first be saved with the cached attachment, and
260
- afterwards (in an "after commit" hook) updated with the stored attachment. This
261
- is done so that processing/storing isn't performed inside a database
262
- transaction. If you're doing processing, there will be a period of time when
263
- the record will be saved with an unprocessed attachment, so you may need to
264
- account for that.*
265
-
266
187
  ## Processing
267
188
 
268
- Whenever a file is uploaded, `Shrine#process` is called, and this is where
269
- you're expected to define your processing.
189
+ Shrine allows you to perform file processing in functional style; you receive
190
+ the original file as the input, and return processed files as the output.
270
191
 
271
- ```rb
272
- class ImageUploader < Shrine
273
- def process(io, context)
274
- # ...
275
- end
276
- end
277
- ```
278
-
279
- Shrine's uploaders are stateless; the `#process` method is simply a function
280
- which takes an input `io` and returns processed file(s) as output. Since it's
281
- called for each upload, attaching the file will call it twice, first when
282
- raw file is cached to temporary storage on assignment, then when cached file
283
- is uploaded to permanent storage on saving. We usually want to process in the
284
- latter phase (after file validations):
192
+ Processing can be performed whenever a file is uploaded. On attaching this
193
+ happens twice; first the raw file is cached to temporary storage ("cache"
194
+ action), then when the record is saved the cached file is "promoted" to
195
+ permanent storage ("store" action). We generally want to process on the "store"
196
+ action, because it happens after file validations and can be backgrounded.
285
197
 
286
198
  ```rb
287
199
  class ImageUploader < Shrine
288
- def process(io, context)
289
- if context[:phase] == :store
290
- # ...
291
- end
200
+ plugin :processing
201
+
202
+ process(:store) do |io, context|
203
+ # ...
292
204
  end
293
205
  end
294
206
  ```
@@ -303,53 +215,56 @@ require "image_processing/mini_magick"
303
215
 
304
216
  class ImageUploader < Shrine
305
217
  include ImageProcessing::MiniMagick
218
+ plugin :processing
306
219
 
307
- def process(io, context)
308
- if context[:phase] == :store
309
- resize_to_limit(io.download, 700, 700)
310
- end
220
+ process(:store) do |io, context|
221
+ resize_to_limit(io.download, 700, 700)
311
222
  end
312
223
  end
313
224
  ```
314
225
 
315
226
  Since here `io` is a cached `Shrine::UploadedFile`, we need to download it to
316
- a file, as image_processing only accepts real files.
227
+ a `File`, which is what image_processing recognizes.
317
228
 
318
229
  ### Versions
319
230
 
320
- If you're uploading images, often you'll want to store various thumbnails
321
- alongside your original image. You can do that by loading the versions plugin,
322
- and in `#process` simply returning a Hash of versions:
231
+ Sometimes we want to generate multiple files as the result of processing. If
232
+ we're uploading images, we might want to store various thumbnails alongside the
233
+ original image. If we're uploading videos, we might want to save a screenshot
234
+ or transcode it into different formats.
235
+
236
+ To save multiple files, we just need to load the versions plugin, and then in
237
+ `#process` we can return a Hash of files:
323
238
 
324
239
  ```rb
325
240
  require "image_processing/mini_magick"
326
241
 
327
242
  class ImageUploader < Shrine
328
243
  include ImageProcessing::MiniMagick
244
+ plugin :processing
329
245
  plugin :versions
330
246
 
331
- def process(io, context)
332
- if context[:phase] == :store
333
- size_700 = resize_to_limit(io.download, 700, 700)
334
- size_500 = resize_to_limit(size_700, 500, 500)
335
- size_300 = resize_to_limit(size_500, 300, 300)
247
+ process(:store) do |io, context|
248
+ size_700 = resize_to_limit(io.download, 700, 700)
249
+ size_500 = resize_to_limit(size_700, 500, 500)
250
+ size_300 = resize_to_limit(size_500, 300, 300)
336
251
 
337
- {large: size_700, medium: size_500, small: size_300}
338
- end
252
+ {large: size_700, medium: size_500, small: size_300}
339
253
  end
340
254
  end
341
255
  ```
342
256
 
343
- Being able to define processing on instance level provides a lot of flexibility,
344
- allowing things like choosing the order or adding parallelization. It is
345
- recommended to use the delete_raw plugin for automatically deleting processed
257
+ Being able to define processing on instance-level like this provides a lot of
258
+ flexibility. For example, you can choose to process files in a certain order
259
+ for maximum performance, and you can also add parallelization. It is
260
+ recommended to load the delete_raw plugin for automatically deleting processed
346
261
  files after uploading.
347
262
 
348
- The attachment getter will simply return the processed attachment as a Hash of
349
- versions:
263
+ Each version will be saved to the attachment column, and the attachment getter
264
+ will simply return a Hash of `Shrine::UploadedFile` objects:
350
265
 
351
266
  ```rb
352
- photo.image.class #=> Hash
267
+ photo.image #=> {large: ..., medium: ..., small: ...}
353
268
 
354
269
  # With the store_dimensions plugin
355
270
  photo.image[:large].width #=> 700
@@ -360,39 +275,53 @@ photo.image[:small].width #=> 300
360
275
  photo.image_url(:large) #=> "..."
361
276
  ```
362
277
 
363
- ## Context
278
+ ### Custom processing
364
279
 
365
- You may have noticed the `context` variable as the second argument to
366
- `Shrine#process`. This variable contains information about the context in
367
- which the file is uploaded.
280
+ Your processing tool doesn't have to be in any way designed for Shrine
281
+ ([image_processing] is a generic library), you only need to return processed
282
+ files as IO objects, e.g. `File` objects. Here's an example of processing a
283
+ video with [ffmpeg]:
368
284
 
369
285
  ```rb
370
- class ImageUploader < Shrine
371
- def process(io, context)
372
- puts context
286
+ require "streamio-ffmpeg"
287
+
288
+ class VideoUploader < Shrine
289
+ plugin :processing
290
+ plugin :versions
291
+
292
+ process(:store) do |io, context|
293
+ mov = io.download
294
+ video = Tempfile.new(["video", ".mp4"], binmode: true)
295
+ screenshot = Tempfile.new(["screenshot", ".jpg"], binmode: true)
296
+
297
+ movie = FFMPEG::Movie.new(mov.path)
298
+ movie.transcode(video.path)
299
+ movie.screenshot(screenshot.path)
300
+
301
+ mov.delete
302
+
303
+ {video: video, screenshot: screenshot}
373
304
  end
374
305
  end
375
306
  ```
376
- ```rb
377
- photo = Photo.new
378
- photo.image = File.open("image.jpg") # "cache"
379
- photo.save # "store"
380
- ```
381
- ```
382
- {:name=>:image, :record=>#<Photo:0x007fe1627f1138>, :phase=>:cache}
383
- {:name=>:image, :record=>#<Photo:0x007fe1627f1138>, :phase=>:store}
384
- ```
385
307
 
386
- The `:name` is the name of the attachment, in this case "image". The `:record`
387
- is the model instance, in this case instance of `Photo`. Lastly, the `:phase`
388
- is a symbol which indicates the purpose of the upload (by default there are
389
- only `:cache` and `:store`, but some plugins add more of them).
308
+ ## Context
390
309
 
391
- Context is useful for doing conditional processing and validation, since we
392
- have access to the record and attachment name, and it is also used by some
393
- plugins internally.
310
+ You may have noticed the `context` variable floating around as the second
311
+ argument for processing. This variable is present all the way from input file
312
+ to uploaded file, and contains any additional information that can affect the
313
+ upload:
394
314
 
395
- ## Validations
315
+ * `context[:record]` -- the model instance
316
+ * `context[:name]` -- attachment name on the model
317
+ * `context[:action]` -- identifier for the action being performed (`:cache`, `:store`, `:recache`, `:backup`, ...)
318
+ * `context[:version]` -- version name of the IO in the argument
319
+ * ...
320
+
321
+ The `context` is useful for doing conditional processing, validation,
322
+ generating location etc, and it is also used by some plugins internally.
323
+
324
+ ## Validation
396
325
 
397
326
  Validations are registered by calling `Attacher.validate`, and are best done
398
327
  with the validation_helpers plugin:
@@ -424,6 +353,7 @@ Shrine automatically extracts and stores general file metadata:
424
353
 
425
354
  ```rb
426
355
  photo = Photo.create(image: image)
356
+
427
357
  photo.image.metadata #=>
428
358
  # {
429
359
  # "filename" => "nature.jpg",
@@ -458,20 +388,25 @@ photo.image.mime_type #=> "text/x-php"
458
388
 
459
389
  ### Custom metadata
460
390
 
461
- You can also extract and store custom metadata by overriding
462
- `Shrine#extract_metadata`:
391
+ You can also extract and store completely custom metadata with the metadata
392
+ plugin:
463
393
 
464
394
  ```rb
395
+ require "mini_magick"
396
+
465
397
  class ImageUploader < Shrine
466
- def extract_metadata(io, context)
467
- metadata = super
468
- metadata["custom"] = extract_custom(io)
469
- metadata
398
+ plugin :add_metadata
399
+
400
+ add_metadata "exif" do |io, context|
401
+ MiniMagick::Image.new(io.path).exif
470
402
  end
471
403
  end
472
404
  ```
473
-
474
- Note that you should always rewind the `io` after reading from it.
405
+ ```rb
406
+ photo.image.metadata["exif"]
407
+ # or
408
+ photo.image.exif
409
+ ```
475
410
 
476
411
  ## Locations
477
412
 
@@ -488,7 +423,7 @@ photo = Photo.create(image: File.open("nature.jpg"))
488
423
  photo.image.id #=> "photo/34/image/34krtreds2df.jpg"
489
424
  ```
490
425
 
491
- If you want to generate locations on your own, simply override
426
+ If you want to generate locations on your own, you can override
492
427
  `Shrine#generate_location`:
493
428
 
494
429
  ```rb
@@ -503,8 +438,8 @@ class ImageUploader < Shrine
503
438
  end
504
439
  ```
505
440
 
506
- Note that there should always be a random component in the location, for dirty
507
- tracking to be detected properly (you can use `Shrine#generate_uid`). Inside
441
+ Note that there should always be a random component in the location, so that
442
+ dirty tracking is detected properly; you can use `Shrine#generate_uid`. Inside
508
443
  `#generate_location` you can access the extracted metadata through
509
444
  `context[:metadata]`.
510
445
 
@@ -512,13 +447,14 @@ When using the uploader directly, it's possible to bypass `#generate_location`
512
447
  by passing a `:location`:
513
448
 
514
449
  ```rb
515
- uploader = Shrine.new(:store)
450
+ uploader = MyUploader.new(:store)
516
451
  file = File.open("nature.jpg")
517
452
  uploader.upload(file, location: "some/specific/location.jpg")
518
453
  ```
519
454
 
520
455
  ## Storage
521
456
 
457
+ "Storages" are objects which know how to manage files on a particular service.
522
458
  Other than [FileSystem], Shrine also ships with Amazon [S3] storage:
523
459
 
524
460
  ```rb
@@ -536,33 +472,33 @@ Shrine.storages[:store] = Shrine::Storage::S3.new(
536
472
  ```
537
473
 
538
474
  ```rb
539
- movie = Movie.new(video: File.open("video.mp4"))
540
- movie.video_url #=> "/uploads/cache/j4k343ui12ls9.jpg"
541
- movie.save
542
- movie.video_url #=> "https://my-bucket.s3-eu-west-1.amazonaws.com/0943sf8gfk13.mp4"
475
+ photo = Photo.new(image: File.open("image.png"))
476
+ photo.image_url #=> "/uploads/cache/j4k343ui12ls9.png"
477
+ photo.save
478
+ photo.image_url #=> "https://my-bucket.s3.amazonaws.com/0943sf8gfk13.png"
543
479
  ```
544
480
 
545
- If you're using S3 both for cache and store, uploading a cached file to store
546
- will simply do an S3 COPY request instead of downloading and reuploading the
547
- file. Also, the versions plugin takes advantage of S3's MULTI DELETE
548
- capabilities, so versions are deleted with a single HTTP request.
481
+ Note that any options passed to `image_url` will be forwarded to the underlying
482
+ storage, see the documentation of the storage that you're using for which URL
483
+ options it supports.
549
484
 
550
- See the full documentation for [FileSystem] and [S3] storages. There are also
551
- many other Shrine storages available, see the [Plugins & Storages] section.
485
+ You can see the full documentation for [FileSystem] and [S3] storages. There
486
+ are also many other Shrine storages available, see [External] section on the
487
+ website.
552
488
 
553
- ## Upload options
489
+ ### Upload options
554
490
 
555
491
  Many storages accept additional upload options, which you can pass via the
556
492
  upload_options plugin, or manually when uploading:
557
493
 
558
494
  ```rb
559
- uploader = Shrine.new(:store)
495
+ uploader = MyUploader.new(:store)
560
496
  uploader.upload(file, upload_options: {acl: "private"})
561
497
  ```
562
498
 
563
499
  ## Direct uploads
564
500
 
565
- Shrine comes with a direct_upload plugin which provides a [Roda] endpoint that
501
+ Shrine comes with a [direct_upload] plugin which provides a [Roda] endpoint that
566
502
  accepts file uploads. This allows you to asynchronously start caching the file
567
503
  the moment the user selects it via AJAX (e.g. using the [jQuery-File-Upload] JS
568
504
  library).
@@ -572,12 +508,12 @@ Shrine.plugin :direct_upload # Provides a Roda endpoint
572
508
  ```
573
509
  ```rb
574
510
  Rails.application.routes.draw do
575
- mount VideoUploader::UploadEndpoint => "/attachments/videos"
511
+ mount VideoUploader::UploadEndpoint => "/videos"
576
512
  end
577
513
  ```
578
514
  ```js
579
515
  $('[type="file"]').fileupload({
580
- url: '/attachments/videos/cache/upload',
516
+ url: '/videos/cache/upload',
581
517
  paramName: 'file',
582
518
  add: function(e, data) { /* Disable the submit button */ },
583
519
  progress: function(e, data) { /* Add a nice progress bar */ },
@@ -585,10 +521,11 @@ $('[type="file"]').fileupload({
585
521
  });
586
522
  ```
587
523
 
588
- The plugin also provides a route that can be used for doing direct S3 uploads.
589
- See the documentation of the plugin for more details, as well as the
524
+ Along with the upload route, this endpoint also includes a route for generating
525
+ presigns for direct uploads to 3rd-party services like Amazon S3. See the
526
+ [direct_upload] plugin documentation for more details, as well as the
590
527
  [Roda](https://github.com/janko-m/shrine-example)/[Rails](https://github.com/erikdahlstrand/shrine-rails-example)
591
- example app which demonstrates multiple uploads directly to S3.
528
+ example apps which demonstrate multiple uploads directly to S3.
592
529
 
593
530
  ## Backgrounding
594
531
 
@@ -619,9 +556,9 @@ class DeleteJob
619
556
  end
620
557
  ```
621
558
 
622
- The above puts all promoting (moving to store) and deleting of files into a
623
- background Sidekiq job. Obviously instead of Sidekiq you can use any other
624
- backgrounding library.
559
+ The above puts all promoting (uploading cached file to permanent storage) and
560
+ deleting of files into a background Sidekiq job. Obviously instead of Sidekiq
561
+ you can use any other backgrounding library.
625
562
 
626
563
  The main advantages of Shrine's backgrounding support over other file upload
627
564
  libraries are:
@@ -640,8 +577,8 @@ libraries are:
640
577
 
641
578
  ## Clearing cache
642
579
 
643
- You will want to periodically clean your temporary storage. Amazon S3 provides
644
- [a built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
580
+ From time to time you'll want to clean your temporary storage from old files.
581
+ Amazon S3 provides [a built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
645
582
  and for FileSystem you can put something like this in your Rake task:
646
583
 
647
584
  ```rb
@@ -667,6 +604,21 @@ class ImageUploader < Shrine
667
604
  end
668
605
  ```
669
606
 
607
+ ## On-the-fly processing
608
+
609
+ Shrine allows you to define processing that will be performed on upload.
610
+ However, what if want to perform processing on-the-fly, only when the URL is
611
+ requested? Unlike Refile or Dragonfly, Shrine doesn't come with an image server
612
+ built in, instead it expects you to integrate any of the existing generic image
613
+ servers.
614
+
615
+ Shrine has integrations for many commercial on-the-fly processing services, so
616
+ you can use [shrine-cloudinary], [shrine-imgix] or [shrine-uploadcare].
617
+
618
+ If you don't want to use a commercial service, [Attache] is a great open-source
619
+ image server. There isn't a Shrine integration written for it yet, but it
620
+ should be fairly easy to write one.
621
+
670
622
  ## Inspiration
671
623
 
672
624
  Shrine was heavily inspired by [Refile] and [Roda]. From Refile it borrows the
@@ -674,6 +626,13 @@ idea of "backends" (here named "storages"), attachment interface, and direct
674
626
  uploads. From Roda it borrows the implementation of an extensible [plugin
675
627
  system].
676
628
 
629
+ ## Similar libraries
630
+
631
+ * Paperclip
632
+ * CarrierWave
633
+ * Dragonfly
634
+ * Refile
635
+
677
636
  ## License
678
637
 
679
638
  The gem is available as open source under the terms of the [MIT License].
@@ -693,5 +652,12 @@ The gem is available as open source under the terms of the [MIT License].
693
652
  [introductory blog post]: http://twin.github.io/introducing-shrine/
694
653
  [FileSystem]: http://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
695
654
  [S3]: http://shrinerb.com/rdoc/classes/Shrine/Storage/S3.html
696
- [Plugins & Storages]: http://shrinerb.com#external
655
+ [External]: http://shrinerb.com#external
697
656
  [`Shrine::UploadedFile`]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/Base/FileMethods.html
657
+ [direct uploads]: #direct-uploads
658
+ [ffmpeg]: https://github.com/streamio/streamio-ffmpeg
659
+ [direct_upload]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/DirectUpload.html
660
+ [shrine-cloudinary]: https://github.com/janko-m/shrine-cloudinary
661
+ [shrine-imgix]: https://github.com/janko-m/shrine-imgix
662
+ [shrine-uploadcare]: https://github.com/janko-m/shrine-uploadcare
663
+ [Attache]: https://github.com/choonkeat/attache