shrine 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/doc/changing_derivatives.md +2 -1
- data/doc/changing_location.md +17 -5
- data/doc/getting_started.md +4 -2
- data/doc/plugins/derivation_endpoint.md +2 -1
- data/doc/plugins/derivatives.md +2 -1
- data/doc/plugins/download_endpoint.md +16 -4
- data/doc/plugins/refresh_metadata.md +20 -0
- data/doc/plugins/signature.md +8 -6
- data/doc/processing.md +5 -3
- data/doc/release_notes/3.7.0.md +75 -0
- data/lib/shrine/attacher.rb +21 -21
- data/lib/shrine/attachment.rb +2 -2
- data/lib/shrine/plugins/_urlsafe_serialization.rb +4 -4
- data/lib/shrine/plugins/add_metadata.rb +2 -4
- data/lib/shrine/plugins/atomic_helpers.rb +7 -7
- data/lib/shrine/plugins/backgrounding.rb +9 -9
- data/lib/shrine/plugins/column.rb +6 -4
- data/lib/shrine/plugins/default_url.rb +4 -4
- data/lib/shrine/plugins/delete_raw.rb +2 -2
- data/lib/shrine/plugins/derivation_endpoint.rb +25 -25
- data/lib/shrine/plugins/derivatives.rb +5 -1
- data/lib/shrine/plugins/download_endpoint.rb +62 -10
- data/lib/shrine/plugins/entity.rb +7 -7
- data/lib/shrine/plugins/infer_extension.rb +1 -1
- data/lib/shrine/plugins/instrumentation.rb +8 -8
- data/lib/shrine/plugins/mirroring.rb +10 -10
- data/lib/shrine/plugins/model.rb +9 -9
- data/lib/shrine/plugins/presign_endpoint.rb +4 -4
- data/lib/shrine/plugins/pretty_location.rb +2 -2
- data/lib/shrine/plugins/processing.rb +3 -3
- data/lib/shrine/plugins/rack_file.rb +2 -2
- data/lib/shrine/plugins/rack_response.rb +4 -4
- data/lib/shrine/plugins/refresh_metadata.rb +6 -6
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/restore_cached_data.rb +3 -3
- data/lib/shrine/plugins/signature.rb +2 -2
- data/lib/shrine/plugins/store_dimensions.rb +2 -2
- data/lib/shrine/plugins/upload_endpoint.rb +4 -4
- data/lib/shrine/plugins/upload_options.rb +1 -1
- data/lib/shrine/plugins/validation.rb +8 -8
- data/lib/shrine/plugins/versions.rb +10 -10
- data/lib/shrine/plugins.rb +6 -14
- data/lib/shrine/storage/file_system.rb +4 -17
- data/lib/shrine/storage/linter.rb +8 -8
- data/lib/shrine/storage/memory.rb +1 -3
- data/lib/shrine/storage/s3.rb +45 -37
- data/lib/shrine/uploaded_file.rb +20 -18
- data/lib/shrine/version.rb +1 -1
- data/lib/shrine.rb +18 -18
- data/shrine.gemspec +6 -6
- metadata +15 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01c1404cab9882d16a6795c41467591b4083cbef76b5d42b91626a4306b33584
|
|
4
|
+
data.tar.gz: bec484b8594573db2056eabeda97ae5c22fb170a35f984802cd200085611e556
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 298e5065ba40638cee54dc4b74be98e466974502f56b5bff956fb2d7cc4d73028ec8a9d79dae7c073306c4512145cfef082ec4beeeb60a0eddef5497bbcf8174
|
|
7
|
+
data.tar.gz: 3413b070afe6315133342ab3faf7c41d97bb9767a3aa13f62bf4ba339246fb4f1b3d1ebc91036b37942b8893c20b652edf4603f2b710bd66647caed6b9cddc48
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 3.7.0 (2026-05-27)
|
|
2
|
+
|
|
3
|
+
* `download_endpoint` - Add support for expiring URLs (@davidwessman)
|
|
4
|
+
|
|
5
|
+
* `s3` - Use `TransferManager` where available instead of deprecated `upload_steam` (@danieldevlewis)
|
|
6
|
+
|
|
7
|
+
* `column` - Don't attempt to deserialize empty string as JSON (@adam12)
|
|
8
|
+
|
|
9
|
+
* `derivatives` - Add `:keep_derivatives` plugin option to keep existing derivatives when a new file is attached (@fnordfish)
|
|
10
|
+
|
|
11
|
+
* `refresh_metadata` - Add `replace:` keyword argument to `refresh_metadata!` for replacing instead of merging existing metadata (@JacobGalati)
|
|
12
|
+
|
|
13
|
+
* `backgrounding` - Fix options not being forwarded from `promote_cached` to `promote_block` (@4ndypanda)
|
|
14
|
+
|
|
15
|
+
* Fix URI default parser warnings in `UploadedFile` (@adam12)
|
|
16
|
+
|
|
17
|
+
* Drop support for Ruby < 3.2
|
|
18
|
+
|
|
1
19
|
## 3.6.0 (2024-04-29)
|
|
2
20
|
|
|
3
21
|
* Add Rack 3 support (@tomasc, @janko)
|
data/doc/changing_derivatives.md
CHANGED
|
@@ -36,7 +36,8 @@ derivatives generated). Let's assume you're generating image thumbnails:
|
|
|
36
36
|
|
|
37
37
|
```rb
|
|
38
38
|
# Gemfile
|
|
39
|
-
gem "image_processing", "~>
|
|
39
|
+
gem "image_processing", "~> 2.0"
|
|
40
|
+
gem "mini_magick", "~> 5.0"
|
|
40
41
|
```
|
|
41
42
|
```rb
|
|
42
43
|
require "image_processing/mini_magick"
|
data/doc/changing_location.md
CHANGED
|
@@ -3,7 +3,7 @@ id: changing-location
|
|
|
3
3
|
title: Migrating File Locations
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
This guide shows how to migrate the location of uploaded files on the same
|
|
6
|
+
This guide shows how to migrate the location of uploaded files on the same
|
|
7
7
|
storage in production, with zero downtime.
|
|
8
8
|
|
|
9
9
|
Let's assume we have a `Photo` model with an `image` file attachment:
|
|
@@ -31,20 +31,32 @@ to work with the previously stored urls because the files have not been migrated
|
|
|
31
31
|
```rb
|
|
32
32
|
class ImageUploader < Shrine
|
|
33
33
|
def generate_location(io, **options)
|
|
34
|
-
# change location generation
|
|
34
|
+
# change location generation, eg....
|
|
35
|
+
[
|
|
36
|
+
options[:record] && options[:record].class.name.underscore,
|
|
37
|
+
option[:record] && options[:record].id,
|
|
38
|
+
super
|
|
39
|
+
].compact.join("/")
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
42
|
```
|
|
38
43
|
|
|
39
|
-
We can now deploy this change to production so new file uploads will be stored in
|
|
44
|
+
We can now deploy this change to production so new file uploads will be stored in
|
|
40
45
|
the new location.
|
|
41
46
|
|
|
47
|
+
As seen above, we can call `super` to get the include the default location, which uses ruby
|
|
48
|
+
`SecureRandom.hex` to have a unique immutable storage location. While it isn't
|
|
49
|
+
strictly required to have a unique immutable storage location, it makes many
|
|
50
|
+
things work smoother when different content will get a different storage location,
|
|
51
|
+
and is recommended. One approach is using fixed directory/prefix as above.
|
|
52
|
+
|
|
53
|
+
|
|
42
54
|
## 2. Move existing files
|
|
43
55
|
|
|
44
56
|
To move existing files to new location, run the following script. It fetches
|
|
45
57
|
the photos in batches, downloads the image, and re-uploads it to the new location.
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
Only the files in `:store` storage need to be migrated as the files in `:cache`
|
|
59
|
+
storage will be uploaded to the new location on promotion.
|
|
48
60
|
|
|
49
61
|
```rb
|
|
50
62
|
Photo.find_each do |photo|
|
data/doc/getting_started.md
CHANGED
|
@@ -602,7 +602,8 @@ creation:
|
|
|
602
602
|
|
|
603
603
|
```rb
|
|
604
604
|
# Gemfile
|
|
605
|
-
gem "image_processing", "~>
|
|
605
|
+
gem "image_processing", "~> 2.0"
|
|
606
|
+
gem "mini_magick", "~> 5.0"
|
|
606
607
|
```
|
|
607
608
|
```rb
|
|
608
609
|
Shrine.plugin :derivatives, create_on_promote: true
|
|
@@ -656,7 +657,8 @@ processing we want to perform:
|
|
|
656
657
|
|
|
657
658
|
```rb
|
|
658
659
|
# Gemfile
|
|
659
|
-
gem "image_processing", "~>
|
|
660
|
+
gem "image_processing", "~> 2.0"
|
|
661
|
+
gem "mini_magick", "~> 5.0"
|
|
660
662
|
```
|
|
661
663
|
```rb
|
|
662
664
|
# config/initializers/rails.rb (Rails)
|
|
@@ -35,7 +35,8 @@ apply to an attached file. For example, we can generate image thumbnails using
|
|
|
35
35
|
the [ImageProcessing] gem:
|
|
36
36
|
|
|
37
37
|
```rb
|
|
38
|
-
gem "image_processing", "~>
|
|
38
|
+
gem "image_processing", "~> 2.0"
|
|
39
|
+
gem "mini_magick", "~> 5.0"
|
|
39
40
|
```
|
|
40
41
|
```rb
|
|
41
42
|
require "image_processing/mini_magick"
|
data/doc/plugins/derivatives.md
CHANGED
|
@@ -7,7 +7,7 @@ downloading uploaded files from specified storages. This can be useful when
|
|
|
7
7
|
files from your storage isn't accessible over URL (e.g. database storages) or
|
|
8
8
|
if you want to authenticate your downloads.
|
|
9
9
|
|
|
10
|
-
## Global Endpoint
|
|
10
|
+
## Global Endpoint
|
|
11
11
|
|
|
12
12
|
You can configure the plugin with the path prefix which the endpoint will be
|
|
13
13
|
mounted on.
|
|
@@ -34,6 +34,7 @@ Links to the download endpoint are generated by calling
|
|
|
34
34
|
```rb
|
|
35
35
|
uploaded_file.download_url #=> "/attachments/eyJpZCI6ImFkdzlyeTM..."
|
|
36
36
|
```
|
|
37
|
+
|
|
37
38
|
## Endpoint via Uploader
|
|
38
39
|
|
|
39
40
|
You can also configure the plugin in the uploader directly - just make sure to mount it via your Uploader-class.
|
|
@@ -52,8 +53,8 @@ Rails.application.routes.draw do
|
|
|
52
53
|
end
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
twice (globally and in your uploader class - see #408)
|
|
56
|
+
_Hint: For shrine versions 2.x -> ensure that you don't include the plugin
|
|
57
|
+
twice (globally and in your uploader class - see #408)_
|
|
57
58
|
|
|
58
59
|
## Calling from a controller
|
|
59
60
|
|
|
@@ -69,6 +70,7 @@ Rails.application.routes.draw do
|
|
|
69
70
|
get "/attachments/*rest", to: "downloads#image"
|
|
70
71
|
end
|
|
71
72
|
```
|
|
73
|
+
|
|
72
74
|
```rb
|
|
73
75
|
# app/controllers/downloads_controller.rb (Rails)
|
|
74
76
|
class DownloadsController < ApplicationController
|
|
@@ -131,6 +133,16 @@ plugin :download_endpoint, download_options: -> (uploaded_file, request) {
|
|
|
131
133
|
}
|
|
132
134
|
```
|
|
133
135
|
|
|
136
|
+
## Expiring download urls
|
|
137
|
+
|
|
138
|
+
If you want to have URLs that expire after a certain time, you can use the `:expires_in` and `secret_key` options:
|
|
139
|
+
|
|
140
|
+
```rb
|
|
141
|
+
plugin :download_endpoint, expires_in: 5 * 60, secret_key: "secret"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
this will generate URLs that are signed with a signature valid for 5 minutes.
|
|
145
|
+
|
|
134
146
|
## Performance considerations
|
|
135
147
|
|
|
136
148
|
Streaming files through the app might impact the request throughput, depending
|
|
@@ -162,7 +174,7 @@ Shrine.download_endpoint(disposition: "attachment")
|
|
|
162
174
|
## Plugin options
|
|
163
175
|
|
|
164
176
|
| Name | Description | Default |
|
|
165
|
-
|
|
|
177
|
+
| :------------------ | :-------------------------------------------------------------------------------- | :------- |
|
|
166
178
|
| `:disposition` | Whether browser should render the file `inline` or download it as an `attachment` | `inline` |
|
|
167
179
|
| `:download_options` | Hash of storage-specific options passed to `Storage#open` | `{}` |
|
|
168
180
|
| `:host` | URL host that will be added to download URLs | `nil` |
|
|
@@ -69,5 +69,25 @@ Any options passed in will be forwarded to metadata extraction:
|
|
|
69
69
|
uploaded_file.refresh_metadata!(foo: "bar") # passes `{ foo: "bar" }` options to metadata extraction
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Replacing Metadata
|
|
73
|
+
|
|
74
|
+
By default the `#refresh_metadata!` method will merge the results into any existing metadata.
|
|
75
|
+
|
|
76
|
+
```rb
|
|
77
|
+
uploaded_file.metadata["custom"] = "custom value"
|
|
78
|
+
uploaded_file.refresh_metadata!
|
|
79
|
+
uploaded_file.metadata
|
|
80
|
+
# returns {"filename"=>"example.jpg", "size"=>1024, "mime_type"=>"image/jpeg", "custom"=>"custom value"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Passing `replace: true` will instead fully overwrite the existing metadata with the new metadata.
|
|
84
|
+
|
|
85
|
+
```rb
|
|
86
|
+
uploaded_file.metadata["custom"] = "custom value"
|
|
87
|
+
uploaded_file.refresh_metadata!(replace: true)
|
|
88
|
+
uploaded_file.metadata
|
|
89
|
+
# returns {"filename"=>"example.jpg", "size"=>1024, "mime_type"=>"image/jpeg"}
|
|
90
|
+
```
|
|
91
|
+
|
|
72
92
|
[refresh_metadata]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/refresh_metadata.rb
|
|
73
93
|
[model]: https://shrinerb.com/docs/plugins/model
|
data/doc/plugins/signature.md
CHANGED
|
@@ -78,15 +78,17 @@ plugin :signature
|
|
|
78
78
|
Calculating signature will trigger a `signature.shrine` event with the
|
|
79
79
|
following payload:
|
|
80
80
|
|
|
81
|
-
| Key
|
|
82
|
-
| :--
|
|
83
|
-
| `:
|
|
84
|
-
| `:
|
|
81
|
+
| Key | Description |
|
|
82
|
+
| :-- | :---- |
|
|
83
|
+
| `:algorithm` | The hashing algorithm used |
|
|
84
|
+
| `:format` | The encoding format |
|
|
85
|
+
| `:io` | The IO object |
|
|
86
|
+
| `:uploader` | The uploader class that sent the event |
|
|
85
87
|
|
|
86
88
|
A default log subscriber is added as well which logs these events:
|
|
87
89
|
|
|
88
90
|
```
|
|
89
|
-
|
|
91
|
+
Signature (1ms) – {io: StringIO, algorithm: :md5, format: :hex, uploader: Shrine}
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
You can also use your own log subscriber:
|
|
@@ -97,7 +99,7 @@ plugin :signature, log_subscriber: -> (event) {
|
|
|
97
99
|
}
|
|
98
100
|
```
|
|
99
101
|
```
|
|
100
|
-
{"name":"signature","duration":24,"io":"#<StringIO:0x00007fb7c5b08b80>","uploader":"Shrine"}
|
|
102
|
+
{"name":"signature","duration":24,"io":"#<StringIO:0x00007fb7c5b08b80>","algorithm":"sha512","format":"hex","uploader":"Shrine"}
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
Or disable logging altogether:
|
data/doc/processing.md
CHANGED
|
@@ -18,7 +18,8 @@ $ brew install imagemagick
|
|
|
18
18
|
```
|
|
19
19
|
```rb
|
|
20
20
|
# Gemfile
|
|
21
|
-
gem "image_processing", "~>
|
|
21
|
+
gem "image_processing", "~> 2.0"
|
|
22
|
+
gem "mini_magick", "~> 5.0"
|
|
22
23
|
```
|
|
23
24
|
```rb
|
|
24
25
|
require "image_processing/mini_magick"
|
|
@@ -405,7 +406,7 @@ Shrine integration, the ImageProcessing gem that we saw earlier is a completely
|
|
|
405
406
|
generic gem.
|
|
406
407
|
|
|
407
408
|
To demonstrate, here is an example of transcoding videos using
|
|
408
|
-
[streamio-ffmpeg]:
|
|
409
|
+
[streamio-ffmpeg][streamio-ffmpeg]:
|
|
409
410
|
|
|
410
411
|
```rb
|
|
411
412
|
# Gemfile
|
|
@@ -502,7 +503,8 @@ $ brew install vips
|
|
|
502
503
|
|
|
503
504
|
```rb
|
|
504
505
|
# Gemfile
|
|
505
|
-
gem "image_processing", "~>
|
|
506
|
+
gem "image_processing", "~> 2.0"
|
|
507
|
+
gem "ruby-vips", "~> 2.3"
|
|
506
508
|
```
|
|
507
509
|
|
|
508
510
|
```rb
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.7.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The `download_endpoint` plugin now supports expiring URLs. Configure a `secret_key` and optionally a default `expires_in` on the plugin, then pass `expires_in:` when generating a URL. The URL is signed with HMAC-SHA256, and the endpoint will reject requests with an expired or tampered signature.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
plugin :download_endpoint,
|
|
11
|
+
prefix: "downloads",
|
|
12
|
+
secret_key: "<your-secret-key>",
|
|
13
|
+
expires_in: 5 * 60 # optional default; can be overridden per URL
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```rb
|
|
17
|
+
uploaded_file.download_url # uses the default expires_in
|
|
18
|
+
uploaded_file.download_url(expires_in: 10 * 60) # override per URL
|
|
19
|
+
# => "https://example.com/downloads/<token>?signature=...&expires_at=..."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
* The `derivatives` plugin now accepts a `:keep_derivatives` option. When set to `true`, existing derivatives are kept when a new file is attached via `Attacher#change`, instead of being cleared.
|
|
23
|
+
|
|
24
|
+
```rb
|
|
25
|
+
plugin :derivatives, keep_derivatives: true
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```rb
|
|
29
|
+
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
|
|
30
|
+
attacher.change(new_file)
|
|
31
|
+
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> } # preserved
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
* The `refresh_metadata` plugin now accepts a `replace:` keyword argument on `refresh_metadata!`. Passing `replace: true` replaces the file's metadata entirely with the freshly extracted values, instead of merging them. This is useful when you want to remove stale custom metadata keys.
|
|
35
|
+
|
|
36
|
+
```rb
|
|
37
|
+
uploaded_file.metadata["custom"] = "stale value"
|
|
38
|
+
|
|
39
|
+
uploaded_file.refresh_metadata! # merge (default)
|
|
40
|
+
uploaded_file.metadata["custom"] #=> "stale value" (preserved)
|
|
41
|
+
|
|
42
|
+
uploaded_file.refresh_metadata!(replace: true) # replace
|
|
43
|
+
uploaded_file.metadata["custom"] #=> nil (removed)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Other improvements
|
|
47
|
+
|
|
48
|
+
* The `s3` storage now prefers using `TransferManager#upload_stream` over the deprecated `#upload_stream` method on the S3 object, when `TransferManager` is available. This avoids deprecation warnings from newer versions of the AWS SDK.
|
|
49
|
+
|
|
50
|
+
* The `column` plugin no longer attempts to deserialize an empty string as JSON. Previously this would raise a parse error; now the attachment is treated as blank.
|
|
51
|
+
|
|
52
|
+
* The `backgrounding` plugin now correctly forwards keyword arguments passed to `Attacher#promote_cached` into the `promote_block` callback.
|
|
53
|
+
|
|
54
|
+
```rb
|
|
55
|
+
Shrine::Attacher.promote_block do |attacher:, my_option:, **|
|
|
56
|
+
# my_option was previously not forwarded here
|
|
57
|
+
SomePromoteJob.perform_async(attacher.dump, my_option: my_option)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attacher.promote_cached(my_option: "value") # now forwarded correctly
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
* `UploadedFile` no longer produces URI default parser warnings (`URI::RFC3986_PARSER.make_regexp is obsolete`) when verbose warnings are enabled.
|
|
64
|
+
|
|
65
|
+
## Backwards compatibility
|
|
66
|
+
|
|
67
|
+
* Support for Ruby versions below 3.2 has been dropped. Ruby >= 3.2 is now required.
|
|
68
|
+
|
|
69
|
+
* ImageProcessing 2.0 made `mini_magick` and `ruby-vips` soft dependencies that are no longer loaded automatically. If you use either gem for image processing, you will need to add it explicitly to your `Gemfile`:
|
|
70
|
+
|
|
71
|
+
```rb
|
|
72
|
+
gem "mini_magick"
|
|
73
|
+
# or
|
|
74
|
+
gem "ruby-vips"
|
|
75
|
+
```
|
data/lib/shrine/attacher.rb
CHANGED
|
@@ -22,8 +22,8 @@ class Shrine
|
|
|
22
22
|
#
|
|
23
23
|
# attacher = Attacher.from_data({ "id" => "...", "storage" => "...", "metadata" => { ... } })
|
|
24
24
|
# attacher.file #=> #<Shrine::UploadedFile>
|
|
25
|
-
def from_data(data, **
|
|
26
|
-
attacher = new(**
|
|
25
|
+
def from_data(data, **)
|
|
26
|
+
attacher = new(**)
|
|
27
27
|
attacher.load_data(data)
|
|
28
28
|
attacher
|
|
29
29
|
end
|
|
@@ -47,14 +47,14 @@ class Shrine
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Returns the temporary storage identifier.
|
|
50
|
-
def cache_key
|
|
50
|
+
def cache_key = @cache.to_sym
|
|
51
51
|
# Returns the permanent storage identifier.
|
|
52
|
-
def store_key
|
|
52
|
+
def store_key = @store.to_sym
|
|
53
53
|
|
|
54
54
|
# Returns the uploader that is used for the temporary storage.
|
|
55
|
-
def cache
|
|
55
|
+
def cache = shrine_class.new(cache_key)
|
|
56
56
|
# Returns the uploader that is used for the permanent storage.
|
|
57
|
-
def store
|
|
57
|
+
def store = shrine_class.new(store_key)
|
|
58
58
|
|
|
59
59
|
# Calls #attach_cached, but skips if value is an empty string (this is
|
|
60
60
|
# useful when the uploaded file comes from form fields). Forwards any
|
|
@@ -67,14 +67,14 @@ class Shrine
|
|
|
67
67
|
#
|
|
68
68
|
# # ignores the assignment when a blank string is given
|
|
69
69
|
# attacher.assign("")
|
|
70
|
-
def assign(value, **
|
|
70
|
+
def assign(value, **)
|
|
71
71
|
return if value == "" # skip empty hidden field
|
|
72
72
|
|
|
73
73
|
if value.is_a?(Hash) || value.is_a?(String)
|
|
74
74
|
return if uploaded_file(value) == file # skip assignment for current file
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
attach_cached(value, **
|
|
77
|
+
attach_cached(value, **)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# Sets an existing cached file, or uploads an IO object to temporary
|
|
@@ -92,11 +92,11 @@ class Shrine
|
|
|
92
92
|
#
|
|
93
93
|
# # sets an existing cached file from Hash data
|
|
94
94
|
# attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} })
|
|
95
|
-
def attach_cached(value, **
|
|
95
|
+
def attach_cached(value, **)
|
|
96
96
|
if value.is_a?(String) || value.is_a?(Hash)
|
|
97
|
-
change(cached(value, **
|
|
97
|
+
change(cached(value, **))
|
|
98
98
|
else
|
|
99
|
-
attach(value, storage: cache_key, action: :cache, **
|
|
99
|
+
attach(value, storage: cache_key, action: :cache, **)
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -113,8 +113,8 @@ class Shrine
|
|
|
113
113
|
#
|
|
114
114
|
# # removes the attachment
|
|
115
115
|
# attacher.attach(nil)
|
|
116
|
-
def attach(io, storage: store_key, **
|
|
117
|
-
file = upload(io, storage, **
|
|
116
|
+
def attach(io, storage: store_key, **)
|
|
117
|
+
file = upload(io, storage, **) if io
|
|
118
118
|
|
|
119
119
|
change(file)
|
|
120
120
|
end
|
|
@@ -158,8 +158,8 @@ class Shrine
|
|
|
158
158
|
# attacher.cached? #=> true
|
|
159
159
|
# attacher.promote_cached
|
|
160
160
|
# attacher.stored? #=> true
|
|
161
|
-
def promote_cached(**
|
|
162
|
-
promote(**
|
|
161
|
+
def promote_cached(**)
|
|
162
|
+
promote(**) if promote?
|
|
163
163
|
end
|
|
164
164
|
|
|
165
165
|
# Uploads current file to permanent storage and sets the stored file.
|
|
@@ -167,8 +167,8 @@ class Shrine
|
|
|
167
167
|
# attacher.cached? #=> true
|
|
168
168
|
# attacher.promote
|
|
169
169
|
# attacher.stored? #=> true
|
|
170
|
-
def promote(storage: store_key, **
|
|
171
|
-
set upload(file, storage, action: :store, **
|
|
170
|
+
def promote(storage: store_key, **)
|
|
171
|
+
set upload(file, storage, action: :store, **)
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
# Delegates to `Shrine.upload`, passing the #context.
|
|
@@ -178,8 +178,8 @@ class Shrine
|
|
|
178
178
|
#
|
|
179
179
|
# # pass additional options for the uploader
|
|
180
180
|
# attacher.upload(io, :store, metadata: { "foo" => "bar" })
|
|
181
|
-
def upload(io, storage = store_key, **
|
|
182
|
-
shrine_class.upload(io, storage, **context, **
|
|
181
|
+
def upload(io, storage = store_key, **)
|
|
182
|
+
shrine_class.upload(io, storage, **context, **)
|
|
183
183
|
end
|
|
184
184
|
|
|
185
185
|
# If a new file was attached, deletes previously attached file if any.
|
|
@@ -249,8 +249,8 @@ class Shrine
|
|
|
249
249
|
#
|
|
250
250
|
# attacher.file = nil
|
|
251
251
|
# attacher.url #=> nil
|
|
252
|
-
def url(**
|
|
253
|
-
file&.url(**
|
|
252
|
+
def url(**)
|
|
253
|
+
file&.url(**)
|
|
254
254
|
end
|
|
255
255
|
|
|
256
256
|
# Returns whether the attachment has changed.
|
data/lib/shrine/attachment.rb
CHANGED
|
@@ -23,8 +23,8 @@ class Shrine
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
module FileMethods
|
|
26
|
-
def urlsafe_dump(**
|
|
27
|
-
self.class.urlsafe_dump(self, **
|
|
26
|
+
def urlsafe_dump(**)
|
|
27
|
+
self.class.urlsafe_dump(self, **)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def urlsafe_data(metadata: [])
|
|
@@ -45,8 +45,8 @@ class Shrine
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
module FileClassMethods
|
|
48
|
-
def urlsafe_dump(file, **
|
|
49
|
-
data = file.urlsafe_data(**
|
|
48
|
+
def urlsafe_dump(file, **)
|
|
49
|
+
data = file.urlsafe_data(**)
|
|
50
50
|
|
|
51
51
|
shrine_class.urlsafe_serialize(data)
|
|
52
52
|
end
|
|
@@ -29,11 +29,9 @@ class Shrine
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
module InstanceMethods
|
|
32
|
-
def extract_metadata(io, **
|
|
32
|
+
def extract_metadata(io, **)
|
|
33
33
|
metadata = super
|
|
34
|
-
|
|
35
|
-
extract_custom_metadata(io, **options, metadata: metadata)
|
|
36
|
-
|
|
34
|
+
extract_custom_metadata(io, **, metadata:)
|
|
37
35
|
metadata
|
|
38
36
|
end
|
|
39
37
|
|
|
@@ -14,14 +14,14 @@ class Shrine
|
|
|
14
14
|
#
|
|
15
15
|
# Shrine::Attacher.retrieve(model: photo, name: :image, file: file_data)
|
|
16
16
|
# #=> #<ImageUploader::Attacher>
|
|
17
|
-
def retrieve(model: nil, entity: nil, name:, file:, **
|
|
17
|
+
def retrieve(model: nil, entity: nil, name:, file:, **)
|
|
18
18
|
fail ArgumentError, "either :model or :entity is required" unless model || entity
|
|
19
19
|
|
|
20
20
|
record = model || entity
|
|
21
21
|
|
|
22
|
-
attacher = record.send(:"#{name}_attacher", **
|
|
23
|
-
attacher ||= from_model(record, name, **
|
|
24
|
-
attacher ||= from_entity(record, name, **
|
|
22
|
+
attacher = record.send(:"#{name}_attacher", **) if record.respond_to?(:"#{name}_attacher")
|
|
23
|
+
attacher ||= from_model(record, name, **) if model
|
|
24
|
+
attacher ||= from_entity(record, name, **) if entity
|
|
25
25
|
|
|
26
26
|
if attacher.file != attacher.uploaded_file(file)
|
|
27
27
|
fail Shrine::AttachmentChanged, "attachment has changed"
|
|
@@ -43,13 +43,13 @@ class Shrine
|
|
|
43
43
|
#
|
|
44
44
|
# This more convenient to use with concrete persistence plugins, which
|
|
45
45
|
# provide defaults for reloading and persistence.
|
|
46
|
-
def abstract_atomic_promote(reload:, persist:,
|
|
46
|
+
def abstract_atomic_promote(reload:, persist:, **, &block)
|
|
47
47
|
original_file = file
|
|
48
48
|
|
|
49
|
-
result = promote(**
|
|
49
|
+
result = promote(**)
|
|
50
50
|
|
|
51
51
|
begin
|
|
52
|
-
abstract_atomic_persist(original_file, reload
|
|
52
|
+
abstract_atomic_persist(original_file, reload:, persist:, &block)
|
|
53
53
|
result
|
|
54
54
|
rescue Shrine::AttachmentChanged
|
|
55
55
|
destroy_attached
|
|
@@ -67,19 +67,19 @@ class Shrine
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# Does a background promote if promote block was registered.
|
|
70
|
-
def promote_cached(**
|
|
70
|
+
def promote_cached(**)
|
|
71
71
|
if promote? && promote_block
|
|
72
|
-
promote_background
|
|
72
|
+
promote_background(**)
|
|
73
73
|
else
|
|
74
74
|
super
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
# Calls the registered promote block.
|
|
79
|
-
def promote_background(**
|
|
79
|
+
def promote_background(**)
|
|
80
80
|
fail Error, "promote block is not registered" unless promote_block
|
|
81
81
|
|
|
82
|
-
background_block(promote_block, **
|
|
82
|
+
background_block(promote_block, **)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Does a background destroy if destroy block was registered.
|
|
@@ -92,19 +92,19 @@ class Shrine
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
# Calls the registered destroy block.
|
|
95
|
-
def destroy_background(**
|
|
95
|
+
def destroy_background(**)
|
|
96
96
|
fail Error, "destroy block is not registered" unless destroy_block
|
|
97
97
|
|
|
98
|
-
background_block(destroy_block, **
|
|
98
|
+
background_block(destroy_block, **)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
private
|
|
102
102
|
|
|
103
|
-
def background_block(block, **
|
|
103
|
+
def background_block(block, **)
|
|
104
104
|
if block.arity == 1
|
|
105
|
-
block.call(self, **
|
|
105
|
+
block.call(self, **)
|
|
106
106
|
else
|
|
107
|
-
instance_exec(
|
|
107
|
+
instance_exec(**, &block)
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
end
|
|
@@ -16,8 +16,8 @@ class Shrine
|
|
|
16
16
|
# from a database record column.
|
|
17
17
|
#
|
|
18
18
|
# Attacher.from_column('{"id":"...","storage":"...","metadata":{...}}')
|
|
19
|
-
def from_column(data, **
|
|
20
|
-
attacher = new(**
|
|
19
|
+
def from_column(data, **)
|
|
20
|
+
attacher = new(**)
|
|
21
21
|
attacher.load_column(data)
|
|
22
22
|
attacher
|
|
23
23
|
end
|
|
@@ -28,8 +28,8 @@ class Shrine
|
|
|
28
28
|
attr_reader :column_serializer
|
|
29
29
|
|
|
30
30
|
# Allows overriding the default column serializer.
|
|
31
|
-
def initialize(column_serializer: shrine_class.opts[:column][:serializer], **
|
|
32
|
-
super(**
|
|
31
|
+
def initialize(column_serializer: shrine_class.opts[:column][:serializer], **)
|
|
32
|
+
super(**)
|
|
33
33
|
@column_serializer = column_serializer
|
|
34
34
|
end
|
|
35
35
|
|
|
@@ -75,6 +75,8 @@ class Shrine
|
|
|
75
75
|
# Attacher.deserialize_column(nil)
|
|
76
76
|
# #=> nil
|
|
77
77
|
def deserialize_column(data)
|
|
78
|
+
return nil if data == ""
|
|
79
|
+
|
|
78
80
|
if column_serializer && data && !data.is_a?(Hash)
|
|
79
81
|
column_serializer.load(data)
|
|
80
82
|
else
|
|
@@ -16,16 +16,16 @@ class Shrine
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
module AttacherMethods
|
|
19
|
-
def url(**
|
|
20
|
-
super || default_url(**
|
|
19
|
+
def url(**)
|
|
20
|
+
super || default_url(**)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
-
def default_url(**
|
|
25
|
+
def default_url(**)
|
|
26
26
|
return unless default_url_block
|
|
27
27
|
|
|
28
|
-
url = instance_exec(
|
|
28
|
+
url = instance_exec(**, &default_url_block)
|
|
29
29
|
|
|
30
30
|
[*default_url_host, url].join
|
|
31
31
|
end
|