shrine 1.0.0 → 1.1.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +101 -149
  3. data/doc/carrierwave.md +12 -16
  4. data/doc/changing_location.md +50 -0
  5. data/doc/creating_plugins.md +2 -2
  6. data/doc/creating_storages.md +70 -9
  7. data/doc/direct_s3.md +132 -61
  8. data/doc/migrating_storage.md +12 -10
  9. data/doc/paperclip.md +12 -17
  10. data/doc/refile.md +338 -0
  11. data/doc/regenerating_versions.md +75 -11
  12. data/doc/securing_uploads.md +172 -0
  13. data/lib/shrine.rb +21 -16
  14. data/lib/shrine/plugins/activerecord.rb +2 -2
  15. data/lib/shrine/plugins/background_helpers.rb +2 -148
  16. data/lib/shrine/plugins/backgrounding.rb +148 -0
  17. data/lib/shrine/plugins/backup.rb +88 -0
  18. data/lib/shrine/plugins/data_uri.rb +25 -4
  19. data/lib/shrine/plugins/default_url.rb +37 -0
  20. data/lib/shrine/plugins/delete_uploaded.rb +40 -0
  21. data/lib/shrine/plugins/determine_mime_type.rb +4 -2
  22. data/lib/shrine/plugins/direct_upload.rb +107 -62
  23. data/lib/shrine/plugins/download_endpoint.rb +157 -0
  24. data/lib/shrine/plugins/hooks.rb +19 -5
  25. data/lib/shrine/plugins/keep_location.rb +43 -0
  26. data/lib/shrine/plugins/moving.rb +11 -10
  27. data/lib/shrine/plugins/parallelize.rb +1 -5
  28. data/lib/shrine/plugins/parsed_json.rb +7 -1
  29. data/lib/shrine/plugins/pretty_location.rb +6 -0
  30. data/lib/shrine/plugins/rack_file.rb +7 -1
  31. data/lib/shrine/plugins/remove_invalid.rb +22 -0
  32. data/lib/shrine/plugins/sequel.rb +2 -2
  33. data/lib/shrine/plugins/upload_options.rb +41 -0
  34. data/lib/shrine/plugins/versions.rb +9 -7
  35. data/lib/shrine/storage/file_system.rb +46 -30
  36. data/lib/shrine/storage/linter.rb +48 -25
  37. data/lib/shrine/storage/s3.rb +89 -22
  38. data/lib/shrine/version.rb +1 -1
  39. data/shrine.gemspec +3 -3
  40. metadata +16 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 583eed751bfa6c1ae0ad5d9495509091563a8c02
4
- data.tar.gz: 9c82a62a10a45df20f3f4d9422066beb705cfa39
3
+ metadata.gz: 11b0d1c76734507b02ec22fb1ae1aef8a8c704e7
4
+ data.tar.gz: 397c1df52d016428395879fbc8bfba4c0dbb7db7
5
5
  SHA512:
6
- metadata.gz: 68e093c6b118b379383305bfede12fd5710342c82c6b66040661be744d6995a723147f3b426b0d8afc03009c35d3b21a529232822e9232d2c25531d831fd973b
7
- data.tar.gz: 41fabbd7fa515c3eb992bbbf5205fd924d502c64ec6f430d053ef135512e2476797435a5a4642fd066ea9bd8ba0b375455317a6bc54aaabcf24b557841b98de1
6
+ metadata.gz: ad5a997b948d19f19fd636502886fe1398deda24be00d5028aabaaabe17c99dd5251c50a4da976c0a7bd1025f9edec0243b2d8dbf67b5115605e3f74c30f7903
7
+ data.tar.gz: fa8e72de26ecafcbf0be585f25c495072b7502d50c018449a0071171de11e4501df43b75a074cb70968219ad9eb4259138669e86f58e9a2280f7f67b80b33a12
data/README.md CHANGED
@@ -22,7 +22,7 @@ Shrine has been tested on MRI 2.1, MRI 2.2, JRuby and Rubinius.
22
22
 
23
23
  ## Basics
24
24
 
25
- Here's a basic example showing how the file upload works:
25
+ Here's a basic example showing how the file upload works in Shrine:
26
26
 
27
27
  ```rb
28
28
  require "shrine"
@@ -45,12 +45,13 @@ uploaded_file.data #=>
45
45
 
46
46
  First we add the storage we want to use to Shrine's registry. Storages are
47
47
  simple Ruby classes which perform the actual uploads. We instantiate a `Shrine`
48
- with the storage name, and when we call `Shrine#upload` the following happens:
48
+ with the storage name, and when we call `#upload` Shrine does the following:
49
49
 
50
- * a unique location is generated for the file
51
- * metadata is extracted from the file
52
- * the underlying storage is called to store the file
53
- * a `Shrine::UploadedFile` is returned with these data
50
+ * generates a unique location for the file
51
+ * extracts metadata from the file
52
+ * uploads the file using the underlying storage
53
+ * closes the file
54
+ * returns a `Shrine::UploadedFile` with relevant data
54
55
 
55
56
  The argument to `Shrine#upload` needs to be an IO-like object. So, `File`,
56
57
  `Tempfile` and `StringIO` are all valid arguments. But the object doesn't have
@@ -80,8 +81,8 @@ uploaded_file.delete
80
81
  ## Attachment
81
82
 
82
83
  In web applications, instead of managing files directly, we rather want to
83
- treat them as "attachments" to models and to tie them to the lifecycle of
84
- records. In Shrine we do this by generating and including "attachment" modules.
84
+ treat them as "attachments" to recod tie them to their lifecycle. In Shrine we
85
+ do this by generating and including "attachment" modules.
85
86
 
86
87
  Firstly we need to assign the special `:cache` and `:store` storages:
87
88
 
@@ -89,13 +90,14 @@ Firstly we need to assign the special `:cache` and `:store` storages:
89
90
  require "shrine/storage/file_system"
90
91
 
91
92
  Shrine.storages = {
92
- cache: Shrine::Storage::FileSystem.new("public", subdirectory: "uploads/cache"),
93
- store: Shrine::Storage::FileSystem.new("public", subdirectory: "uploads/store"),
93
+ cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
94
+ store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
94
95
  }
95
96
  ```
96
97
 
97
- Next we should create an uploader specific to the type of files we're
98
- uploading:
98
+ These storages will by default be used for caching and storing attachments, but
99
+ you can use additional storages with the `default_storage` plugin. Next we
100
+ should create an uploader specific to the type of files we're uploading:
99
101
 
100
102
  ```rb
101
103
  class ImageUploader < Shrine
@@ -118,7 +120,7 @@ Now our model has gained special methods for attaching avatars:
118
120
 
119
121
  ```rb
120
122
  user = User.new
121
- user.avatar = File.open("avatar.jpg") # uploads the file to `:cache`
123
+ user.avatar = File.open("avatar.jpg") # uploads the file to cache
122
124
  user.avatar #=> #<Shrine::UploadedFile>
123
125
  user.avatar_url #=> "/uploads/9260ea09d8effd.jpg"
124
126
  user.avatar_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
@@ -145,17 +147,35 @@ column (`avatar_data`). The getter (`#avatar`) reads the "data" column and
145
147
  returns a `Shrine::UploadedFile`. The url method (`#avatar_url`) calls
146
148
  `avatar.url` if the attachment is present, otherwise returns nil.
147
149
 
150
+ This is how you would typically create the form for a `@user`:
151
+
152
+ ```erb
153
+ <form action="/users" method="post" enctype="multipart/form-data">
154
+ <input name="user[avatar]" type="hidden" value="<%= @user.avatar_data %>">
155
+ <input name="user[avatar]" type="file">
156
+ </form>
157
+ ```
158
+
159
+ The "file" field is for file upload, while the "hidden" field is to make the
160
+ file persist in case of validation errors, and for direct uploads. This code
161
+ works because `#avatar=` also accepts already cached files via their JSON
162
+ representation:
163
+
164
+ ```rb
165
+ user.avatar = '{"id":"9jsdf02kd", "storage":"cache", "metadata": {...}}'
166
+ ```
167
+
148
168
  ### ORM
149
169
 
150
170
  Your models probably won't be POROs, so Shrine ships with plugins for
151
- Sequel and ActiveRecord ORMs. Shrine uses the "\<attachment\>\_data" column
171
+ Sequel and ActiveRecord ORMs. Shrine uses the `<attachment>_data` column
152
172
  for storing attachments, so you'll need to add it in a migration:
153
173
 
154
174
  ```rb
155
- add_column :users, :avatar_data, :text # or a "jsonb" column if you need querying
175
+ add_column :users, :avatar_data, :text # or a JSON column
156
176
  ```
157
177
  ```rb
158
- Shrine.plugin :sequel
178
+ Shrine.plugin :sequel # or :activerecord
159
179
  ```
160
180
  ```rb
161
181
  class User < Sequel::Model
@@ -175,30 +195,17 @@ user.destroy
175
195
  user.avatar.exists? #=> false
176
196
  ```
177
197
 
178
- This is how you would typically create the form for a `@user`:
179
-
180
- ```erb
181
- <form action="/users" method="post" enctype="multipart/form-data">
182
- <input name="user[avatar]" type="hidden" value="<%= @user.avatar_data %>">
183
- <input name="user[avatar]" type="file">
184
- </form>
185
- ```
186
-
187
- The "file" field is for file upload, while the "hidden" field is to make the
188
- file persist in case of validation errors, and for direct uploads.
189
-
190
198
  ## Direct uploads
191
199
 
192
- Shrine comes with a `direct_upload` plugin which provides an endpoint
193
- (implemented in [Roda]) that can be used for AJAX uploads.
200
+ Shrine comes with a `direct_upload` plugin which provides a [Roda] endpoint
201
+ that can be used for AJAX uploads (using any JavaScript file upload library):
194
202
 
195
203
  ```rb
196
204
  Shrine.plugin :direct_upload # Provides a Roda endpoint
197
205
  ```
198
206
  ```rb
199
207
  Rails.application.routes.draw do
200
- # adds `POST /attachments/images/:storage/:name`
201
- mount ImageUploader.direct_endpoint => "/attachments/images"
208
+ mount ImageUploader::UploadEndpoint => "/attachments/images"
202
209
  end
203
210
  ```
204
211
  ```rb
@@ -214,21 +221,9 @@ end
214
221
  }
215
222
  ```
216
223
 
217
- There are many great JavaScript libraries for AJAX file uploads, for example
218
- this is how we could hook up [jQuery-File-Upload] to our endpoint:
219
-
220
- ```js
221
- $('[type="file"]').fileupload({
222
- url: '/attachments/images/cache/avatar',
223
- paramName: 'file',
224
- done: function(e, data) { $(this).prev().value(data.result) }
225
- });
226
- ```
227
-
228
- This is an oversimplified implementation without any UX, it's just to show you
229
- how easy it is. The `direct_upload` plugin also provides a route for direct S3
230
- uploads, see the [example app] for how you can do multiple uploads directly to
231
- S3.
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.
232
227
 
233
228
  ## Processing
234
229
 
@@ -248,8 +243,8 @@ end
248
243
  The `io` is the file being uploaded, and `context` we'll leave for later. You
249
244
  may be wondering why we need this conditional. Well, when an attachment is
250
245
  assigned and saved, an "upload" actually happens two times. First the file is
251
- "uploaded" to `:cache` on assignment, and then the cached file is reuploaded to
252
- `:store` on save.
246
+ "uploaded" to cache on assignment, and then the cached file is reuploaded to
247
+ store on save.
253
248
 
254
249
  Ok, now how do we do the actual processing? Well, Shrine actually doesn't ship
255
250
  with any file processing functionality, because that is a generic problem that
@@ -271,8 +266,8 @@ end
271
266
  ```
272
267
 
273
268
  Notice that we needed to call `io.download`. This is because the original file
274
- was already stored to `:cache`, and now this cached file is being uploaded to
275
- `:store`. The cached file is an instance of `Shrine::UploadedFile`, but for
269
+ was already stored to cache, and now this cached file is being uploaded to
270
+ store. The cached file is an instance of `Shrine::UploadedFile`, but for
276
271
  processing we need to work with actual files, so we first need to download it.
277
272
 
278
273
  In general, processing works in a way that if `#process` returns a file, Shrine
@@ -456,20 +451,6 @@ class ImageUploader < Shrine
456
451
  end
457
452
  ```
458
453
 
459
- ## Default URL
460
-
461
- When attachment is missing, `user.avatar_url` by default returns nil. This
462
- because it internally calls `Shrine#default_url`, which returns nil unless
463
- overriden. For custom default URLs simply override the method:
464
-
465
- ```rb
466
- class ImageUploader < Shrine
467
- def default_url(context)
468
- "/images/fallback/#{context[:name]}.png"
469
- end
470
- end
471
- ```
472
-
473
454
  ## Locations
474
455
 
475
456
  By default files will all be put in the same folder. If you want that each
@@ -489,73 +470,75 @@ If you want to generate your own locations, simply override
489
470
  ```rb
490
471
  class ImageUploader < Shrine
491
472
  def generate_location(io, context)
492
- # your custom logic
473
+ "#{context[:record].class}/#{context[:record].id}/#{io.original_filename}"
493
474
  end
494
475
  end
495
476
  ```
496
477
 
497
- Note that in this case should be careful to make the locations unique,
498
- otherwise dirty tracking won't be detected properly (you can use
499
- `Shrine#generate_uid`).
478
+ Note that in this case should make your locations unique, otherwise dirty
479
+ tracking won't be detected properly (you can use `Shrine#generate_uid`).
500
480
 
501
481
  When using `Shrine` directly you can bypass `#generate_location` by passing in
502
482
  `:location`
503
483
 
504
484
  ```rb
505
485
  file = File.open("avatar.jpg")
506
- Shrine.new(:store).upload(file, location: "a/specific/location.jpg")
486
+ Shrine.new(:store).upload(file, location: "some/specific/location.jpg")
507
487
  ```
508
488
 
509
- ## Amazon S3
489
+ ## Storage
510
490
 
511
- So far in the examples we've only used the FileSystem storage. However, Shrine
512
- also ships with S3 storage (which internally uses the [aws-sdk] gem).
491
+ Other than [FileSystem], Shrine also ships with [S3] storage:
513
492
 
514
- ```
493
+ ```rb
515
494
  gem "aws-sdk", "~> 2.1"
516
495
  ```
517
-
518
- It's typically good to use FileSystem for `:cache`, and S3 for `:store`:
519
-
520
496
  ```rb
521
- require "shrine"
522
- require "shrine/storage/file_system"
523
497
  require "shrine/storage/s3"
524
498
 
525
- s3_options = {
499
+ Shrine.storages[:store] = Shrine::Storage::S3.new(
526
500
  access_key_id: "<ACCESS_KEY_ID>", # "xyz"
527
501
  secret_access_key: "<SECRET_ACCESS_KEY>", # "abc"
528
502
  region: "<REGION>", # "eu-west-1"
529
503
  bucket: "<BUCKET>", # "my-app"
530
- }
531
-
532
- Shrine.storages = {
533
- cache: Shrine::Storage::FileSystem.new("public", subdirectory: "uploads"),
534
- store: Shrine::Storage::S3.new(s3_options),
535
- }
504
+ )
536
505
  ```
537
506
 
538
507
  ```rb
539
508
  user = User.new(avatar: File.open(:avatar))
540
509
  user.avatar.url #=> "/uploads/j4k343ui12ls9.jpg"
541
510
  user.save
542
- user.avatar.url #=> "https://s3-sa-east-1.amazonaws.com/my-bucket/0943sf8gfk13.jpg"
511
+ user.avatar.url #=> "https://my-bucket.s3-eu-west-1.amazonaws.com/0943sf8gfk13.jpg"
543
512
  ```
544
513
 
545
- If you're using S3 for both `:cache` and `:store`, saving the record will
546
- execute an S3 COPY command if possible, which avoids reuploading the file.
547
- Also, the `versions` plugin takes advantage of S3's MULTI DELETE capabilities,
548
- so versions are deleted with a single HTTP request.
514
+ If you're using S3 for both cache and store, saving the record will avoid
515
+ reuploading the file by issuing an S3 COPY command instead. Also, the
516
+ `versions` plugin takes advantage of S3's MULTI DELETE capabilities, so
517
+ versions are deleted with a single HTTP request.
518
+
519
+ See the full documentation for [FileSystem] and [S3] storages. There are also
520
+ many other Shrine storages available, see the [Plugins & Storages] section.
521
+
522
+ ### Clearing cache
523
+
524
+ You will want to periodically clean your cache storage. Amazon S3 provides [a
525
+ built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
526
+ and for FileSystem you can put something like this in your Rake task:
527
+
528
+ ```rb
529
+ file_system = Shrine.storages[:cache]
530
+ file_system.clear!(older_than: 1.week.ago) # adjust the time
531
+ ```
549
532
 
550
533
  ## Background jobs
551
534
 
552
- Unlike other uploading libraries, Shrine embraces that putting phases of file
553
- upload into background jobs is essential for scaling and good user experience,
554
- so it ships with `background_helpers` plugin which makes backgrounding really
555
- easy:
535
+ Shrine is the first uploading library designed from day one to be used with
536
+ background jobs. Backgrounding parts of file upload is essential for scaling
537
+ and good user experience, and Shrine provides a `backgrounding` plugin which
538
+ makes it really easy to plug in your backgrounding library:
556
539
 
557
540
  ```rb
558
- Shrine.plugin :background_helpers
541
+ Shrine.plugin :backgrounding
559
542
  Shrine::Attacher.promote { |data| UploadJob.perform_async(data) }
560
543
  Shrine::Attacher.delete { |data| DeleteJob.perform_async(data) }
561
544
  ```
@@ -580,61 +563,27 @@ The above puts all promoting (moving to store) and deleting of files into a
580
563
  background Sidekiq job. Obviously instead of Sidekiq you can just as well use
581
564
  any other backgrounding library.
582
565
 
583
- ### Seamless user experience
584
-
585
- In combination with direct upload for caching, this provides a completely
586
- seamless user experience. First the user ansynchronosuly caches the file and
587
- hopefully sees a nice progress bar. After this is finishes and user submits the
588
- form, promoting will be kicked off into a background job, and the record will
589
- be saved with the cached file If your cache is public (e.g. in the "public"
590
- folder), the end user will immediately see their uploaded file, because the URL
591
- will point to the cached version.
592
-
593
- In the meanwhile, what `#promote` does is it uploads the cached file `:store`,
594
- and writes the stored file to the column. When the record gets saved, the URL
595
- will switch from filesystem to S3, but the user won't even notice that
596
- something happened, because they will still see the same file.
597
-
598
- ### Generality
599
-
600
- This solution is completely agnostic about what kind of attachment it is
601
- uploading/deleting, and for which model. This means that all attachments can
602
- use this same worker. Also, there is no need for any extra columns.
603
-
604
- ### Safety
605
-
606
- It is possible that the user changes their mind and reuploads a new file before
607
- the background job finished promoting. With a naive implementation, this means
608
- that after uploading a new file, there can happen a brief moment where the user
609
- sees the old file again, which can be upsetting.
610
-
611
- Shrine handles this gracefully. After `#promote` uploads the cached file to
612
- `:store`, it checks if the cached file still matches the file in the record
613
- column. If the files are different, that means the user uploaded a new
614
- attachment, and Shrine won't do the replacement. Additionally, this job is
615
- idempotent, meaning it can be safely repeated in case of failure.
616
-
617
- ## Clearing cache
618
-
619
- Your `:cache` storage will grow over time, so you'll want to periodically clean
620
- it. If you're using FileSystem as your `:cache`, you can put this in a
621
- scheduled job:
622
-
623
- ```rb
624
- file_system = Shrine.storages[:cache]
625
- file_system.clear!(older_than: 1.week.ago) # adjust the time
626
- ```
627
-
628
- If your `:cache` is S3, Amazon provides settings for automatic cache clearing,
629
- see [this article](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html).
566
+ The main advantages of Shrine's backgrounding support over other file upload
567
+ libraries are:
568
+
569
+ * **User experience** After starting the background job, Shrine will save the
570
+ record with the cached attachment so that it can be immediately shown to the
571
+ user. With other file upload libraries users cannot see the file until the
572
+ background job has finished, which is really lame.
573
+ * **Simplicity** Instead of writing the workers for you, Shrine allows you
574
+ to use your own workers in a very simple way. Also, no extra columns are
575
+ required.
576
+ * **Generality** The above solution will automatically work for all uploaders,
577
+ types of files and models.
578
+ * **Safety** All of Shrine's code has been designed to take delayed storing
579
+ into account, so concurrency issues should be nonexistent.
630
580
 
631
581
  ## Plugins
632
582
 
633
- Shrine comes with a small core which provides only the essential functionality.
634
- However, it comes with a lot of additional features which can be loaded via
635
- plugins. This way you can choose exactly how much Shrine does for you. Shrine
636
- itself [ships with over 25 plugins], most of them I haven't managed to cover
637
- here.
583
+ Shrine comes with a small core which provides only the essential functionality,
584
+ and all additional features are available via plugins. This way you can choose
585
+ exactly how much Shrine does for you. Shrine itself [ships with over 35
586
+ plugins], most of them I haven't managed to cover here.
638
587
 
639
588
  The plugin system respects inheritance, so you can choose which plugins will
640
589
  be applied to which uploaders:
@@ -670,5 +619,8 @@ The gem is available as open source under the terms of the [MIT License].
670
619
  [plugin system]: http://twin.github.io/the-plugin-system-of-sequel-and-roda/
671
620
  [MIT License]: http://opensource.org/licenses/MIT
672
621
  [example app]: https://github.com/janko-m/shrine-example
673
- [ships with over 25 plugins]: http://shrinerb.com#plugins
622
+ [ships with over 35 plugins]: http://shrinerb.com#plugins
674
623
  [introductory blog post]: http://twin.github.io/introducing-shrine/
624
+ [FileSystem]: http://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
625
+ [S3]: http://shrinerb.com/rdoc/classes/Shrine/Storage/S3.html
626
+ [Plugins & Storages]: http://shrinerb.com#external
data/doc/carrierwave.md CHANGED
@@ -25,8 +25,8 @@ instantiate uploaders with a specific storage.
25
25
  require "shrine/storage/file_system"
26
26
 
27
27
  Shrine.storages = {
28
- cache: Shrine::Storage::FileSystem.new("public", subdirectory: "uploads/cache"),
29
- store: Shrine::Storage::FileSystem.new("public", subdirectory: "uploads/store"),
28
+ cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
29
+ store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
30
30
  }
31
31
  ```
32
32
  ```rb
@@ -105,12 +105,12 @@ for store. If you want to change that, you can use the `default_storage`
105
105
  plugin:
106
106
 
107
107
  ```rb
108
- Shrine.storages[:dropbox] = Shrine::Storage::Dropbox.new(*args)
108
+ Shrine.storages[:foo] = Shrine::Storage::Foo.new(*args)
109
109
  ```
110
110
 
111
111
  ```rb
112
112
  class ImageUploader
113
- plugin :default_storage, store: :dropbox
113
+ plugin :default_storage, store: :foo
114
114
  end
115
115
  ```
116
116
 
@@ -185,10 +185,7 @@ storages:
185
185
  ```rb
186
186
  class ImageUploader < Shrine
187
187
  def generate_location(io, context)
188
- case storage_key
189
- when :cache then "..."
190
- when :store then "..."
191
- end
188
+ "#{context[:record].class}/#{context[:record].id}/#{io.original_filename}"
192
189
  end
193
190
  end
194
191
  ```
@@ -199,13 +196,12 @@ for automatically generating an organized folder structure.
199
196
 
200
197
  #### `#default_url`
201
198
 
202
- Similarly to CarrierWave, you also provide default URLs be overriding the
203
- method:
199
+ For default URLs you can use the `default_url` plugin:
204
200
 
205
201
  ```rb
206
202
  class ImageUploader < Shrine
207
- def default_url(context)
208
- # ...
203
+ plugin :default_url do |context|
204
+ "/attachments/#{context[:name]}/default.jpg"
209
205
  end
210
206
  end
211
207
  ```
@@ -264,10 +260,10 @@ end
264
260
 
265
261
  #### `#recreate_versions!`
266
262
 
267
- Shrine doesn't provide an automatic mechanism for recreating versions, because
268
- that is very individual for different situations. For example, sometimes you want to
269
- regenerate all versions and sometimes just one. However, I wrote a guide
270
- "[Regenerating versions]" that should help you out with that.
263
+ Shrine doesn't have a built-in way of regenerating versions, because that's
264
+ very individual and depends on what versions you want regenerated, what ORM are
265
+ you using, how many records there are in your database etc. The [Regenerating
266
+ versions] guide provides some useful tips on this task.
271
267
 
272
268
  ### Models
273
269