activestorage 8.0.5 → 8.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb8d04a9237523518b1a6b00aab432decc944e0432e7296d1f283126c73eb374
4
- data.tar.gz: 74fabd6db3bfefae7e51f9be208fafb37b76e5df3bdc238bafe60fa8357bd405
3
+ metadata.gz: 2a42fb3f7bbc7e2032f582d6d934bfb06245afdd8fe2db1b07b35d6c8223a617
4
+ data.tar.gz: aefc082d3ed704bf9c675e40560d9eb3be6f2e315ea28d7313ceabb493a95b93
5
5
  SHA512:
6
- metadata.gz: e8555d671d585f19dcecb996f38d4aa4f182e3e98082856916e13cefa8f6f9c0288f11dbf83f7f80f641dbf82e5e93a39559bf7e86b98638b3737029c17660c4
7
- data.tar.gz: bd3b5a92c37258cecf1bca978f4a59a090c6bb20a30cf290479a173add2efa6de64ffe120d4649c1d4d9aa3b0e500c2ae7b24b44ee4b6413e65efafe7eac91e4
6
+ metadata.gz: c5c63ac998f475f5b3b9fb11d6bd57f0697b1ece83903f4283b35eefe490a0b0a6edb4e9b8d6d2fc4673c67948296c66af79bc25821a16ed9ad3144d76bdef5e
7
+ data.tar.gz: 12412531395d736326f067fdb7cdbe23f4a99a3167d4921d1882191baf4d5c79d6260d9ef598b1bcc90c0ffc35a78a2b898bcad9d8d2332c5f5749378eceeaca
data/CHANGELOG.md CHANGED
@@ -1,11 +1,11 @@
1
- ## Rails 8.0.5 (March 24, 2026) ##
1
+ ## Rails 8.1.3 (March 24, 2026) ##
2
2
 
3
3
  * Fix `ActiveStorage::Blob` content type predicate methods to handle `nil`.
4
4
 
5
5
  *Daichi KUDO*
6
6
 
7
7
 
8
- ## Rails 8.0.4.1 (March 23, 2026) ##
8
+ ## Rails 8.1.2.1 (March 23, 2026) ##
9
9
 
10
10
  * Filter user supplied metadata in DirectUploadController
11
11
 
@@ -28,6 +28,7 @@
28
28
 
29
29
  *Jean Boussier*
30
30
 
31
+
31
32
  * Prevent path traversal in `DiskService`.
32
33
 
33
34
  `DiskService#path_for` now raises an `InvalidKeyError` when passed keys with dot segments (".",
@@ -56,90 +57,118 @@
56
57
  *Mike Dalessio*
57
58
 
58
59
 
59
- ## Rails 8.0.4 (October 28, 2025) ##
60
-
61
- * No changes.
62
-
63
-
64
- ## Rails 8.0.3 (September 22, 2025) ##
65
-
66
- * Address deprecation of `Aws::S3::Object#upload_stream` in `ActiveStorage::Service::S3Service`.
67
-
68
- *Joshua Young*
69
-
70
- * Fix `config.active_storage.touch_attachment_records` to work with eager loading.
60
+ ## Rails 8.1.2 (January 08, 2026) ##
71
61
 
72
- *fatkodima*
62
+ * Restore ADC when signing URLs with IAM for GCS
73
63
 
64
+ ADC was previously used for automatic authorization when signing URLs with IAM.
65
+ Now it is again, but the auth client is memoized so that new credentials are only
66
+ requested when the current ones expire. Other auth methods can now be used
67
+ instead by setting the authorization on `ActiveStorage::Service::GCSService#iam_client`.
74
68
 
75
- ## Rails 8.0.2.1 (August 13, 2025) ##
69
+ ```ruby
70
+ ActiveStorage::Blob.service.iam_client.authorization = Google::Auth::ImpersonatedServiceAccountCredentials.new(options)
71
+ ```
76
72
 
77
- * Remove dangerous transformations
73
+ This is safer than setting `Google::Apis::RequestOptions.default.authorization`
74
+ because it only applies to Active Storage and does not affect other Google API
75
+ clients.
78
76
 
79
- [CVE-2025-24293]
77
+ *Justin Malčić*
80
78
 
81
- *Zack Deveau*
82
79
 
83
- ## Rails 8.0.2 (March 12, 2025) ##
80
+ ## Rails 8.1.1 (October 28, 2025) ##
84
81
 
85
- * A Blob will no longer autosave associated Attachment.
86
-
87
- This fixes an issue where a record with an attachment would have
88
- its dirty attributes reset, preventing your `after commit` callbacks
89
- on that record to behave as expected.
82
+ * No changes.
90
83
 
91
- Note that this change doesn't require any changes on your application
92
- and is supposed to be internal. Active Storage Attachment will continue
93
- to be autosaved (through a different relation).
94
84
 
95
- *Edouard-chin*
85
+ ## Rails 8.1.0 (October 22, 2025) ##
96
86
 
87
+ * Add structured events for Active Storage:
88
+ - `active_storage.service_upload`
89
+ - `active_storage.service_download`
90
+ - `active_storage.service_streaming_download`
91
+ - `active_storage.preview`
92
+ - `active_storage.service_delete`
93
+ - `active_storage.service_delete_prefixed`
94
+ - `active_storage.service_exist`
95
+ - `active_storage.service_url`
96
+ - `active_storage.service_mirror`
97
97
 
98
- ## Rails 8.0.1 (December 13, 2024) ##
98
+ *Gannon McGibbon*
99
99
 
100
- * No changes.
100
+ * Allow analyzers and variant transformer to be fully configurable
101
101
 
102
+ ```ruby
103
+ # ActiveStorage.analyzers can be set to an empty array:
104
+ config.active_storage.analyzers = []
105
+ # => ActiveStorage.analyzers = []
102
106
 
103
- ## Rails 8.0.0.1 (December 10, 2024) ##
107
+ # or use custom analyzer:
108
+ config.active_storage.analyzers = [ CustomAnalyzer ]
109
+ # => ActiveStorage.analyzers = [ CustomAnalyzer ]
110
+ ```
104
111
 
105
- * No changes.
112
+ If no configuration is provided, it will use the default analyzers.
106
113
 
114
+ You can also disable variant processor to remove warnings on startup about missing gems.
107
115
 
108
- ## Rails 8.0.0 (November 07, 2024) ##
116
+ ```ruby
117
+ config.active_storage.variant_processor = :disabled
118
+ ```
109
119
 
110
- * No changes.
120
+ *zzak*, *Alexandre Ruban*
111
121
 
122
+ * Remove deprecated `:azure` storage service.
112
123
 
113
- ## Rails 8.0.0.rc2 (October 30, 2024) ##
124
+ *Rafael Mendonça França*
114
125
 
115
- * No changes.
126
+ * Remove unnecessary calls to the GCP metadata server.
116
127
 
128
+ Calling Google::Auth.get_application_default triggers an explicit call to
129
+ the metadata server - given it was being called for significant number of
130
+ file operations, it can lead to considerable tail latencies and even metadata
131
+ server overloads. Instead, it's preferable (and significantly more efficient)
132
+ that applications use:
117
133
 
118
- ## Rails 8.0.0.rc1 (October 19, 2024) ##
134
+ ```ruby
135
+ Google::Apis::RequestOptions.default.authorization = Google::Auth.get_application_default(...)
136
+ ```
119
137
 
120
- * No changes.
138
+ In the cases applications do not set that, the GCP libraries automatically determine credentials.
121
139
 
140
+ This also enables using credentials other than those of the associated GCP
141
+ service account like when using impersonation.
122
142
 
123
- ## Rails 8.0.0.beta1 (September 26, 2024) ##
143
+ *Alex Coomans*
124
144
 
125
- * Deprecate `ActiveStorage::Service::AzureStorageService`.
145
+ * Direct upload progress accounts for server processing time.
126
146
 
127
- *zzak*
147
+ *Jeremy Daer*
128
148
 
129
- * Improve `ActiveStorage::Filename#sanitized` method to handle special characters more effectively.
130
- Replace the characters `"*?<>` with `-` if they exist in the Filename to match the Filename convention of Win OS.
149
+ * Delegate `ActiveStorage::Filename#to_str` to `#to_s`
131
150
 
132
- *Luong Viet Dung(Martin)*
151
+ Supports checking String equality:
133
152
 
134
- * Improve InvariableError, UnpreviewableError and UnrepresentableError message.
153
+ ```ruby
154
+ filename = ActiveStorage::Filename.new("file.txt")
155
+ filename == "file.txt" # => true
156
+ filename in "file.txt" # => true
157
+ "file.txt" == filename # => true
158
+ ```
135
159
 
136
- Include Blob ID and content_type in the messages.
160
+ *Sean Doyle*
137
161
 
138
- *Petrik de Heus*
162
+ * A Blob will no longer autosave associated Attachment.
139
163
 
140
- * Mark proxied files as `immutable` in their Cache-Control header
164
+ This fixes an issue where a record with an attachment would have
165
+ its dirty attributes reset, preventing your `after commit` callbacks
166
+ on that record to behave as expected.
141
167
 
142
- *Nate Matykiewicz*
168
+ Note that this change doesn't require any changes on your application
169
+ and is supposed to be internal. Active Storage Attachment will continue
170
+ to be autosaved (through a different relation).
143
171
 
172
+ *Edouard-chin*
144
173
 
145
- Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activestorage/CHANGELOG.md) for previous changes.
174
+ Please check [8-0-stable](https://github.com/rails/rails/blob/8-0-stable/activestorage/CHANGELOG.md) for previous changes.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Active Storage
2
2
 
3
- Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), [Google Cloud Storage](https://cloud.google.com/storage/docs/), or [Microsoft Azure Storage](https://azure.microsoft.com/en-us/services/storage/), and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage.
3
+ Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), or [Google Cloud Storage](https://cloud.google.com/storage/docs/), and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage.
4
4
 
5
5
  Files can be uploaded from the server to the cloud or directly from the client to the cloud.
6
6
 
@@ -173,7 +173,10 @@ Active Storage, with its included JavaScript library, supports uploading directl
173
173
  ```erb
174
174
  <%= form.file_field :attachments, multiple: true, direct_upload: true %>
175
175
  ```
176
- 3. That's it! Uploads begin upon form submission.
176
+
177
+ 3. Configure CORS on third-party storage services to allow direct upload requests.
178
+
179
+ 4. That's it! Uploads begin upon form submission.
177
180
 
178
181
  ### Direct upload JavaScript events
179
182
 
@@ -672,7 +672,7 @@ class DirectUploadController {
672
672
  }));
673
673
  }
674
674
  uploadRequestDidProgress(event) {
675
- const progress = event.loaded / event.total * 100;
675
+ const progress = event.loaded / event.total * 90;
676
676
  if (progress) {
677
677
  this.dispatch("progress", {
678
678
  progress: progress
@@ -707,6 +707,42 @@ class DirectUploadController {
707
707
  xhr: xhr
708
708
  });
709
709
  xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
710
+ xhr.upload.addEventListener("loadend", (() => {
711
+ this.simulateResponseProgress(xhr);
712
+ }));
713
+ }
714
+ simulateResponseProgress(xhr) {
715
+ let progress = 90;
716
+ const startTime = Date.now();
717
+ const updateProgress = () => {
718
+ const elapsed = Date.now() - startTime;
719
+ const estimatedResponseTime = this.estimateResponseTime();
720
+ const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
721
+ progress = 90 + responseProgress * 9;
722
+ this.dispatch("progress", {
723
+ progress: progress
724
+ });
725
+ if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
726
+ requestAnimationFrame(updateProgress);
727
+ }
728
+ };
729
+ xhr.addEventListener("loadend", (() => {
730
+ this.dispatch("progress", {
731
+ progress: 100
732
+ });
733
+ }));
734
+ requestAnimationFrame(updateProgress);
735
+ }
736
+ estimateResponseTime() {
737
+ const fileSize = this.file.size;
738
+ const MB = 1024 * 1024;
739
+ if (fileSize < MB) {
740
+ return 1e3;
741
+ } else if (fileSize < 10 * MB) {
742
+ return 2e3;
743
+ } else {
744
+ return 3e3 + fileSize / MB * 50;
745
+ }
710
746
  }
711
747
  }
712
748
 
@@ -662,7 +662,7 @@
662
662
  }));
663
663
  }
664
664
  uploadRequestDidProgress(event) {
665
- const progress = event.loaded / event.total * 100;
665
+ const progress = event.loaded / event.total * 90;
666
666
  if (progress) {
667
667
  this.dispatch("progress", {
668
668
  progress: progress
@@ -697,6 +697,42 @@
697
697
  xhr: xhr
698
698
  });
699
699
  xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
700
+ xhr.upload.addEventListener("loadend", (() => {
701
+ this.simulateResponseProgress(xhr);
702
+ }));
703
+ }
704
+ simulateResponseProgress(xhr) {
705
+ let progress = 90;
706
+ const startTime = Date.now();
707
+ const updateProgress = () => {
708
+ const elapsed = Date.now() - startTime;
709
+ const estimatedResponseTime = this.estimateResponseTime();
710
+ const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
711
+ progress = 90 + responseProgress * 9;
712
+ this.dispatch("progress", {
713
+ progress: progress
714
+ });
715
+ if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
716
+ requestAnimationFrame(updateProgress);
717
+ }
718
+ };
719
+ xhr.addEventListener("loadend", (() => {
720
+ this.dispatch("progress", {
721
+ progress: 100
722
+ });
723
+ }));
724
+ requestAnimationFrame(updateProgress);
725
+ }
726
+ estimateResponseTime() {
727
+ const fileSize = this.file.size;
728
+ const MB = 1024 * 1024;
729
+ if (fileSize < MB) {
730
+ return 1e3;
731
+ } else if (fileSize < 10 * MB) {
732
+ return 2e3;
733
+ } else {
734
+ return 3e3 + fileSize / MB * 50;
735
+ }
700
736
  }
701
737
  }
702
738
  const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
@@ -31,7 +31,8 @@ export class DirectUploadController {
31
31
  }
32
32
 
33
33
  uploadRequestDidProgress(event) {
34
- const progress = event.loaded / event.total * 100
34
+ // Scale upload progress to 0-90% range
35
+ const progress = (event.loaded / event.total) * 90
35
36
  if (progress) {
36
37
  this.dispatch("progress", { progress })
37
38
  }
@@ -63,5 +64,51 @@ export class DirectUploadController {
63
64
  directUploadWillStoreFileWithXHR(xhr) {
64
65
  this.dispatch("before-storage-request", { xhr })
65
66
  xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event))
67
+
68
+ // Start simulating progress after upload completes
69
+ xhr.upload.addEventListener("loadend", () => {
70
+ this.simulateResponseProgress(xhr)
71
+ })
72
+ }
73
+
74
+ simulateResponseProgress(xhr) {
75
+ let progress = 90
76
+ const startTime = Date.now()
77
+
78
+ const updateProgress = () => {
79
+ // Simulate progress from 90% to 99% over estimated time
80
+ const elapsed = Date.now() - startTime
81
+ const estimatedResponseTime = this.estimateResponseTime()
82
+ const responseProgress = Math.min(elapsed / estimatedResponseTime, 1)
83
+ progress = 90 + (responseProgress * 9) // 90% to 99%
84
+
85
+ this.dispatch("progress", { progress })
86
+
87
+ // Continue until response arrives or we hit 99%
88
+ if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
89
+ requestAnimationFrame(updateProgress)
90
+ }
91
+ }
92
+
93
+ // Stop simulation when response arrives
94
+ xhr.addEventListener("loadend", () => {
95
+ this.dispatch("progress", { progress: 100 })
96
+ })
97
+
98
+ requestAnimationFrame(updateProgress)
99
+ }
100
+
101
+ estimateResponseTime() {
102
+ // Base estimate: 1 second for small files, scaling up for larger files
103
+ const fileSize = this.file.size
104
+ const MB = 1024 * 1024
105
+
106
+ if (fileSize < MB) {
107
+ return 1000 // 1 second for files under 1MB
108
+ } else if (fileSize < 10 * MB) {
109
+ return 2000 // 2 seconds for files 1-10MB
110
+ } else {
111
+ return 3000 + (fileSize / MB * 50) // 3+ seconds for larger files
112
+ }
66
113
  }
67
114
  }
@@ -64,6 +64,7 @@ class ActiveStorage::Filename
64
64
  def to_s
65
65
  sanitized.to_s
66
66
  end
67
+ alias_method :to_str, :to_s
67
68
 
68
69
  def as_json(*)
69
70
  to_s
@@ -8,17 +8,17 @@
8
8
  #
9
9
  # Variants rely on {ImageProcessing}[https://github.com/janko/image_processing] gem for the actual transformations
10
10
  # of the file, so you must add <tt>gem "image_processing"</tt> to your Gemfile if you wish to use variants. By
11
- # default, images will be processed with {ImageMagick}[http://imagemagick.org] using the
12
- # {MiniMagick}[https://github.com/minimagick/minimagick] gem, but you can also switch to the
13
- # {libvips}[http://libvips.github.io/libvips/] processor operated by the {ruby-vips}[https://github.com/libvips/ruby-vips]
11
+ # default, images will be processed with {libvips}[http://libvips.github.io/libvips/] using the
12
+ # {ruby-vips}[https://github.com/libvips/ruby-vips] gem, but you can also switch to the
13
+ # {ImageMagick}[http://imagemagick.org] processor operated by the {MiniMagick}[https://github.com/minimagick/minimagick]
14
14
  # gem).
15
15
  #
16
16
  # Rails.application.config.active_storage.variant_processor
17
- # # => :mini_magick
18
- #
19
- # Rails.application.config.active_storage.variant_processor = :vips
20
17
  # # => :vips
21
18
  #
19
+ # Rails.application.config.active_storage.variant_processor = :mini_magick
20
+ # # => :mini_magick
21
+ #
22
22
  # Note that to create a variant it's necessary to download the entire blob file from the service. Because of this process,
23
23
  # you also want to be considerate about when the variant is actually processed. You shouldn't be processing variants inline
24
24
  # in a template, for example. Delay the processing to an on-demand controller, like the one provided in
@@ -82,6 +82,6 @@ class ActiveStorage::Variation
82
82
 
83
83
  private
84
84
  def transformer
85
- ActiveStorage::Transformers::ImageProcessingTransformer.new(transformations.except(:format))
85
+ ActiveStorage.variant_transformer.new(transformations.except(:format))
86
86
  end
87
87
  end
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ begin
4
+ gem "mini_magick"
5
+ require "mini_magick"
6
+ ActiveStorage::MINIMAGICK_AVAILABLE = true # :nodoc:
7
+ rescue LoadError => error
8
+ ActiveStorage::MINIMAGICK_AVAILABLE = false # :nodoc:
9
+ raise error unless error.message.include?("mini_magick")
10
+ end
11
+
3
12
  module ActiveStorage
4
13
  # This analyzer relies on the third-party {MiniMagick}[https://github.com/minimagick/minimagick] gem. MiniMagick requires
5
14
  # the {ImageMagick}[http://www.imagemagick.org] system library.
@@ -10,10 +19,8 @@ module ActiveStorage
10
19
 
11
20
  private
12
21
  def read_image
13
- begin
14
- require "mini_magick"
15
- rescue LoadError
16
- logger.info "Skipping image analysis because the mini_magick gem isn't installed"
22
+ unless MINIMAGICK_AVAILABLE
23
+ logger.error "Skipping image analysis because the mini_magick gem isn't installed"
17
24
  return {}
18
25
  end
19
26
 
@@ -1,5 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ begin
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
19
+
3
20
  module ActiveStorage
4
21
  # This analyzer relies on the third-party {ruby-vips}[https://github.com/libvips/ruby-vips] gem. Ruby-vips requires
5
22
  # the {libvips}[https://libvips.github.io/libvips/] system library.
@@ -10,10 +27,8 @@ module ActiveStorage
10
27
 
11
28
  private
12
29
  def read_image
13
- begin
14
- require "ruby-vips"
15
- rescue LoadError
16
- logger.info "Skipping image analysis because the ruby-vips gem isn't installed"
30
+ unless VIPS_AVAILABLE
31
+ logger.error "Skipping image analysis because the ruby-vips gem isn't installed"
17
32
  return {}
18
33
  end
19
34
 
@@ -35,6 +50,9 @@ module ActiveStorage
35
50
  logger.error "Skipping image analysis due to a Vips error: #{error.message}"
36
51
  {}
37
52
  end
53
+ rescue ::Vips::Error => error
54
+ logger.error "Skipping image analysis due to an Vips error: #{error.message}"
55
+ {}
38
56
  end
39
57
 
40
58
  ROTATIONS = /Right-top|Left-bottom|Top-right|Bottom-left/
@@ -12,6 +12,11 @@ module ActiveStorage
12
12
  # ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick.new(blob).metadata
13
13
  # # => { width: 4104, height: 2736 }
14
14
  class Analyzer::ImageAnalyzer < Analyzer
15
+ extend ActiveSupport::Autoload
16
+
17
+ autoload :Vips
18
+ autoload :ImageMagick
19
+
15
20
  def self.accept?(blob)
16
21
  blob.image?
17
22
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/module/delegation"
4
3
 
5
4
  module ActiveStorage
6
5
  # = Active Storage \Attached
@@ -12,8 +12,6 @@ require "active_storage/previewer/mupdf_previewer"
12
12
  require "active_storage/previewer/video_previewer"
13
13
 
14
14
  require "active_storage/analyzer/image_analyzer"
15
- require "active_storage/analyzer/image_analyzer/image_magick"
16
- require "active_storage/analyzer/image_analyzer/vips"
17
15
  require "active_storage/analyzer/video_analyzer"
18
16
  require "active_storage/analyzer/audio_analyzer"
19
17
 
@@ -93,6 +91,35 @@ module ActiveStorage
93
91
  ActiveStorage.variant_processor = app.config.active_storage.variant_processor || :mini_magick
94
92
  ActiveStorage.previewers = app.config.active_storage.previewers || []
95
93
  ActiveStorage.analyzers = app.config.active_storage.analyzers || []
94
+
95
+ begin
96
+ ActiveStorage.variant_transformer =
97
+ case ActiveStorage.variant_processor
98
+ when :disabled
99
+ ActiveStorage::Transformers::NullTransformer
100
+ when :vips
101
+ ActiveStorage::Transformers::Vips
102
+ when :mini_magick
103
+ ActiveStorage::Transformers::ImageMagick
104
+ end
105
+ rescue LoadError => error
106
+ case error.message
107
+ when /libvips/
108
+ ActiveStorage.logger.warn <<~WARNING.squish
109
+ Using vips to process variants requires the libvips library.
110
+ Please install libvips using the instructions on the libvips website.
111
+ WARNING
112
+ when /image_processing/
113
+ ActiveStorage.logger.warn <<~WARNING.squish
114
+ Generating image variants require the image_processing gem.
115
+ Please add `gem "image_processing", "~> 1.2"` to your Gemfile
116
+ or set `config.active_storage.variant_processor = :disabled`.
117
+ WARNING
118
+ else
119
+ raise
120
+ end
121
+ end
122
+
96
123
  ActiveStorage.paths = app.config.active_storage.paths || {}
97
124
  ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage"
98
125
  ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false
@@ -140,9 +167,9 @@ module ActiveStorage
140
167
  end
141
168
  end
142
169
 
143
- initializer "active_storage.services" do
170
+ initializer "active_storage.services" do |app|
144
171
  ActiveSupport.on_load(:active_storage_blob) do
145
- configs = Rails.configuration.active_storage.service_configurations ||=
172
+ configs = app.config.active_storage.service_configurations ||=
146
173
  begin
147
174
  config_file = Rails.root.join("config/storage/#{Rails.env}.yml")
148
175
  config_file = Rails.root.join("config/storage.yml") unless config_file.exist?
@@ -153,7 +180,7 @@ module ActiveStorage
153
180
 
154
181
  ActiveStorage::Blob.services = ActiveStorage::Service::Registry.new(configs)
155
182
 
156
- if config_choice = Rails.configuration.active_storage.service
183
+ if config_choice = app.config.active_storage.service
157
184
  ActiveStorage::Blob.service = ActiveStorage::Blob.services.fetch(config_choice)
158
185
  end
159
186
  end
@@ -175,7 +202,7 @@ module ActiveStorage
175
202
  initializer "action_view.configuration" do
176
203
  config.after_initialize do |app|
177
204
  ActiveSupport.on_load(:action_view) do
178
- multiple_file_field_include_hidden = app.config.active_storage.delete(:multiple_file_field_include_hidden)
205
+ multiple_file_field_include_hidden = app.config.active_storage.multiple_file_field_include_hidden
179
206
 
180
207
  unless multiple_file_field_include_hidden.nil?
181
208
  ActionView::Helpers::FormHelper.multiple_file_field_include_hidden = multiple_file_field_include_hidden
@@ -8,8 +8,8 @@ module ActiveStorage
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 8
11
- MINOR = 0
12
- TINY = 5
11
+ MINOR = 1
12
+ TINY = 3
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -3,7 +3,7 @@
3
3
  require "active_support/log_subscriber"
4
4
 
5
5
  module ActiveStorage
6
- class LogSubscriber < ActiveSupport::LogSubscriber
6
+ class LogSubscriber < ActiveSupport::LogSubscriber # :nodoc:
7
7
  def service_upload(event)
8
8
  message = "Uploaded file to key: #{key_in(event)}"
9
9
  message += " (checksum: #{event.payload[:checksum]})" if event.payload[:checksum]
@@ -19,6 +19,12 @@ module ActiveStorage
19
19
  )
20
20
  end
21
21
 
22
+ def inspect # :nodoc:
23
+ attrs = configurations.any? ?
24
+ " configurations=[#{configurations.keys.map(&:inspect).join(", ")}]" : ""
25
+ "#<#{self.class}#{attrs}>"
26
+ end
27
+
22
28
  private
23
29
  def config_for(name)
24
30
  configurations.fetch name do