shrine 2.19.4 → 3.0.0.alpha
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/CHANGELOG.md +299 -11
- data/README.md +9 -3
- data/doc/advantages.md +1 -1
- data/doc/carrierwave.md +4 -4
- data/doc/creating_persistence_plugins.md +172 -0
- data/doc/creating_plugins.md +1 -1
- data/doc/creating_storages.md +3 -1
- data/doc/design.md +2 -2
- data/doc/direct_s3.md +0 -22
- data/doc/paperclip.md +3 -3
- data/doc/plugins/activerecord.md +211 -42
- data/doc/plugins/atomic_helpers.md +153 -0
- data/doc/plugins/column.md +90 -0
- data/doc/plugins/derivation_endpoint.md +54 -62
- data/doc/plugins/derivatives.md +752 -0
- data/doc/plugins/entity.md +204 -0
- data/doc/plugins/infer_extension.md +8 -8
- data/doc/plugins/instrumentation.md +33 -13
- data/doc/plugins/keep_files.md +5 -15
- data/doc/plugins/model.md +157 -0
- data/doc/plugins/presign_endpoint.md +2 -1
- data/doc/plugins/refresh_metadata.md +44 -7
- data/doc/plugins/sequel.md +190 -33
- data/doc/plugins/{default_url_options.md → url_options.md} +5 -5
- data/doc/processing.md +1 -1
- data/doc/release_notes/1.1.0.md +2 -2
- data/doc/release_notes/2.15.0.md +1 -1
- data/doc/storage/s3.md +2 -2
- data/doc/testing.md +1 -1
- data/lib/shrine.rb +72 -138
- data/lib/shrine/attacher.rb +272 -176
- data/lib/shrine/attachment.rb +2 -42
- data/lib/shrine/plugins/activerecord.rb +103 -26
- data/lib/shrine/plugins/add_metadata.rb +9 -10
- data/lib/shrine/plugins/atomic_helpers.rb +111 -0
- data/lib/shrine/plugins/attacher_options.rb +55 -0
- data/lib/shrine/plugins/backgrounding.rb +147 -115
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -9
- data/lib/shrine/plugins/column.rb +104 -0
- data/lib/shrine/plugins/data_uri.rb +35 -38
- data/lib/shrine/plugins/default_storage.rb +18 -12
- data/lib/shrine/plugins/default_url.rb +11 -21
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +9 -13
- data/lib/shrine/plugins/derivation_endpoint.rb +75 -114
- data/lib/shrine/plugins/derivatives.rb +576 -0
- data/lib/shrine/plugins/determine_mime_type.rb +3 -15
- data/lib/shrine/plugins/download_endpoint.rb +83 -131
- data/lib/shrine/plugins/dynamic_storage.rb +4 -8
- data/lib/shrine/plugins/entity.rb +128 -0
- data/lib/shrine/plugins/form_assign.rb +107 -0
- data/lib/shrine/plugins/included.rb +4 -3
- data/lib/shrine/plugins/infer_extension.rb +10 -17
- data/lib/shrine/plugins/instrumentation.rb +45 -25
- data/lib/shrine/plugins/keep_files.rb +2 -12
- data/lib/shrine/plugins/metadata_attributes.rb +15 -14
- data/lib/shrine/plugins/model.rb +137 -0
- data/lib/shrine/plugins/module_include.rb +2 -0
- data/lib/shrine/plugins/presign_endpoint.rb +1 -15
- data/lib/shrine/plugins/pretty_location.rb +5 -5
- data/lib/shrine/plugins/processing.rb +21 -6
- data/lib/shrine/plugins/rack_file.rb +1 -39
- data/lib/shrine/plugins/rack_response.rb +14 -7
- data/lib/shrine/plugins/recache.rb +5 -2
- data/lib/shrine/plugins/refresh_metadata.rb +12 -8
- data/lib/shrine/plugins/remote_url.rb +44 -53
- data/lib/shrine/plugins/remove_attachment.rb +7 -2
- data/lib/shrine/plugins/remove_invalid.rb +8 -4
- data/lib/shrine/plugins/restore_cached_data.rb +12 -4
- data/lib/shrine/plugins/sequel.rb +115 -27
- data/lib/shrine/plugins/signature.rb +2 -7
- data/lib/shrine/plugins/store_dimensions.rb +13 -27
- data/lib/shrine/plugins/upload_endpoint.rb +14 -15
- data/lib/shrine/plugins/upload_options.rb +9 -8
- data/lib/shrine/plugins/url_options.rb +33 -0
- data/lib/shrine/plugins/validation.rb +87 -0
- data/lib/shrine/plugins/validation_helpers.rb +33 -54
- data/lib/shrine/plugins/versions.rb +106 -84
- data/lib/shrine/storage/file_system.rb +32 -57
- data/lib/shrine/storage/linter.rb +9 -1
- data/lib/shrine/storage/memory.rb +42 -0
- data/lib/shrine/storage/s3.rb +38 -146
- data/lib/shrine/uploaded_file.rb +22 -29
- data/lib/shrine/version.rb +4 -4
- data/shrine.gemspec +2 -3
- metadata +27 -54
- data/doc/plugins/backup.md +0 -31
- data/doc/plugins/copy.md +0 -24
- data/doc/plugins/delete_promoted.md +0 -12
- data/doc/plugins/direct_upload.md +0 -172
- data/doc/plugins/hooks.md +0 -58
- data/doc/plugins/logging.md +0 -42
- data/doc/plugins/migration_helpers.md +0 -60
- data/doc/plugins/moving.md +0 -19
- data/doc/plugins/multi_delete.md +0 -20
- data/doc/plugins/parallelize.md +0 -16
- data/doc/plugins/parsed_json.md +0 -23
- data/lib/shrine/plugins/background_helpers.rb +0 -5
- data/lib/shrine/plugins/backup.rb +0 -90
- data/lib/shrine/plugins/copy.rb +0 -50
- data/lib/shrine/plugins/delete_promoted.rb +0 -20
- data/lib/shrine/plugins/direct_upload.rb +0 -217
- data/lib/shrine/plugins/hooks.rb +0 -90
- data/lib/shrine/plugins/logging.rb +0 -142
- data/lib/shrine/plugins/migration_helpers.rb +0 -70
- data/lib/shrine/plugins/moving.rb +0 -57
- data/lib/shrine/plugins/multi_delete.rb +0 -32
- data/lib/shrine/plugins/parallelize.rb +0 -78
- data/lib/shrine/plugins/parsed_json.rb +0 -29
@@ -0,0 +1,90 @@
|
|
1
|
+
# Column
|
2
|
+
|
3
|
+
The [`column`][column] plugin provides interface for serializing and
|
4
|
+
deserializing attachment data in format suitable for persisting in a database
|
5
|
+
column (JSON by default).
|
6
|
+
|
7
|
+
```rb
|
8
|
+
plugin :column
|
9
|
+
```
|
10
|
+
|
11
|
+
## Serializing
|
12
|
+
|
13
|
+
The `Attacher#column_data` method returns attached file data in serialized
|
14
|
+
format, ready to be persisted into a database column.
|
15
|
+
|
16
|
+
```rb
|
17
|
+
attacher.attach(io)
|
18
|
+
attacher.column_data #=> '{"id":"...","storage":"...","metadata":{...}}'
|
19
|
+
```
|
20
|
+
|
21
|
+
If there is no attached file, `nil` is returned.
|
22
|
+
|
23
|
+
```rb
|
24
|
+
attacher.column_data #=> nil
|
25
|
+
```
|
26
|
+
|
27
|
+
If you want to retrieve this data as a Hash, use `Attacher#data` instead.
|
28
|
+
|
29
|
+
## Deserializing
|
30
|
+
|
31
|
+
The `Attacher.from_column` method instantiates the attacher from serialized
|
32
|
+
attached file data.
|
33
|
+
|
34
|
+
```rb
|
35
|
+
attacher = Shrine::Attacher.from_column('{"id":"...","storage":"...","metadata":{...}}')
|
36
|
+
attacher.file #=> #<Shrine::UploadedFile>
|
37
|
+
```
|
38
|
+
|
39
|
+
If `nil` is given, it means no attached file.
|
40
|
+
|
41
|
+
```rb
|
42
|
+
attacher = Shrine::Attacher.from_column(nil)
|
43
|
+
attacher.file #=> nil
|
44
|
+
```
|
45
|
+
|
46
|
+
Any additional options are forwarded to `Attacher#initialize`.
|
47
|
+
|
48
|
+
```rb
|
49
|
+
attacher = Shrine::Attacher.from_column('{...}', store: :other_store)
|
50
|
+
attacher.store_key #=> :other_store
|
51
|
+
```
|
52
|
+
|
53
|
+
If you want to load attachment data into an existing attacher, use
|
54
|
+
`Attacher#load_column`.
|
55
|
+
|
56
|
+
```rb
|
57
|
+
attacher.file #=> nil
|
58
|
+
attacher.load_column('{"id":"...","storage":"...","metadata":{...}}')
|
59
|
+
attacher.file #=> #<Shrine::UploadedFile>
|
60
|
+
```
|
61
|
+
|
62
|
+
If you want to load attachment from a Hash, use `Attacher.from_data` or
|
63
|
+
`Attacher#load_data` instead.
|
64
|
+
|
65
|
+
## Serializer
|
66
|
+
|
67
|
+
By default the `JSON` standard library is used as the serializer, but you can
|
68
|
+
use your own serializer. The serializer object needs to implement `#dump` and
|
69
|
+
`#load` methods.
|
70
|
+
|
71
|
+
```rb
|
72
|
+
require "oj"
|
73
|
+
|
74
|
+
plugin :column, serializer: Oj # use custom serializer
|
75
|
+
```
|
76
|
+
|
77
|
+
If you want to disable serialization, you can set serializer to `nil`.
|
78
|
+
|
79
|
+
```rb
|
80
|
+
plugin :column, serializer: nil # disable serialization
|
81
|
+
```
|
82
|
+
|
83
|
+
You can also change the serializer on the attacher level:
|
84
|
+
|
85
|
+
```rb
|
86
|
+
Shrine::Attacher.new(column_serializer: Oj) # use custom serializer
|
87
|
+
Shrine::Attacher.new(column_serializer: nil) # disable serialization
|
88
|
+
```
|
89
|
+
|
90
|
+
[column]: /lib/shrine/plugins/column.rb
|
@@ -76,7 +76,7 @@ Now we can generate "derivation" URLs from attached files, which on request
|
|
76
76
|
will call the derivation block we defined.
|
77
77
|
|
78
78
|
```rb
|
79
|
-
photo.image.derivation_url(:thumbnail,
|
79
|
+
photo.image.derivation_url(:thumbnail, 600, 400)
|
80
80
|
#=> "/derivations/image/thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
|
81
81
|
```
|
82
82
|
|
@@ -102,10 +102,14 @@ derivation URLs, preventing potential DoS attacks.
|
|
102
102
|
|
103
103
|
The derivation endpoint then extracts the source file data, derivation name and
|
104
104
|
arguments from the request URL, and calls the corresponding derivation block,
|
105
|
-
passing the downloaded source file and derivation arguments.
|
105
|
+
passing the downloaded source file and derivation arguments. The derivation
|
106
|
+
block is evaluated within the context of a
|
107
|
+
[`Shrine::Derivation`](#derivation-api) instance.
|
106
108
|
|
107
109
|
```rb
|
108
110
|
derivation :thumbnail do |file, arg1, arg2, ...|
|
111
|
+
self #=> #<Shrine::Derivation>
|
112
|
+
|
109
113
|
file #=> #<Tempfile:...> (source file downloaded to disk)
|
110
114
|
arg1 #=> "600" (first derivation argument in #derivation_url)
|
111
115
|
arg2 #=> "400" (second derivation argument in #derivation_url)
|
@@ -494,11 +498,9 @@ sense.
|
|
494
498
|
|
495
499
|
### Deleting derivatives
|
496
500
|
|
497
|
-
When
|
498
|
-
|
499
|
-
|
500
|
-
delete the derivatives before deleting the record or updating the record with
|
501
|
-
the new original file.
|
501
|
+
When the original attachment is deleted, its uploaded derivatives will not be
|
502
|
+
automatically deleted, you will need to do the deletion manually. You can do
|
503
|
+
that by calling `Shrine::Derivation#delete` for each derivation you're using:
|
502
504
|
|
503
505
|
```rb
|
504
506
|
# photo is the model and image is the file attachment
|
@@ -538,50 +540,38 @@ uploaded_file.derivation_url(:thumbnail, version: 1)
|
|
538
540
|
|
539
541
|
## Accessing source file
|
540
542
|
|
541
|
-
|
542
|
-
|
543
|
+
Inside the derivation block we can access the source `UploadedFile` object via
|
544
|
+
`Shrine::Derivation#source`:
|
543
545
|
|
544
546
|
```rb
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
```rb
|
552
|
-
derivation :thumbnail do |file, uploaded_file, width, height|
|
553
|
-
uploaded_file #=> #<Shrine::UploadedFile>
|
554
|
-
uploaded_file.id #=> "9a7d1bfdad24a76f9cfaff137fe1b5c7.jpg"
|
555
|
-
uploaded_file.storage_key #=> "store"
|
556
|
-
uploaded_file.metadata #=> {}
|
547
|
+
derivation :thumbnail do |file, width, height|
|
548
|
+
source #=> #<Shrine::UploadedFile>
|
549
|
+
source.id #=> "9a7d1bfdad24a76f9cfaff137fe1b5c7.jpg"
|
550
|
+
source.storage_key #=> :store
|
551
|
+
source.metadata #=> {}
|
557
552
|
|
558
553
|
# ...
|
559
554
|
end
|
560
555
|
```
|
561
556
|
|
562
|
-
By default
|
563
|
-
available in the derivation block. This is because metadata
|
564
|
-
available would need to be serialized into the derivation
|
565
|
-
it longer.
|
566
|
-
|
557
|
+
By default, when using the derivation endpoint, original metadata of the source
|
558
|
+
file won't be available in the derivation block. This is because any metadata
|
559
|
+
we would want to have available would need to be serialized into the derivation
|
560
|
+
URL, which would make it longer. Instead, you can opt in for the metadata you
|
561
|
+
want to have available:
|
567
562
|
|
568
563
|
```rb
|
569
564
|
plugin :derivation_endpoint, metadata: ["filename", "mime_type"]
|
570
|
-
```
|
571
|
-
|
572
|
-
Now `filename` and `mime_type` metadata values will be available in the
|
573
|
-
derivation block:
|
574
565
|
|
575
|
-
|
576
|
-
|
577
|
-
uploaded_file.metadata #=>
|
566
|
+
derivation :thumbnail do |file, width, height|
|
567
|
+
source.metadata #=>
|
578
568
|
# {
|
579
569
|
# "filename" => "nature.jpg",
|
580
570
|
# "mime_type" => "image/jpeg"
|
581
571
|
# }
|
582
572
|
|
583
|
-
|
584
|
-
|
573
|
+
source.original_filename #=> "nature.jpg"
|
574
|
+
source.mime_type #=> "image/jpeg"
|
585
575
|
|
586
576
|
# ...
|
587
577
|
end
|
@@ -602,17 +592,9 @@ plugin :derivation_endpoint, download_options: {
|
|
602
592
|
}
|
603
593
|
```
|
604
594
|
|
605
|
-
If the source file
|
606
|
-
|
607
|
-
`
|
608
|
-
these errors converted to 404 responses by adding them to `:download_errors`:
|
609
|
-
|
610
|
-
```rb
|
611
|
-
plugin :derivation_endpoint, download_errors: [
|
612
|
-
Errno::ENOENT, # raised by Shrine::Storage::FileSystem
|
613
|
-
Aws::S3::Errors::NotFound, # raised by Shrine::Storage::S3
|
614
|
-
]
|
615
|
-
```
|
595
|
+
If the source file was not found, `Shrine::Derivation::SourceNotFound`
|
596
|
+
exception is raised. In a derivation response this is converted into a `404 Not
|
597
|
+
Found` response.
|
616
598
|
|
617
599
|
### Skipping download
|
618
600
|
|
@@ -621,15 +603,8 @@ disk for you, you can set `:download` to `false`.
|
|
621
603
|
|
622
604
|
```rb
|
623
605
|
plugin :derivation_endpoint, download: false
|
624
|
-
```
|
625
|
-
|
626
|
-
In this case the `UploadedFile` object is yielded to the derivation block
|
627
|
-
instead of the raw file:
|
628
|
-
|
629
|
-
```rb
|
630
|
-
derivation :thumbnail do |uploaded_file, width, height|
|
631
|
-
uploaded_file #=> #<Shrine::UploadedFile>
|
632
606
|
|
607
|
+
derivation :thumbnail do |width, height| # source file is not downloaded
|
633
608
|
# ...
|
634
609
|
end
|
635
610
|
```
|
@@ -639,10 +614,10 @@ One use case for this is delegating processing to a 3rd-party service:
|
|
639
614
|
```rb
|
640
615
|
require "down/http"
|
641
616
|
|
642
|
-
derivation :thumbnail do |
|
617
|
+
derivation :thumbnail do |width, height|
|
643
618
|
# generate the thumbnail using ImageOptim.com
|
644
619
|
down = Down::Http.new(method: :post)
|
645
|
-
down.download("https://im2.io/<USERNAME>/#{width}x#{height}/#{
|
620
|
+
down.download("https://im2.io/<USERNAME>/#{width}x#{height}/#{source.url}")
|
646
621
|
end
|
647
622
|
```
|
648
623
|
|
@@ -735,13 +710,20 @@ uploaded_file #=> #<Shrine::UploadedFile>
|
|
735
710
|
uploaded_file.id #=> "bcfd0d67e4a8ec2dc9a6d7ddcf3825a1/thumbnail-500-500"
|
736
711
|
```
|
737
712
|
|
738
|
-
|
713
|
+
It can also be called without arguments, in which case it will generate a new
|
714
|
+
derivative and upload it.
|
715
|
+
|
716
|
+
```rb
|
717
|
+
derivation.upload # generates derivative and uploads it
|
718
|
+
```
|
719
|
+
|
720
|
+
Any additional options will be passed to the uploader.
|
739
721
|
|
740
722
|
### `#retrieve`
|
741
723
|
|
742
|
-
`Derivation#retrieve` method returns
|
743
|
-
|
744
|
-
returned.
|
724
|
+
`Derivation#retrieve` method returns `Shrine::UploadedFile` object pointing to
|
725
|
+
the uploaded derivative if it exists. If the uploaded derivative does not
|
726
|
+
exist, `nil` is returned.
|
745
727
|
|
746
728
|
```rb
|
747
729
|
uploaded_file = derivation.retrieve
|
@@ -749,6 +731,18 @@ uploaded_file #=> #<Shrine::UploadedFile>
|
|
749
731
|
uploaded_file.id #=> "bcfd0d67e4a8ec2dc9a6d7ddcf3825a1/thumbnail-500-500"
|
750
732
|
```
|
751
733
|
|
734
|
+
### `#opened`
|
735
|
+
|
736
|
+
`Derivation#opened` method returns opened `Shrine::UploadedFile` object pointing
|
737
|
+
to the uploaded derivative if it exists. If the uploaded derivative does not
|
738
|
+
exist, `nil` is returned.
|
739
|
+
|
740
|
+
```rb
|
741
|
+
uploaded_file = derivation.opened
|
742
|
+
uploaded_file #=> #<Shrine::UploadedFile>
|
743
|
+
uploaded_file.id #=> "bcfd0d67e4a8ec2dc9a6d7ddcf3825a1/thumbnail-500-500"
|
744
|
+
```
|
745
|
+
|
752
746
|
### `#delete`
|
753
747
|
|
754
748
|
`Derivation#delete` method deletes the uploaded derivative file from the
|
@@ -774,12 +768,10 @@ derivation.option(:upload_location)
|
|
774
768
|
| `:cache_control` | Hash of directives for the `Cache-Control` response header | `{ public: true, max_age: 365*24*60*60 }` |
|
775
769
|
| `:disposition` | Whether the browser should attempt to render the derivative (`inline`) or prompt the user to download the file to disk (`attachment`) | `inline` |
|
776
770
|
| `:download` | Whether the source uploaded file should be downloaded to disk when the derivation block is called | `true` |
|
777
|
-
| `:download_errors` | List of error classes that will be converted to a `404 Not Found` response by the derivation endpoint | `[]` |
|
778
771
|
| `:download_options` | Additional options to pass when downloading the source uploaded file | `{}` |
|
779
772
|
| `:expires_in` | Number of seconds after which the URL will not be available anymore | `nil` |
|
780
773
|
| `:filename` | Filename the browser will assume when the derivative is downloaded to disk | `<name>-<args>-<source id basename>` |
|
781
774
|
| `:host` | URL host to use when generated URLs | `nil` |
|
782
|
-
| `:include_uploaded_file` | Whether to include the source uploaded file in the derivation block arguments | `false` |
|
783
775
|
| `:metadata` | List of metadata keys the source uploaded file should include in the derivation block | `[]` |
|
784
776
|
| `:prefix` | Path prefix added to the URLs | `nil` |
|
785
777
|
| `:secret_key` | Key used to sign derivation URLs in order to prevent tampering | required |
|
@@ -0,0 +1,752 @@
|
|
1
|
+
# Derivatives
|
2
|
+
|
3
|
+
The derivatives plugin allows storing processed files ("derivatives") alongside
|
4
|
+
the main attached file. The processed file data will be saved together with the
|
5
|
+
main attachment data in the same record attribute.
|
6
|
+
|
7
|
+
```rb
|
8
|
+
plugin :derivatives
|
9
|
+
```
|
10
|
+
|
11
|
+
## Contents
|
12
|
+
|
13
|
+
* [API overview](#api-overview)
|
14
|
+
* [Creating derivatives](#creating-derivatives)
|
15
|
+
- [Nesting derivatives](#nesting-derivatives)
|
16
|
+
* [Retrieving derivatives](#retrieving-derivatives)
|
17
|
+
* [Derivative URL](#derivative-url)
|
18
|
+
* [Processing derivatives](#processing-derivatives)
|
19
|
+
- [Dynamic processing](#dynamic-processing)
|
20
|
+
- [Source file](#source-file)
|
21
|
+
* [Adding derivatives](#adding-derivatives)
|
22
|
+
* [Uploading derivatives](#uploading-derivatives)
|
23
|
+
- [Derivatives storage](#derivatives-storage)
|
24
|
+
- [Uploader options](#uploader-options)
|
25
|
+
- [File deletion](#file-deletion)
|
26
|
+
* [Merging derivatives](#merging-derivatives)
|
27
|
+
- [Setting derivatives](#setting-derivatives)
|
28
|
+
* [Promoting derivatives](#promoting-derivatives)
|
29
|
+
* [Removing derivatives](#removing-derivatives)
|
30
|
+
* [Deleting derivatives](#deleting-derivatives)
|
31
|
+
* [Miscellaneous](#miscellaneous)
|
32
|
+
* [Without original](#without-original)
|
33
|
+
* [Iterating derivatives](#iterating-derivatives)
|
34
|
+
* [Parsing derivatives](#parsing-derivatives)
|
35
|
+
* [Instrumentation](#instrumentation)
|
36
|
+
|
37
|
+
## API overview
|
38
|
+
|
39
|
+
The interface for managing derivatives is implemented on the `Shrine::Attacher`
|
40
|
+
class, and it's layered in the following way:
|
41
|
+
|
42
|
+
* [`Attacher#create_derivatives`](#creating-derivatives) – processes, uploads and merges derivatives
|
43
|
+
* [`Attacher#process_derivatives`](#processing-derivatives) – processes derivatives
|
44
|
+
* [`Attacher#add_derivatives`](#adding-derivatives) – uploads and merges derivatives
|
45
|
+
* [`Attacher#upload_derivatives`](#uploading-derivatives) – uploads derivatives
|
46
|
+
* [`Attacher#merge_derivatives`](#merging-derivatives) – merges derivatives
|
47
|
+
* [`Attacher#set_derivatives`](#setting-derivatives) – overrides derivatives
|
48
|
+
|
49
|
+
## Creating derivatives
|
50
|
+
|
51
|
+
When you have a file attached, you can generate derivatives from it and save
|
52
|
+
them alongside the attached file. The simplest way to do this is to define a
|
53
|
+
processor which returns the processed files, and then trigger it with
|
54
|
+
`Attacher#create_derivatives` when you want to generate the derivatives.
|
55
|
+
|
56
|
+
Here is an example of generating image thumbnails:
|
57
|
+
|
58
|
+
```rb
|
59
|
+
# Gemfile
|
60
|
+
gem "image_processing", "~> 1.2"
|
61
|
+
```
|
62
|
+
```rb
|
63
|
+
require "image_processing/mini_magick"
|
64
|
+
|
65
|
+
class ImageUploader < Shrine
|
66
|
+
plugin :derivatives
|
67
|
+
|
68
|
+
Attacher.derivatives_processor :thumbnails do |original|
|
69
|
+
processor = ImageProcessing::MiniMagick.source(original)
|
70
|
+
|
71
|
+
{
|
72
|
+
small: processor.resize_to_limit!(300, 300),
|
73
|
+
medium: processor.resize_to_limit!(500, 500),
|
74
|
+
large: processor.resize_to_limit!(800, 800),
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
```rb
|
80
|
+
class Photo < Model(:image_data)
|
81
|
+
include ImageUploader::Attachment(:image)
|
82
|
+
end
|
83
|
+
```
|
84
|
+
```rb
|
85
|
+
photo.image #=> #<Shrine::UploadedFile @id="original.jpg" @storage_key=:store ...>
|
86
|
+
photo.image_derivatives #=> {}
|
87
|
+
|
88
|
+
photo.image_attacher.create_derivatives(:thumbnails) # calls processor and uploads results
|
89
|
+
photo.image_derivatives #=>
|
90
|
+
# {
|
91
|
+
# small: #<Shrine::UploadedFile @id="small.jpg" @storage_key=:store ...>,
|
92
|
+
# medium: #<Shrine::UploadedFile @id="medium.jpg" @storage_key=:store ...>,
|
93
|
+
# large: #<Shrine::UploadedFile @id="large.jpg" @storage_key=:store ...>,
|
94
|
+
# }
|
95
|
+
```
|
96
|
+
|
97
|
+
The derivatives data is stored in the `#<name>_data` record attribute alongside
|
98
|
+
the main file data:
|
99
|
+
|
100
|
+
```rb
|
101
|
+
photo.image_data #=>
|
102
|
+
# {
|
103
|
+
# "id": "original.jpg",
|
104
|
+
# "store": "store",
|
105
|
+
# "metadata": { ... },
|
106
|
+
# "derivatives": {
|
107
|
+
# "small": { "id": "small.jpg", "storage": "store", "metadata": { ... } },
|
108
|
+
# "medium": { "id": "medium.jpg", "storage": "store", "metadata": { ... } },
|
109
|
+
# "large": { "id": "large.jpg", "storage": "store", "metadata": { ... } },
|
110
|
+
# }
|
111
|
+
# }
|
112
|
+
```
|
113
|
+
|
114
|
+
Any additional options passed to `Attacher#create_derivatives` are forwarded to
|
115
|
+
[`Attacher#upload_derivatives`](#uploading-derivatives).
|
116
|
+
|
117
|
+
```rb
|
118
|
+
attacher.create_derivatives(:thumbnails, storage: :other_store) # specify destination storage
|
119
|
+
attacher.create_derivatives(:thumbnails, upload_options: { acl: "public-read" }) # pass uploader options
|
120
|
+
```
|
121
|
+
|
122
|
+
### Nesting derivatives
|
123
|
+
|
124
|
+
Derivatives can be nested to any level, using both hashes and arrays, but the
|
125
|
+
top-level object must be a hash.
|
126
|
+
|
127
|
+
```rb
|
128
|
+
Attacher.derivatives_processor :tiff do |original|
|
129
|
+
{
|
130
|
+
thumbnail: {
|
131
|
+
small: small,
|
132
|
+
medium: medium,
|
133
|
+
large: large,
|
134
|
+
},
|
135
|
+
layers: [
|
136
|
+
layer_1,
|
137
|
+
layer_2,
|
138
|
+
# ...
|
139
|
+
]
|
140
|
+
}
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
## Retrieving derivatives
|
145
|
+
|
146
|
+
If you're using the `Shrine::Attachment` module, you can retrieve stored
|
147
|
+
derivatives by calling `#<name>_derivatives` on your model/entity.
|
148
|
+
|
149
|
+
```rb
|
150
|
+
class Photo < Model(:image_data)
|
151
|
+
include ImageUploader::Attachment(:image)
|
152
|
+
end
|
153
|
+
```
|
154
|
+
```rb
|
155
|
+
photo.image_derivatives #=>
|
156
|
+
# {
|
157
|
+
# small: #<Shrine::UploadedFile>,
|
158
|
+
# medium: #<Shrine::UploadedFile>,
|
159
|
+
# large: #<Shrine::UploadedFile>,
|
160
|
+
# }
|
161
|
+
```
|
162
|
+
|
163
|
+
A specific derivative can be retrieved in any of the following ways:
|
164
|
+
|
165
|
+
```rb
|
166
|
+
photo.image_derivatives[:small] #=> #<Shrine::UploadedFile>
|
167
|
+
photo.image_derivatives(:small) #=> #<Shrine::UploadedFile>
|
168
|
+
photo.image(:small) #=> #<Shrine::UploadedFile>
|
169
|
+
```
|
170
|
+
|
171
|
+
And with nested derivatives:
|
172
|
+
|
173
|
+
```rb
|
174
|
+
photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
|
175
|
+
|
176
|
+
photo.image_derivatives.dig(:thumbnail, :small) #=> #<Shrine::UploadedFile>
|
177
|
+
photo.image_derivatives(:thumbnail, :small) #=> #<Shrine::UploadedFile>
|
178
|
+
photo.image(:thumbnails :small) #=> #<Shrine::UploadedFile>
|
179
|
+
```
|
180
|
+
|
181
|
+
When using `Shrine::Attacher` directly, you can retrieve derivatives using
|
182
|
+
`Attacher#derivatives`:
|
183
|
+
|
184
|
+
```rb
|
185
|
+
attacher.derivatives #=>
|
186
|
+
# {
|
187
|
+
# small: #<Shrine::UploadedFile>,
|
188
|
+
# medium: #<Shrine::UploadedFile>,
|
189
|
+
# large: #<Shrine::UploadedFile>,
|
190
|
+
# }
|
191
|
+
```
|
192
|
+
|
193
|
+
## Derivative URL
|
194
|
+
|
195
|
+
If you're using the `Shrine::Attachment` module, you can use the `#<name>_url`
|
196
|
+
method to retrieve the URL of a derivative.
|
197
|
+
|
198
|
+
```rb
|
199
|
+
class Photo < Model(:image_data)
|
200
|
+
include ImageUploader::Attachment(:image)
|
201
|
+
end
|
202
|
+
```
|
203
|
+
```rb
|
204
|
+
photo.image_url(:small) #=> "https://example.com/small.jpg"
|
205
|
+
photo.image_url(:medium) #=> "https://example.com/medium.jpg"
|
206
|
+
photo.image_url(:large) #=> "https://example.com/large.jpg"
|
207
|
+
```
|
208
|
+
|
209
|
+
For nested derivatives you can pass multiple keys:
|
210
|
+
|
211
|
+
```rb
|
212
|
+
photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
|
213
|
+
|
214
|
+
photo.image_url(:thumbnail, :medium) #=> "https://example.com/medium.jpg"
|
215
|
+
```
|
216
|
+
|
217
|
+
By default, `#<name>_url` method will return `nil` if derivative is not found.
|
218
|
+
You can use the [`default_url`][default_url] plugin to set up URL fallbacks:
|
219
|
+
|
220
|
+
```rb
|
221
|
+
Attacher.default_url do |derivative: nil, **|
|
222
|
+
"https://fallbacks.com/#{derivative}.jpg" if derivative
|
223
|
+
end
|
224
|
+
```
|
225
|
+
```rb
|
226
|
+
photo.image_url(:medium) #=> "https://example.com/fallbacks.com/medium.jpg"
|
227
|
+
```
|
228
|
+
|
229
|
+
Any additional URL options passed to `#<name>_url` will be forwarded to the
|
230
|
+
storage:
|
231
|
+
|
232
|
+
```rb
|
233
|
+
photo.image_url(:small, response_content_disposition: "attachment")
|
234
|
+
```
|
235
|
+
|
236
|
+
You can also retrieve the derivative URL via `UploadedFile#url`:
|
237
|
+
|
238
|
+
```rb
|
239
|
+
photo.image_derivatives[:large].url
|
240
|
+
# or
|
241
|
+
attacher.derivatives[:large].url
|
242
|
+
```
|
243
|
+
|
244
|
+
## Processing derivatives
|
245
|
+
|
246
|
+
A derivatives processor block takes the original file, and is expected to
|
247
|
+
return a hash of processed files (it can be [nested](#nesting-derivatives)).
|
248
|
+
|
249
|
+
```rb
|
250
|
+
Attacher.derivatives_processor :my_processor do |original|
|
251
|
+
# return a hash of processed files
|
252
|
+
end
|
253
|
+
```
|
254
|
+
|
255
|
+
The [`Attacher#create_derivatives`](#creating-derivatives) method will call
|
256
|
+
the processor and upload results.
|
257
|
+
|
258
|
+
```rb
|
259
|
+
attacher.create_derivatives(:my_processor)
|
260
|
+
```
|
261
|
+
|
262
|
+
Internally this calls `Attacher#process_derivatives`, which calls the
|
263
|
+
processor and returns processed files:
|
264
|
+
|
265
|
+
```rb
|
266
|
+
files = attacher.process_derivatives(:my_processor)
|
267
|
+
attacher.add_derivatives(files)
|
268
|
+
```
|
269
|
+
|
270
|
+
### Dynamic processing
|
271
|
+
|
272
|
+
The processor block is evaluated in context of the `Shrine::Attacher` instance,
|
273
|
+
which allows you to change your processing logic based on the record data.
|
274
|
+
|
275
|
+
```rb
|
276
|
+
Attacher.derivatives_processor :my_processor do |original|
|
277
|
+
self #=> #<Shrine::Attacher>
|
278
|
+
|
279
|
+
record #=> #<Photo>
|
280
|
+
name #=> :image
|
281
|
+
context #=> { ... }
|
282
|
+
|
283
|
+
# ...
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
Moreover, any options passed to `Attacher#process_derivatives` will be
|
288
|
+
forwarded to the processor:
|
289
|
+
|
290
|
+
```rb
|
291
|
+
attacher.process_derivatives(:my_processor, foo: "bar")
|
292
|
+
```
|
293
|
+
```rb
|
294
|
+
Attacher.derivatives_processor :my_processor do |original, **options|
|
295
|
+
options #=> { :foo => "bar" }
|
296
|
+
# ...
|
297
|
+
end
|
298
|
+
```
|
299
|
+
|
300
|
+
### Source file
|
301
|
+
|
302
|
+
The `Attacher#process_derivatives` method will automatically download the
|
303
|
+
attached file and pass it to the processor:
|
304
|
+
|
305
|
+
```rb
|
306
|
+
Attacher.derivatives_processor :my_processor do |original|
|
307
|
+
original #=> #<File:...>
|
308
|
+
# ...
|
309
|
+
end
|
310
|
+
```
|
311
|
+
```rb
|
312
|
+
attacher.process_derivatives(:my_processor) # downloads attached file and passes it to the processor
|
313
|
+
```
|
314
|
+
|
315
|
+
If you already have the source file locally, or if you're calling multiple
|
316
|
+
processors in a row and want to avoid downloading the same source file each
|
317
|
+
time, you can pass the source file as the second argument to
|
318
|
+
`Attacher#process_derivatives`:
|
319
|
+
|
320
|
+
```rb
|
321
|
+
# this way the source file is downloaded only once
|
322
|
+
attacher.file.download do |original|
|
323
|
+
attacher.process_derivatives(:thumbnails, original)
|
324
|
+
attacher.process_derivatives(:colors, original)
|
325
|
+
end
|
326
|
+
```
|
327
|
+
|
328
|
+
## Adding derivatives
|
329
|
+
|
330
|
+
If you already have processed files that you want to save, you can do that with
|
331
|
+
`Attacher#add_derivatives`:
|
332
|
+
|
333
|
+
```rb
|
334
|
+
attacher.add_derivatives(
|
335
|
+
one: file_1,
|
336
|
+
two: file_2,
|
337
|
+
# ...
|
338
|
+
)
|
339
|
+
|
340
|
+
attacher.derivatives #=>
|
341
|
+
# {
|
342
|
+
# one: #<Shrine::UploadedFile>,
|
343
|
+
# two: #<Shrine::UploadedFile>,
|
344
|
+
# ...
|
345
|
+
# }
|
346
|
+
```
|
347
|
+
|
348
|
+
New derivatives will be merged with existing ones:
|
349
|
+
|
350
|
+
```rb
|
351
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
352
|
+
attacher.add_derivatives(two: two_file)
|
353
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
354
|
+
```
|
355
|
+
|
356
|
+
The merging is deep, so the following will work as well:
|
357
|
+
|
358
|
+
```rb
|
359
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
|
360
|
+
attacher.add_derivatives(nested: { two: two_file })
|
361
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
|
362
|
+
```
|
363
|
+
|
364
|
+
For adding a single derivative, you can also use the singular
|
365
|
+
`Attacher#add_derivative`:
|
366
|
+
|
367
|
+
```rb
|
368
|
+
attacher.add_derivative(:thumb, thumbnail_file)
|
369
|
+
```
|
370
|
+
|
371
|
+
Any options passed to `Attacher#add_derivative(s)` will be forwarded to
|
372
|
+
[`Attacher#upload_derivatives`](#uploading-derivatives).
|
373
|
+
|
374
|
+
```rb
|
375
|
+
attacher.add_derivative(:thumb, thumbnail_file, storage: :thumbnails_store) # specify destination storage
|
376
|
+
attacher.add_derivative(:thumb, thumbnail_file, upload_options: { acl: "public-read" }) # pass uploader options
|
377
|
+
```
|
378
|
+
|
379
|
+
The `Attacher#add_derivative(s)` methods are thread-safe.
|
380
|
+
|
381
|
+
## Uploading derivatives
|
382
|
+
|
383
|
+
If you want to upload processed files without setting them, you can use
|
384
|
+
`Attacher#upload_derivatives`:
|
385
|
+
|
386
|
+
```rb
|
387
|
+
derivatives = attacher.upload_derivatives(
|
388
|
+
one: file_1,
|
389
|
+
two: file_2,
|
390
|
+
# ...
|
391
|
+
)
|
392
|
+
|
393
|
+
derivatives #=>
|
394
|
+
# {
|
395
|
+
# one: #<Shrine::UploadedFile>,
|
396
|
+
# two: #<Shrine::UploadedFile>,
|
397
|
+
# ...
|
398
|
+
# }
|
399
|
+
```
|
400
|
+
|
401
|
+
For uploading a single derivative, you can also use the singular
|
402
|
+
`Attacher#upload_derivative`:
|
403
|
+
|
404
|
+
```rb
|
405
|
+
attacher.upload_derivative(:thumb, thumbnail_file)
|
406
|
+
#=> #<Shrine::UploadedFile>
|
407
|
+
```
|
408
|
+
|
409
|
+
### Derivatives storage
|
410
|
+
|
411
|
+
By default, derivatives are uploaded to the permanent storage of the attacher
|
412
|
+
(`:store` by default). You can specify a different destination storage for
|
413
|
+
`Attacher#upload_derivative(s)` with the `:storage` option:
|
414
|
+
|
415
|
+
```rb
|
416
|
+
attacher.upload_derivatives(derivatives, storage: :other_store)
|
417
|
+
```
|
418
|
+
|
419
|
+
You can also set a default derivatives storage on the plugin level:
|
420
|
+
|
421
|
+
```rb
|
422
|
+
plugin :derivatives, storage: :other_store
|
423
|
+
```
|
424
|
+
|
425
|
+
The storage can be dynamic based on the derivative name:
|
426
|
+
|
427
|
+
```rb
|
428
|
+
plugin :derivatives, storage: -> (derivative) do
|
429
|
+
if derivative == :thumb
|
430
|
+
:thumbnail_store
|
431
|
+
else
|
432
|
+
:store
|
433
|
+
end
|
434
|
+
end
|
435
|
+
```
|
436
|
+
|
437
|
+
You can also set this option with `Attacher.derivatives_storage`:
|
438
|
+
|
439
|
+
```rb
|
440
|
+
Attacher.derivatives_storage :other_store
|
441
|
+
# or
|
442
|
+
Attacher.derivatives_storage do |derivative|
|
443
|
+
if derivative == :thumb
|
444
|
+
:thumbnail_store
|
445
|
+
else
|
446
|
+
:store
|
447
|
+
end
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
The storage block is evaluated in the context of a `Shrine::Attacher` instance:
|
452
|
+
|
453
|
+
```rb
|
454
|
+
Attacher.derivatives_storage do |derivative|
|
455
|
+
self #=> #<Shrine::Attacher>
|
456
|
+
|
457
|
+
record #=> #<Photo>
|
458
|
+
name #=> :image
|
459
|
+
context #=> { ... }
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
### Uploader options
|
464
|
+
|
465
|
+
Any options other than `:storage` will be forwarded to the uploader:
|
466
|
+
|
467
|
+
```rb
|
468
|
+
attacher.upload_derivative :thumb, thumbnail_file,
|
469
|
+
upload_options: { acl: "public-read" },
|
470
|
+
metadata: { "foo" => "bar" }),
|
471
|
+
location: "path/to/derivative"
|
472
|
+
```
|
473
|
+
|
474
|
+
A `:derivative` option is automatically passed to the uploader and holds the
|
475
|
+
name of the derivative, which you can use when extracting metadata, generating
|
476
|
+
location or generating upload options:
|
477
|
+
|
478
|
+
```rb
|
479
|
+
class MyUploader < Shrine
|
480
|
+
plugin :add_metadata
|
481
|
+
|
482
|
+
add_metadata :md5 do |io, derivative: nil, **|
|
483
|
+
calculate_signature(io, :md5) unless derivative
|
484
|
+
end
|
485
|
+
|
486
|
+
def generate_location(io, derivative: nil, **)
|
487
|
+
"location/for/#{derivative}"
|
488
|
+
end
|
489
|
+
|
490
|
+
plugin :upload_options, store: -> (io, derivative: nil, **) {
|
491
|
+
{ acl: "public-read" } if derivative
|
492
|
+
}
|
493
|
+
end
|
494
|
+
```
|
495
|
+
|
496
|
+
### File deletion
|
497
|
+
|
498
|
+
Files given to `Attacher#upload_derivative(s)` are assumed to be temporary, so
|
499
|
+
for convenience they're automatically closed and unlinked after upload.
|
500
|
+
|
501
|
+
If you want to disable this behaviour, pass `delete: false`:
|
502
|
+
|
503
|
+
```rb
|
504
|
+
attacher.upload_derivative(:thumb, thumbnail_file, delete: false)
|
505
|
+
|
506
|
+
File.exist?(thumbnail_file.path) #=> true
|
507
|
+
```
|
508
|
+
|
509
|
+
## Merging derivatives
|
510
|
+
|
511
|
+
If you want to save already uploaded derivatives, you can use
|
512
|
+
`Attacher#merge_derivatives`:
|
513
|
+
|
514
|
+
```rb
|
515
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
516
|
+
attacher.merge_derivatives attacher.upload_derivatives(two: two_file)
|
517
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
518
|
+
```
|
519
|
+
|
520
|
+
This does a deep merge, so the following will work as well:
|
521
|
+
|
522
|
+
```rb
|
523
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
|
524
|
+
attacher.merge_derivatives attacher.upload_derivatives(nested: { two: two_file })
|
525
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
|
526
|
+
```
|
527
|
+
|
528
|
+
The `Attacher#merge_derivatives` method is thread-safe.
|
529
|
+
|
530
|
+
### Setting derivatives
|
531
|
+
|
532
|
+
If instead of adding you want to *override* existing derivatives, you can use
|
533
|
+
`Attacher#set_derivatives`:
|
534
|
+
|
535
|
+
```rb
|
536
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
537
|
+
attacher.set_derivatives attacher.upload_derivatives(two: two_file)
|
538
|
+
attacher.derivatives #=> { two: #<Shrine::UploadedFile> }
|
539
|
+
```
|
540
|
+
|
541
|
+
If you're using the [`model`][model] plugin, this method will trigger writing
|
542
|
+
derivatives data into the column attribute.
|
543
|
+
|
544
|
+
## Promoting derivatives
|
545
|
+
|
546
|
+
Any assigned derivatives that are uploaded to temporary storage will be
|
547
|
+
automatically uploaded to permanent storage on `Attacher#promote`.
|
548
|
+
|
549
|
+
```rb
|
550
|
+
attacher.derivatives[:one].storage_key #=> :cache
|
551
|
+
attacher.promote
|
552
|
+
attacher.derivatives[:one].storage_key #=> :store
|
553
|
+
```
|
554
|
+
|
555
|
+
If you want more control over derivatives promotion, you can use
|
556
|
+
`Attacher#promote_derivatives`. Any additional options passed to it are
|
557
|
+
forwarded to the uploader.
|
558
|
+
|
559
|
+
```rb
|
560
|
+
attacher.derivatives[:one].storage_key #=> :cache
|
561
|
+
attacher.promote_derivatives(upload_options: { acl: "public-read" })
|
562
|
+
attacher.derivatives[:one].storage_key #=> :store
|
563
|
+
```
|
564
|
+
|
565
|
+
## Removing derivatives
|
566
|
+
|
567
|
+
If you want to manually remove certain derivatives, you can do that with
|
568
|
+
`Attacher#remove_derivative`.
|
569
|
+
|
570
|
+
```rb
|
571
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
572
|
+
attacher.remove_derivative(:two) #=> #<Shrine::UploadedFile> (removed derivative)
|
573
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
574
|
+
```
|
575
|
+
|
576
|
+
You can also use the plural `Attacher#remove_derivatives` for removing multiple
|
577
|
+
derivatives:
|
578
|
+
|
579
|
+
```rb
|
580
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile>, three: #<Shrine::UploadedFile> }
|
581
|
+
attacher.remove_derivative(:two, :three) #=> [#<Shrine::UploadedFile>, #<Shrine::UploadedFile>] (removed derivatives)
|
582
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
|
583
|
+
```
|
584
|
+
|
585
|
+
It's possible to remove nested derivatives as well:
|
586
|
+
|
587
|
+
```rb
|
588
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
|
589
|
+
attacher.remove_derivative([:nested, :one]) #=> #<Shrine::UploadedFile> (removed derivative)
|
590
|
+
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
|
591
|
+
```
|
592
|
+
|
593
|
+
The removed derivatives are not automatically deleted, because it's safer to
|
594
|
+
first persist the removal change, and only then perform the deletion.
|
595
|
+
|
596
|
+
```rb
|
597
|
+
derivative = attacher.remove_derivative(:two)
|
598
|
+
# ... persist removal change ...
|
599
|
+
derivative.delete
|
600
|
+
```
|
601
|
+
|
602
|
+
If you still want to delete the derivative at the time of removal, you can
|
603
|
+
pass `delete: true`:
|
604
|
+
|
605
|
+
```rb
|
606
|
+
derivative = attacher.remove_derivative(:two, delete: true)
|
607
|
+
derivative.exists? #=> false
|
608
|
+
```
|
609
|
+
|
610
|
+
### Deleting derivatives
|
611
|
+
|
612
|
+
If you want to delete a collection of derivatives, you can use
|
613
|
+
`Attacher#delete_derivatives`:
|
614
|
+
|
615
|
+
```rb
|
616
|
+
derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
617
|
+
|
618
|
+
attacher.delete_derivatives(derivatives)
|
619
|
+
|
620
|
+
derivatives[:one].exists? #=> false
|
621
|
+
derivatives[:two].exists? #=> false
|
622
|
+
```
|
623
|
+
|
624
|
+
Without arguments `Attacher#delete_derivatives` deletes current derivatives:
|
625
|
+
|
626
|
+
```rb
|
627
|
+
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
|
628
|
+
|
629
|
+
attacher.delete_derivatives
|
630
|
+
|
631
|
+
attacher.derivatives[:one].exists? #=> false
|
632
|
+
attacher.derivatives[:two].exists? #=> false
|
633
|
+
```
|
634
|
+
|
635
|
+
Derivatives are automatically deleted on `Attacher#destroy`.
|
636
|
+
|
637
|
+
## Without original
|
638
|
+
|
639
|
+
You can store derivatives even if there is no main attached file:
|
640
|
+
|
641
|
+
```rb
|
642
|
+
attacher.file #=> nil
|
643
|
+
attacher.add_derivatives(one: one_file, two: two_file)
|
644
|
+
attacher.data #=>
|
645
|
+
# {
|
646
|
+
# "derivatives" => {
|
647
|
+
# "one" => { "id" => "...", "storage" => "...", "metadata": { ... } },
|
648
|
+
# "two" => { "id" => "...", "storage" => "...", "metadata": { ... } },
|
649
|
+
# }
|
650
|
+
# }
|
651
|
+
```
|
652
|
+
|
653
|
+
However, note that in this case operations such as promotion and deletion will
|
654
|
+
not be automatically triggered in the attachment flow, you'd need to trigger
|
655
|
+
them manually as needed.
|
656
|
+
|
657
|
+
## Iterating derivatives
|
658
|
+
|
659
|
+
If you want to iterate over a nested hash of derivatives (which can be
|
660
|
+
`Shrine::UploadedFile` objects or raw files), you can use
|
661
|
+
`Attacher#map_derivative` or `Shrine.map_derivative`:
|
662
|
+
|
663
|
+
```rb
|
664
|
+
derivatives #=>
|
665
|
+
# {
|
666
|
+
# one: #<Shrine::UploadedFile>,
|
667
|
+
# two: { three: #<Shrine::UploadedFile> },
|
668
|
+
# four: [#<Shrine::UploadedFile>],
|
669
|
+
# }
|
670
|
+
|
671
|
+
# or Shrine.map_derivative
|
672
|
+
attacher.map_derivative(derivatives) do |name, file|
|
673
|
+
puts "#{name}, #{file}"
|
674
|
+
end
|
675
|
+
|
676
|
+
# output:
|
677
|
+
#
|
678
|
+
# :one, #<Shrine::UploadedFile>
|
679
|
+
# [:two, :three], #<Shrine::UploadedFile>
|
680
|
+
# [:four, 0], #<Shrine::UploadedFile>
|
681
|
+
```
|
682
|
+
|
683
|
+
## Parsing derivatives
|
684
|
+
|
685
|
+
If you want to directly parse derivatives data written to a record attribute,
|
686
|
+
you can use `Shrine.derivatives` (counterpart to `Shrine.uploaded_file`):
|
687
|
+
|
688
|
+
```rb
|
689
|
+
# or MyUploader.derivatives
|
690
|
+
derivatives = Shrine.derivatives({
|
691
|
+
"one" => { "id" => "...", "storage" => "...", "metadata" => { ... } },
|
692
|
+
"two" => { "three" => { "id" => "...", "storage" => "...", "metadata" => { ... } } }
|
693
|
+
"four" => [{ "id" => "...", "storage" => "...", "metadata" => { ... } }]
|
694
|
+
})
|
695
|
+
|
696
|
+
derivatives #=>
|
697
|
+
# {
|
698
|
+
# one: #<Shrine::UploadedFile>,
|
699
|
+
# two: { three: #<Shrine::UploadedFile> },
|
700
|
+
# four: [#<Shrine::UploadedFile>],
|
701
|
+
# }
|
702
|
+
```
|
703
|
+
|
704
|
+
Like `Shrine.uploaded_file`, the `Shrine.derivatives` method accepts data as a
|
705
|
+
hash (stringified or symbolized) or a JSON string.
|
706
|
+
|
707
|
+
## Instrumentation
|
708
|
+
|
709
|
+
If the `instrumentation` plugin has been loaded, the `derivatives` plugin adds
|
710
|
+
instrumentation around derivatives processing.
|
711
|
+
|
712
|
+
```rb
|
713
|
+
# instrumentation plugin needs to be loaded *before* derivatives
|
714
|
+
plugin :instrumentation
|
715
|
+
plugin :derivatives
|
716
|
+
```
|
717
|
+
|
718
|
+
Processing derivatives will trigger a `derivatives.shrine` event with the
|
719
|
+
following payload:
|
720
|
+
|
721
|
+
| Key | Description |
|
722
|
+
| :-- | :---- |
|
723
|
+
| `:processor` | Name of the derivatives processor |
|
724
|
+
| `:processor_options` | Any options passed to the processor |
|
725
|
+
| `:uploader` | The uploader class that sent the event |
|
726
|
+
|
727
|
+
A default log subscriber is added as well which logs these events:
|
728
|
+
|
729
|
+
```
|
730
|
+
Derivatives (2133ms) – {:processor=>:thumbnails, :processor_options=>{}, :uploader=>ImageUploader}
|
731
|
+
```
|
732
|
+
|
733
|
+
You can also use your own log subscriber:
|
734
|
+
|
735
|
+
```rb
|
736
|
+
plugin :derivatives, log_subscriber: -> (event) {
|
737
|
+
Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)
|
738
|
+
}
|
739
|
+
```
|
740
|
+
```
|
741
|
+
{"name":"derivatives","duration":2133,"processor":"thumbnails","processor_options":{},"uploader":"ImageUploader"}
|
742
|
+
```
|
743
|
+
|
744
|
+
Or disable logging altogether:
|
745
|
+
|
746
|
+
```rb
|
747
|
+
plugin :derivatives, log_subscriber: nil
|
748
|
+
```
|
749
|
+
|
750
|
+
[default_url]: /doc/plugins/default_url.md#readme
|
751
|
+
[entity]: /doc/plugins/entity.md#readme
|
752
|
+
[model]: /doc/plugins/model.md#readme
|