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.
- checksums.yaml +4 -4
- data/README.md +101 -149
- data/doc/carrierwave.md +12 -16
- data/doc/changing_location.md +50 -0
- data/doc/creating_plugins.md +2 -2
- data/doc/creating_storages.md +70 -9
- data/doc/direct_s3.md +132 -61
- data/doc/migrating_storage.md +12 -10
- data/doc/paperclip.md +12 -17
- data/doc/refile.md +338 -0
- data/doc/regenerating_versions.md +75 -11
- data/doc/securing_uploads.md +172 -0
- data/lib/shrine.rb +21 -16
- data/lib/shrine/plugins/activerecord.rb +2 -2
- data/lib/shrine/plugins/background_helpers.rb +2 -148
- data/lib/shrine/plugins/backgrounding.rb +148 -0
- data/lib/shrine/plugins/backup.rb +88 -0
- data/lib/shrine/plugins/data_uri.rb +25 -4
- data/lib/shrine/plugins/default_url.rb +37 -0
- data/lib/shrine/plugins/delete_uploaded.rb +40 -0
- data/lib/shrine/plugins/determine_mime_type.rb +4 -2
- data/lib/shrine/plugins/direct_upload.rb +107 -62
- data/lib/shrine/plugins/download_endpoint.rb +157 -0
- data/lib/shrine/plugins/hooks.rb +19 -5
- data/lib/shrine/plugins/keep_location.rb +43 -0
- data/lib/shrine/plugins/moving.rb +11 -10
- data/lib/shrine/plugins/parallelize.rb +1 -5
- data/lib/shrine/plugins/parsed_json.rb +7 -1
- data/lib/shrine/plugins/pretty_location.rb +6 -0
- data/lib/shrine/plugins/rack_file.rb +7 -1
- data/lib/shrine/plugins/remove_invalid.rb +22 -0
- data/lib/shrine/plugins/sequel.rb +2 -2
- data/lib/shrine/plugins/upload_options.rb +41 -0
- data/lib/shrine/plugins/versions.rb +9 -7
- data/lib/shrine/storage/file_system.rb +46 -30
- data/lib/shrine/storage/linter.rb +48 -25
- data/lib/shrine/storage/s3.rb +89 -22
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +3 -3
- metadata +16 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11b0d1c76734507b02ec22fb1ae1aef8a8c704e7
|
4
|
+
data.tar.gz: 397c1df52d016428395879fbc8bfba4c0dbb7db7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
48
|
+
with the storage name, and when we call `#upload` Shrine does the following:
|
49
49
|
|
50
|
-
* a unique location
|
51
|
-
* metadata
|
52
|
-
* the
|
53
|
-
*
|
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
|
84
|
-
|
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",
|
93
|
-
store: Shrine::Storage::FileSystem.new("public",
|
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
|
-
|
98
|
-
|
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
|
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
|
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
|
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
|
193
|
-
|
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
|
-
|
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
|
-
|
218
|
-
|
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
|
252
|
-
|
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
|
275
|
-
|
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
|
-
#
|
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
|
498
|
-
|
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: "
|
486
|
+
Shrine.new(:store).upload(file, location: "some/specific/location.jpg")
|
507
487
|
```
|
508
488
|
|
509
|
-
##
|
489
|
+
## Storage
|
510
490
|
|
511
|
-
|
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
|
-
|
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-
|
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
|
546
|
-
|
547
|
-
|
548
|
-
|
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
|
-
|
553
|
-
|
554
|
-
|
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 :
|
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
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
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
|
-
|
635
|
-
|
636
|
-
|
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
|
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",
|
29
|
-
store: Shrine::Storage::FileSystem.new("public",
|
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[:
|
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: :
|
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
|
-
|
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
|
-
|
203
|
-
method:
|
199
|
+
For default URLs you can use the `default_url` plugin:
|
204
200
|
|
205
201
|
```rb
|
206
202
|
class ImageUploader < Shrine
|
207
|
-
|
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
|
268
|
-
|
269
|
-
|
270
|
-
|
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
|
|