activestorage 8.1.3 → 8.1.4
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 +110 -0
- data/app/controllers/active_storage/blobs/proxy_controller.rb +18 -4
- data/app/controllers/active_storage/blobs/redirect_controller.rb +20 -4
- data/app/controllers/active_storage/representations/proxy_controller.rb +18 -4
- data/app/controllers/active_storage/representations/redirect_controller.rb +21 -4
- data/app/models/active_storage/attachment.rb +7 -3
- data/app/models/active_storage/blob.rb +11 -11
- data/app/models/active_storage/variant.rb +1 -1
- data/lib/active_storage/analyzer/image_analyzer/vips.rb +1 -16
- data/lib/active_storage/attached/changes/create_many.rb +2 -1
- data/lib/active_storage/attached/changes/create_one.rb +2 -1
- data/lib/active_storage/attached/changes/delete_many.rb +2 -1
- data/lib/active_storage/attached/changes/delete_one.rb +2 -1
- data/lib/active_storage/attached/many.rb +4 -0
- data/lib/active_storage/attached/model.rb +13 -2
- data/lib/active_storage/attached/one.rb +6 -0
- data/lib/active_storage/engine.rb +10 -0
- data/lib/active_storage/gem_version.rb +1 -1
- data/lib/active_storage/previewer.rb +1 -1
- data/lib/active_storage/service/mirror_service.rb +5 -3
- data/lib/active_storage/transformers/vips.rb +9 -0
- data/lib/active_storage/vips.rb +44 -0
- data/lib/active_storage.rb +4 -0
- metadata +14 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 363218fe84c4760d1243ee6f1325312eab52a4d1d7c33f512022e1a8ad4ed8a5
|
|
4
|
+
data.tar.gz: 603439dba3d29a9f57b433232954d85d9ec70905ab6114d10e370b2136283258
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5277345f853e82ae059c49bde6c2664f84516dfbd4da948e7a48b437da363682104b56fb895b75e7010f7201c5ced03106297c24bba67977a2567b045b2faa60
|
|
7
|
+
data.tar.gz: 27b8cbc33599414d28774b41d1d3891a1fb803e4316e217f24e47903f8a2d5e6f92fd7ffb2d50d9b02efddf26fa08ae6a199993dbbfc4cdad37aab813df1b906
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,113 @@
|
|
|
1
|
+
## Rails 8.1.4 (September 24, 2026) ##
|
|
2
|
+
|
|
3
|
+
* Warn instead of aborting boot when `image_processing` 2.x is installed without `ruby-vips`
|
|
4
|
+
or `mini_magick`.
|
|
5
|
+
|
|
6
|
+
Both gems became soft dependencies in `image_processing` 2.0, and the `LoadError` raised for
|
|
7
|
+
a missing one names that gem instead of `image_processing`. Active Storage did not recognize
|
|
8
|
+
the message, so it re-raised rather than logging its usual warning.
|
|
9
|
+
|
|
10
|
+
Fixes #58413.
|
|
11
|
+
|
|
12
|
+
*Janko Marohnić*
|
|
13
|
+
|
|
14
|
+
* Fix `MirrorService#mirror` losing blob metadata when copying to mirrors.
|
|
15
|
+
|
|
16
|
+
Mirrored copies on S3, Azure, and GCS were served as `application/octet-stream`
|
|
17
|
+
because `content_type`, `filename`, `disposition`, and `custom_metadata` were
|
|
18
|
+
dropped. Forward the blob's `service_metadata` to each mirror's upload.
|
|
19
|
+
|
|
20
|
+
Fixes #57270.
|
|
21
|
+
|
|
22
|
+
*Maksim Romanov* + *Andrii Furmanets*
|
|
23
|
+
|
|
24
|
+
* Prevent `ActiveStorage.touch_attachment_records = false` from crashing the attachment of a Blob.
|
|
25
|
+
|
|
26
|
+
When `ActiveStorage.touch_attachment_records` was set to `false`, attaching a existing Blob to a Record
|
|
27
|
+
would raise an error. This is now fixed.
|
|
28
|
+
|
|
29
|
+
*Edouard Chin*
|
|
30
|
+
|
|
31
|
+
* Fix `MirrorService#mirror` raising `ActiveStorage::IntegrityError` when
|
|
32
|
+
mirroring without a checksum (e.g., `track_variants: false`).
|
|
33
|
+
|
|
34
|
+
*Denis Savchuk*
|
|
35
|
+
|
|
36
|
+
* Preserve attachment changes when converting record to another class using STI.
|
|
37
|
+
|
|
38
|
+
*fatkodima*
|
|
39
|
+
|
|
40
|
+
* Correct unexpected behavior resulting from dependent: :purge when using
|
|
41
|
+
has_one_attached or has_many_attached. Fixes #36423.
|
|
42
|
+
|
|
43
|
+
*Mark Oveson*
|
|
44
|
+
|
|
45
|
+
* Define `as_json` on `ActiveStorage::Attached::One` and `ActiveStorage::Attached::Many`.
|
|
46
|
+
|
|
47
|
+
The proxies hold a back-reference to the owning record in `@record`. Without an explicit
|
|
48
|
+
`as_json`, the default `Object#as_json` fall back serialized `instance_values`, which made
|
|
49
|
+
`record.to_json` recurse infinitely whenever the attached name collided with a model
|
|
50
|
+
attribute (e.g. an `ignored_columns` column brought back by `select('*')`).
|
|
51
|
+
|
|
52
|
+
`Attached::One#as_json` now returns the attachment record's JSON when attached and `nil`
|
|
53
|
+
otherwise. `Attached::Many#as_json` returns the attachment records' JSON as an array.
|
|
54
|
+
|
|
55
|
+
*Renxiang Cai*
|
|
56
|
+
|
|
57
|
+
* Fix Rails hanging when generating video previews
|
|
58
|
+
|
|
59
|
+
When Rails runs in a background process group, ffmpeg's attempt to configure the terminal for
|
|
60
|
+
interactive input would send SIGTTOU to the Rails process, suspending it indefinitely.
|
|
61
|
+
|
|
62
|
+
Fixed by explicitly passing /dev/null to ffmpeg's stdin.
|
|
63
|
+
|
|
64
|
+
*Jonathan del Strother*
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Rails 8.1.3.1 (July 29, 2026) ##
|
|
68
|
+
|
|
69
|
+
* Disable libvips's unfuzzed image loaders and savers.
|
|
70
|
+
|
|
71
|
+
libvips flags some of its loaders and savers as "unfuzzed" or "untrusted", meaning they are only
|
|
72
|
+
safe for trusted content. Active Storage will call `Vips.block_untrusted(true)` to disable them
|
|
73
|
+
while booting. An application that needs a specific loader or saver may re-enable it in an
|
|
74
|
+
initializer.
|
|
75
|
+
|
|
76
|
+
This is a breaking change for applications that process image types with an unfuzzed loader or
|
|
77
|
+
saver. Variant transformation of BMP, ICO, and PSD attachments will raise `Vips::Error`, and
|
|
78
|
+
analysis of these and other types such as SVG, JPEG XL, JPEG 2000, and Netpbm will no longer
|
|
79
|
+
record `width` and `height`. Requesting an unfuzzed output format, typically FITS, JXL, or
|
|
80
|
+
anything delegated to ImageMagick, will also raise `Vips::Error`. Attaching, storing, and
|
|
81
|
+
downloading are unchanged.
|
|
82
|
+
|
|
83
|
+
An application seeing `Vips::Error` raised during image transformation may wish to remove the
|
|
84
|
+
affected content types from `config.active_storage.variable_content_types` in an initializer.
|
|
85
|
+
Active Storage will then treat those attachments as not variable and will not generate variants
|
|
86
|
+
for them. This most often matters to an application that transforms images during a request
|
|
87
|
+
rather than in a background job, where the failure surfaces as an error response instead of a
|
|
88
|
+
failed job.
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
Rails.application.config.active_storage.variable_content_types -=
|
|
92
|
+
%w[ image/bmp image/vnd.microsoft.icon image/vnd.adobe.photoshop ]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Applications using the `:mini_magick` variant processor will see no change in how their
|
|
96
|
+
attachments are processed, but the loaders and savers will be disabled process-wide whenever
|
|
97
|
+
ruby-vips is installed, and the version requirements below will still apply. Such an application
|
|
98
|
+
may remove ruby-vips from its Gemfile to avoid both.
|
|
99
|
+
|
|
100
|
+
The minimum supported version of libvips is now 8.13, and the minimum supported version of
|
|
101
|
+
ruby-vips is now 2.2.1. These are the earliest versions that are capable of disabling untrusted
|
|
102
|
+
operations. When ruby-vips is installed and either minimum is not met, Active Storage will raise
|
|
103
|
+
a `RuntimeError` while booting rather than run in an unsecurable environment.
|
|
104
|
+
|
|
105
|
+
[GHSA-xr9x-r78c-5hrm]
|
|
106
|
+
[CVE-2026-66066]
|
|
107
|
+
|
|
108
|
+
*Mike Dalessio*
|
|
109
|
+
|
|
110
|
+
|
|
1
111
|
## Rails 8.1.3 (March 24, 2026) ##
|
|
2
112
|
|
|
3
113
|
* Fix `ActiveStorage::Blob` content type predicate methods to handle `nil`.
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Finds a blob by a +signed_id+ and proxies the file through the application.
|
|
4
|
+
# The blob is streamed from storage directly to the response. This avoids having
|
|
5
|
+
# a redirect and makes files easier to cache.
|
|
4
6
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
7
|
+
# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
|
|
8
|
+
# The response sets the HTTP cache to public and allows browsers and proxies to cache it indefinitely.
|
|
9
|
+
#
|
|
10
|
+
# The URLs created for this controller are set to never expire by default.
|
|
11
|
+
# To make URLs expire, pass the +expires_in+ option when generating the URL:
|
|
12
|
+
#
|
|
13
|
+
# rails_storage_proxy_url(blob, expires_in: 1.minute)
|
|
14
|
+
#
|
|
15
|
+
# Or set the default for all Active Storage URLs:
|
|
16
|
+
#
|
|
17
|
+
# config.active_storage.urls_expire_in = 1.day
|
|
18
|
+
#
|
|
19
|
+
# WARNING: All Active Storage controllers are publicly accessible by default.
|
|
20
|
+
# Anyone who knows the URL can access the file, even if the rest of your application requires
|
|
21
|
+
# authentication. If your files require access control consider implementing
|
|
8
22
|
# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
|
|
9
23
|
class ActiveStorage::Blobs::ProxyController < ActiveStorage::BaseController
|
|
10
24
|
include ActiveStorage::SetBlob
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Finds a blob by a +signed_id+ and redirects to a blob's expiring service URL.
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
5
|
+
# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
|
|
6
|
+
#
|
|
7
|
+
# The URLs created for this controller are set to never expire by default.
|
|
8
|
+
# To make URLs expire, pass the +expires_in+ option when generating the URL:
|
|
9
|
+
#
|
|
10
|
+
# rails_storage_redirect_url(blob, expires_in: 1.minute)
|
|
11
|
+
#
|
|
12
|
+
# Or set the default for all Active Storage URLs:
|
|
13
|
+
#
|
|
14
|
+
# config.active_storage.urls_expire_in = 1.day
|
|
15
|
+
#
|
|
16
|
+
# The service URLs are set to expire in 5 minutes by default.
|
|
17
|
+
# The default can be changed for all service URLs:
|
|
18
|
+
#
|
|
19
|
+
# config.active_storage.service_urls_expire_in = 1.hour
|
|
20
|
+
#
|
|
21
|
+
# WARNING: All Active Storage controllers are publicly accessible by default.
|
|
22
|
+
# Anyone who knows the URL can access the file, even if the rest of your application requires
|
|
23
|
+
# authentication. If your files require access control consider implementing
|
|
8
24
|
# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
|
|
9
25
|
class ActiveStorage::Blobs::RedirectController < ActiveStorage::BaseController
|
|
10
26
|
include ActiveStorage::SetBlob
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Finds a representation by a +signed_id+ and a +variation_key+, and proxies the file through the application.
|
|
4
|
+
# The representation is streamed from storage directly to the response. This avoids having
|
|
5
|
+
# a redirect and makes files easier to cache.
|
|
4
6
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
7
|
+
# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
|
|
8
|
+
# The response sets the HTTP cache to public and allows browsers and proxies to cache it indefinitely.
|
|
9
|
+
#
|
|
10
|
+
# The URLs created for this controller are set to never expire by default.
|
|
11
|
+
# To make URLs expire, pass the +expires_in+ option when generating the URL:
|
|
12
|
+
#
|
|
13
|
+
# rails_storage_proxy_url(representation, expires_in: 1.minute)
|
|
14
|
+
#
|
|
15
|
+
# Or set the default for all Active Storage URLs:
|
|
16
|
+
#
|
|
17
|
+
# config.active_storage.urls_expire_in = 1.day
|
|
18
|
+
#
|
|
19
|
+
# WARNING: All Active Storage controllers are publicly accessible by default.
|
|
20
|
+
# Anyone who knows the URL can access the file, even if the rest of your application requires
|
|
21
|
+
# authentication. If your files require access control consider implementing
|
|
8
22
|
# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
|
|
9
23
|
class ActiveStorage::Representations::ProxyController < ActiveStorage::Representations::BaseController
|
|
10
24
|
include ActiveStorage::Streaming
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# Finds a representation by a +signed_id+ and a +variation_key+, and redirects to a representation's expiring
|
|
4
|
+
# service URL.
|
|
4
5
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
|
|
7
|
+
#
|
|
8
|
+
# The URLs created for this controller are set to never expire by default.
|
|
9
|
+
# To make URLs expire, pass the +expires_in+ option when generating the URL:
|
|
10
|
+
#
|
|
11
|
+
# rails_storage_redirect_url(representation, expires_in: 1.minute)
|
|
12
|
+
#
|
|
13
|
+
# Or set the default for all Active Storage URLs:
|
|
14
|
+
#
|
|
15
|
+
# config.active_storage.urls_expire_in = 1.day
|
|
16
|
+
#
|
|
17
|
+
# The service URLs are set to expire in 5 minutes by default.
|
|
18
|
+
# The default can be changed for all service URLs:
|
|
19
|
+
#
|
|
20
|
+
# config.active_storage.service_urls_expire_in = 1.hour
|
|
21
|
+
#
|
|
22
|
+
# WARNING: All Active Storage controllers are publicly accessible by default.
|
|
23
|
+
# Anyone who knows the URL can access the file, even if the rest of your application requires
|
|
24
|
+
# authentication. If your files require access control consider implementing
|
|
8
25
|
# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
|
|
9
26
|
class ActiveStorage::Representations::RedirectController < ActiveStorage::Representations::BaseController
|
|
10
27
|
def show
|
|
@@ -34,7 +34,7 @@ class ActiveStorage::Attachment < ActiveStorage::Record
|
|
|
34
34
|
delegate :signed_id, to: :blob
|
|
35
35
|
|
|
36
36
|
after_create_commit :mirror_blob_later, :analyze_blob_later, :transform_variants_later
|
|
37
|
-
after_destroy_commit :
|
|
37
|
+
after_destroy_commit :purge_dependent_blob
|
|
38
38
|
|
|
39
39
|
##
|
|
40
40
|
# :singleton-method:
|
|
@@ -147,8 +147,12 @@ class ActiveStorage::Attachment < ActiveStorage::Record
|
|
|
147
147
|
end
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
-
def
|
|
151
|
-
|
|
150
|
+
def purge_dependent_blob
|
|
151
|
+
if dependent == :purge_later
|
|
152
|
+
blob&.purge_later
|
|
153
|
+
elsif dependent == :purge
|
|
154
|
+
blob&.purge
|
|
155
|
+
end
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
def dependent
|
|
@@ -282,6 +282,16 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
282
282
|
service.compose(keys, key, **service_metadata)
|
|
283
283
|
end
|
|
284
284
|
|
|
285
|
+
def service_metadata # :nodoc:
|
|
286
|
+
if forcibly_serve_as_binary?
|
|
287
|
+
{ content_type: ActiveStorage.binary_content_type, disposition: :attachment, filename: filename, custom_metadata: custom_metadata }
|
|
288
|
+
elsif !allowed_inline?
|
|
289
|
+
{ content_type: content_type, disposition: :attachment, filename: filename, custom_metadata: custom_metadata }
|
|
290
|
+
else
|
|
291
|
+
{ content_type: content_type, custom_metadata: custom_metadata }
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
285
295
|
# Downloads the file associated with this blob. If no block is given, the entire file is read into memory and returned.
|
|
286
296
|
# That'll use a lot of RAM for very large files. If a block is given, then the download is streamed and yielded in chunks.
|
|
287
297
|
def download(&block)
|
|
@@ -371,16 +381,6 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
371
381
|
ActiveStorage.web_image_content_types.include?(content_type)
|
|
372
382
|
end
|
|
373
383
|
|
|
374
|
-
def service_metadata
|
|
375
|
-
if forcibly_serve_as_binary?
|
|
376
|
-
{ content_type: ActiveStorage.binary_content_type, disposition: :attachment, filename: filename, custom_metadata: custom_metadata }
|
|
377
|
-
elsif !allowed_inline?
|
|
378
|
-
{ content_type: content_type, disposition: :attachment, filename: filename, custom_metadata: custom_metadata }
|
|
379
|
-
else
|
|
380
|
-
{ content_type: content_type, custom_metadata: custom_metadata }
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
|
|
384
384
|
def touch_attachments
|
|
385
385
|
attachments.then do |relation|
|
|
386
386
|
if ActiveStorage.touch_attachment_records
|
|
@@ -389,7 +389,7 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
389
389
|
relation
|
|
390
390
|
end
|
|
391
391
|
end.each do |attachment|
|
|
392
|
-
attachment.touch
|
|
392
|
+
attachment.touch unless attachment.new_record?
|
|
393
393
|
end
|
|
394
394
|
end
|
|
395
395
|
|
|
@@ -78,7 +78,7 @@ class ActiveStorage::Variant
|
|
|
78
78
|
#
|
|
79
79
|
# Use <tt>url_for(variant)</tt> (or the implied form, like <tt>link_to variant</tt> or <tt>redirect_to variant</tt>) to get the stable URL
|
|
80
80
|
# for a variant that points to the ActiveStorage::Representations::ProxyController or ActiveStorage::Representations::RedirectController,
|
|
81
|
-
# which in turn will use this +
|
|
81
|
+
# which in turn will use this +url+ method for its redirection.
|
|
82
82
|
def url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline)
|
|
83
83
|
service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
|
|
84
84
|
end
|
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require "nokogiri"
|
|
5
|
-
rescue LoadError
|
|
6
|
-
# Ensure nokogiri is loaded before vips, which also depends on libxml2.
|
|
7
|
-
# See Nokogiri RFC: Stop exporting symbols:
|
|
8
|
-
# https://github.com/sparklemotion/nokogiri/discussions/2746
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
begin
|
|
12
|
-
gem "ruby-vips"
|
|
13
|
-
require "ruby-vips"
|
|
14
|
-
ActiveStorage::VIPS_AVAILABLE = true # :nodoc:
|
|
15
|
-
rescue LoadError => error
|
|
16
|
-
ActiveStorage::VIPS_AVAILABLE = false # :nodoc:
|
|
17
|
-
raise error unless error.message.match?(/libvips|ruby-vips/)
|
|
18
|
-
end
|
|
3
|
+
require "active_storage/vips"
|
|
19
4
|
|
|
20
5
|
module ActiveStorage
|
|
21
6
|
# This analyzer relies on the third-party {ruby-vips}[https://github.com/libvips/ruby-vips] gem. Ruby-vips requires
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveStorage
|
|
4
4
|
class Attached::Changes::CreateMany # :nodoc:
|
|
5
|
-
attr_reader :name, :
|
|
5
|
+
attr_reader :name, :attachables, :pending_uploads
|
|
6
|
+
attr_accessor :record
|
|
6
7
|
|
|
7
8
|
def initialize(name, record, attachables, pending_uploads: [])
|
|
8
9
|
@name, @record, @attachables = name, record, Array(attachables)
|
|
@@ -5,7 +5,8 @@ require "action_dispatch/http/upload"
|
|
|
5
5
|
|
|
6
6
|
module ActiveStorage
|
|
7
7
|
class Attached::Changes::CreateOne # :nodoc:
|
|
8
|
-
attr_reader :name, :
|
|
8
|
+
attr_reader :name, :attachable
|
|
9
|
+
attr_accessor :record
|
|
9
10
|
|
|
10
11
|
def initialize(name, record, attachable)
|
|
11
12
|
@name, @record, @attachable = name, record, attachable
|
|
@@ -75,7 +75,7 @@ module ActiveStorage
|
|
|
75
75
|
# The +:dependent+ option defaults to +:purge_later+. This means the attachment will be
|
|
76
76
|
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
|
77
77
|
# If an ActiveJob::Backend queue adapter is not set in the application set it to
|
|
78
|
-
#
|
|
78
|
+
# +:purge+ instead.
|
|
79
79
|
#
|
|
80
80
|
# If you need the attachment to use a service which differs from the globally configured one,
|
|
81
81
|
# pass the +:service+ option. For example:
|
|
@@ -177,7 +177,7 @@ module ActiveStorage
|
|
|
177
177
|
# The +:dependent+ option defaults to +:purge_later+. This means the attachments will be
|
|
178
178
|
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
|
179
179
|
# If an ActiveJob::Backend queue adapter is not set in the application set it to
|
|
180
|
-
#
|
|
180
|
+
# +:purge+ instead.
|
|
181
181
|
#
|
|
182
182
|
# If you need the attachment to use a service which differs from the globally configured one,
|
|
183
183
|
# pass the +:service+ option. For example:
|
|
@@ -278,6 +278,8 @@ module ActiveStorage
|
|
|
278
278
|
end
|
|
279
279
|
end
|
|
280
280
|
|
|
281
|
+
attr_writer :attachment_changes # :nodoc:
|
|
282
|
+
|
|
281
283
|
def attachment_changes # :nodoc:
|
|
282
284
|
@attachment_changes ||= {}
|
|
283
285
|
end
|
|
@@ -295,5 +297,14 @@ module ActiveStorage
|
|
|
295
297
|
def reload(*) # :nodoc:
|
|
296
298
|
super.tap { @attachment_changes = nil }
|
|
297
299
|
end
|
|
300
|
+
|
|
301
|
+
def becomes(klass) # :nodoc:
|
|
302
|
+
super.tap do |became|
|
|
303
|
+
attachment_changes = @attachment_changes&.each_value do |change|
|
|
304
|
+
change.record = became
|
|
305
|
+
end
|
|
306
|
+
became.attachment_changes = attachment_changes
|
|
307
|
+
end
|
|
308
|
+
end
|
|
298
309
|
end
|
|
299
310
|
end
|
|
@@ -45,6 +45,12 @@ module ActiveStorage
|
|
|
45
45
|
!attached?
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# Returns the attachment record's JSON representation, or +nil+ when no
|
|
49
|
+
# attachment is present.
|
|
50
|
+
def as_json(options = nil)
|
|
51
|
+
attached? ? attachment.as_json(options) : nil
|
|
52
|
+
end
|
|
53
|
+
|
|
48
54
|
# Attaches an +attachable+ to the record.
|
|
49
55
|
#
|
|
50
56
|
# If the record is persisted and unchanged, the attachment is saved to
|
|
@@ -115,6 +115,16 @@ module ActiveStorage
|
|
|
115
115
|
Please add `gem "image_processing", "~> 1.2"` to your Gemfile
|
|
116
116
|
or set `config.active_storage.variant_processor = :disabled`.
|
|
117
117
|
WARNING
|
|
118
|
+
when /ruby-vips/
|
|
119
|
+
ActiveStorage.logger.warn <<~WARNING.squish
|
|
120
|
+
Generating image variants with libvips requires the ruby-vips gem.
|
|
121
|
+
Please add `gem "ruby-vips", "~> 2.3"` to your Gemfile.
|
|
122
|
+
WARNING
|
|
123
|
+
when /mini_magick/
|
|
124
|
+
ActiveStorage.logger.warn <<~WARNING.squish
|
|
125
|
+
Generating image variants with ImageMagick requires the mini_magick gem.
|
|
126
|
+
Please add `gem "mini_magick", "~> 5.0"` to your Gemfile.
|
|
127
|
+
WARNING
|
|
118
128
|
else
|
|
119
129
|
raise
|
|
120
130
|
end
|
|
@@ -7,7 +7,7 @@ module ActiveStorage
|
|
|
7
7
|
# Wraps a set of mirror services and provides a single ActiveStorage::Service object that will all
|
|
8
8
|
# have the files uploaded to them. A +primary+ service is designated to answer calls to:
|
|
9
9
|
# * +download+
|
|
10
|
-
# * +
|
|
10
|
+
# * +exist?+
|
|
11
11
|
# * +url+
|
|
12
12
|
# * +url_for_direct_upload+
|
|
13
13
|
# * +headers_for_direct_upload+
|
|
@@ -66,10 +66,12 @@ module ActiveStorage
|
|
|
66
66
|
def mirror(key, checksum:)
|
|
67
67
|
instrument :mirror, key: key, checksum: checksum do
|
|
68
68
|
if (mirrors_in_need_of_mirroring = mirrors.select { |service| !service.exist?(key) }).any?
|
|
69
|
-
|
|
69
|
+
metadata = ActiveStorage::Blob.find_by(key: key)&.service_metadata || {}
|
|
70
|
+
|
|
71
|
+
primary.open(key, checksum: checksum, verify: checksum.present?) do |io|
|
|
70
72
|
mirrors_in_need_of_mirroring.each do |service|
|
|
71
73
|
io.rewind
|
|
72
|
-
service.upload key, io, checksum: checksum
|
|
74
|
+
service.upload key, io, checksum: checksum, **metadata
|
|
73
75
|
end
|
|
74
76
|
end
|
|
75
77
|
end
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Do not remove either of these requires.
|
|
4
|
+
#
|
|
5
|
+
# active_storage/vips disables libvips's unfuzzed loaders and savers.
|
|
6
|
+
#
|
|
7
|
+
# active_storage/vips has already loaded image_processing/vips, but requiring it here is what
|
|
8
|
+
# raises LoadError when the gem is missing, which the engine reports as an actionable warning.
|
|
9
|
+
require "active_storage/vips"
|
|
10
|
+
require "image_processing/vips"
|
|
11
|
+
|
|
3
12
|
module ActiveStorage
|
|
4
13
|
module Transformers
|
|
5
14
|
class Vips < ImageProcessingTransformer
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
require "active_storage"
|
|
6
|
+
require "active_support/core_ext/string/filters"
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require "nokogiri"
|
|
10
|
+
rescue LoadError
|
|
11
|
+
# Ensure nokogiri is loaded before vips, which also depends on libxml2.
|
|
12
|
+
# See Nokogiri RFC: Stop exporting symbols:
|
|
13
|
+
# https://github.com/sparklemotion/nokogiri/discussions/2746
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
gem "ruby-vips"
|
|
18
|
+
require "ruby-vips"
|
|
19
|
+
ActiveStorage::VIPS_AVAILABLE = true # :nodoc:
|
|
20
|
+
rescue LoadError => error
|
|
21
|
+
ActiveStorage::VIPS_AVAILABLE = false # :nodoc:
|
|
22
|
+
raise error unless error.message.match?(/libvips|ruby-vips/)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if ActiveStorage::VIPS_AVAILABLE
|
|
26
|
+
begin
|
|
27
|
+
# image_processing 2.0 calls Vips.block_untrusted(true) itself when it loads, so it has to load
|
|
28
|
+
# before the lines below. Leaving it to load later, when the transformer is autoloaded, would
|
|
29
|
+
# disable the loaders again after an application's initializers had re-enabled them.
|
|
30
|
+
require "image_processing/vips"
|
|
31
|
+
rescue LoadError
|
|
32
|
+
# image_processing is only needed to generate variants, not to analyze blobs.
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
unless Vips.respond_to?(:block_untrusted)
|
|
36
|
+
raise <<~ERROR.squish
|
|
37
|
+
libvips's unfuzzed operations are not safe to use with untrusted content, and Active Storage
|
|
38
|
+
cannot disable them. Disabling them requires libvips 8.13 or later and ruby-vips 2.2.1 or
|
|
39
|
+
later. Please upgrade libvips and ruby-vips, or remove the ruby-vips gem from your Gemfile.
|
|
40
|
+
ERROR
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Vips.block_untrusted(true)
|
|
44
|
+
end
|
data/lib/active_storage.rb
CHANGED
|
@@ -34,6 +34,7 @@ require "active_storage/deprecator"
|
|
|
34
34
|
require "active_storage/errors"
|
|
35
35
|
|
|
36
36
|
require "marcel"
|
|
37
|
+
require "openssl"
|
|
37
38
|
|
|
38
39
|
# :markup: markdown
|
|
39
40
|
# :include: ../README.md
|
|
@@ -358,6 +359,9 @@ module ActiveStorage
|
|
|
358
359
|
mattr_accessor :touch_attachment_records, default: true
|
|
359
360
|
mattr_accessor :urls_expire_in
|
|
360
361
|
|
|
362
|
+
# Configures the mount point for the default Active Storage routes. Accepts any
|
|
363
|
+
# value supported by `scope`, such as a string path prefix or a hash of routing
|
|
364
|
+
# options.
|
|
361
365
|
mattr_accessor :routes_prefix, default: "/rails/active_storage"
|
|
362
366
|
mattr_accessor :draw_routes, default: true
|
|
363
367
|
mattr_accessor :resolve_model_to_route, default: :rails_storage_redirect
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activestorage
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
@@ -15,56 +15,56 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 8.1.
|
|
18
|
+
version: 8.1.4
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 8.1.
|
|
25
|
+
version: 8.1.4
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: actionpack
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 8.1.
|
|
32
|
+
version: 8.1.4
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 8.1.
|
|
39
|
+
version: 8.1.4
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: activejob
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - '='
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 8.1.
|
|
46
|
+
version: 8.1.4
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 8.1.
|
|
53
|
+
version: 8.1.4
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: activerecord
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - '='
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 8.1.
|
|
60
|
+
version: 8.1.4
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - '='
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 8.1.
|
|
67
|
+
version: 8.1.4
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: marcel
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -186,16 +186,17 @@ files:
|
|
|
186
186
|
- lib/active_storage/transformers/transformer.rb
|
|
187
187
|
- lib/active_storage/transformers/vips.rb
|
|
188
188
|
- lib/active_storage/version.rb
|
|
189
|
+
- lib/active_storage/vips.rb
|
|
189
190
|
- lib/tasks/activestorage.rake
|
|
190
191
|
homepage: https://rubyonrails.org
|
|
191
192
|
licenses:
|
|
192
193
|
- MIT
|
|
193
194
|
metadata:
|
|
194
195
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
195
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.1.
|
|
196
|
-
documentation_uri: https://api.rubyonrails.org/v8.1.
|
|
196
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.1.4/activestorage/CHANGELOG.md
|
|
197
|
+
documentation_uri: https://api.rubyonrails.org/v8.1.4/
|
|
197
198
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
198
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.1.
|
|
199
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.1.4/activestorage
|
|
199
200
|
rubygems_mfa_required: 'true'
|
|
200
201
|
rdoc_options: []
|
|
201
202
|
require_paths:
|
|
@@ -211,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
211
212
|
- !ruby/object:Gem::Version
|
|
212
213
|
version: '0'
|
|
213
214
|
requirements: []
|
|
214
|
-
rubygems_version: 4.0.
|
|
215
|
+
rubygems_version: 4.0.20
|
|
215
216
|
specification_version: 4
|
|
216
217
|
summary: Local and cloud file storage framework.
|
|
217
218
|
test_files: []
|