activestorage 7.1.4 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7090b004185703ae6dc98db17916ed43277cc6e2f36cfe1ed9c80b6ac1571ab0
4
- data.tar.gz: 1d767b1c2a6c88390ac96ba2e17f6215c30487a87541c03780ba3e3feeb40a34
3
+ metadata.gz: da8d7f2cbc1f8d032c38af7720a92c07f024dc9ccbe0f67ff3daadd8dc2613e9
4
+ data.tar.gz: ed3bd8cc7f51aa26e1247ce9182ec0f1a81871b5e0c854ef7c974065f6d71134
5
5
  SHA512:
6
- metadata.gz: dde4aa29cd76824dfe0b3b177c20e2ae87d81a8e86d3ad2272ce5507de30a8fbfbb28bab0b81e7bd1845a0f9a0f80c17d5a1dbc951055f7e9c07f88d2bb3d9cf
7
- data.tar.gz: 1f21e247f8f191da8c81044e194acda189736ff9e4c19939833425f5d0659627988f8a2314cdf193c6f99ff5088d170f3c8b09e8b86459371b3721829ff7e3ae
6
+ metadata.gz: 19f6c085a1a778c4021f0c66dc2b20ce3062e1f56f05eb38887bb4907dac8272819b5a29ac31aaef924f4ea70ede606da6d29de87a9ea2a4c09b718cfc3e34c6
7
+ data.tar.gz: 1e0156a1428ed9d779ddfd0bf67222796b10ce969b5affad4432e543dcc37cad736c2d2c484457cf465ab257da700ca92a55679d716c88084f929f1387205d62
data/CHANGELOG.md CHANGED
@@ -1,31 +1,25 @@
1
- ## Rails 7.1.4 (August 22, 2024) ##
1
+ ## Rails 7.2.0 (August 09, 2024) ##
2
2
 
3
- * Fixes race condition for multiple preprocessed video variants.
3
+ * Remove deprecated `config.active_storage.silence_invalid_content_types_warning`.
4
4
 
5
- *Justin Searls*
6
-
7
-
8
- ## Rails 7.1.3.4 (June 04, 2024) ##
9
-
10
- * No changes.
11
-
12
-
13
- ## Rails 7.1.3.3 (May 16, 2024) ##
14
-
15
- * No changes.
5
+ *Rafael Mendonça França*
16
6
 
7
+ * Remove deprecated `config.active_storage.replace_on_assign_to_many`.
17
8
 
18
- ## Rails 7.1.3.2 (February 21, 2024) ##
9
+ *Rafael Mendonça França*
19
10
 
20
- * No changes.
11
+ * Add support for custom `key` in `ActiveStorage::Blob#compose`.
21
12
 
13
+ *Elvin Efendiev*
22
14
 
23
- ## Rails 7.1.3.1 (February 21, 2024) ##
15
+ * Add `image/webp` to `config.active_storage.web_image_content_types` when `load_defaults "7.2"`
16
+ is set.
24
17
 
25
- * No changes.
18
+ *Lewis Buckley*
26
19
 
20
+ * Fix JSON-encoding of `ActiveStorage::Filename` instances.
27
21
 
28
- ## Rails 7.1.3 (January 16, 2024) ##
22
+ *Jonathan del Strother*
29
23
 
30
24
  * Fix N+1 query when fetching preview images for non-image assets.
31
25
 
@@ -46,16 +40,20 @@
46
40
 
47
41
  *Chedli Bourguiba*
48
42
 
49
- * Fix direct upload forms when submit button contains nested elements.
50
-
51
- *Marc Köhlbrugge*
52
-
53
43
  * When using the `preprocessed: true` option, avoid enqueuing transform jobs
54
44
  for blobs that are not representable.
55
45
 
56
46
  *Chedli Bourguiba*
57
47
 
48
+ * Prevent `ActiveStorage::Blob#preview` to generate a variant if an empty variation is passed.
49
+
50
+ Calls to `#url`, `#key` or `#download` will now use the original preview
51
+ image instead of generating a variant with the exact same dimensions.
52
+
53
+ *Chedli Bourguiba*
54
+
58
55
  * Process preview image variant when calling `ActiveStorage::Preview#processed`.
56
+
59
57
  For example, `attached_pdf.preview(:thumb).processed` will now immediately
60
58
  generate the full-sized preview image and the `:thumb` variant of it.
61
59
  Previously, the `:thumb` variant would not be generated until a further call
@@ -73,266 +71,8 @@
73
71
 
74
72
  *Nico Wenterodt*
75
73
 
74
+ * Allow accepting `service` as a proc as well in `has_one_attached` and `has_many_attached`.
76
75
 
77
- ## Rails 7.1.2 (November 10, 2023) ##
78
-
79
- * No changes.
80
-
81
-
82
- ## Rails 7.1.1 (October 11, 2023) ##
83
-
84
- * No changes.
85
-
86
-
87
- ## Rails 7.1.0 (October 05, 2023) ##
88
-
89
- * No changes.
90
-
91
-
92
- ## Rails 7.1.0.rc2 (October 01, 2023) ##
93
-
94
- * No changes.
95
-
96
-
97
- ## Rails 7.1.0.rc1 (September 27, 2023) ##
98
-
99
- * Add `expires_at` option to `ActiveStorage::Blob#signed_id`.
100
-
101
- ```ruby
102
- rails_blob_path(user.avatar, disposition: "attachment", expires_at: 30.minutes.from_now)
103
- <%= image_tag rails_blob_path(user.avatar.variant(resize: "100x100"), expires_at: 30.minutes.from_now) %>
104
- ```
105
-
106
- *Aki*
107
-
108
- * Allow attaching File and Pathname when assigning attributes, e.g.
109
-
110
- ```ruby
111
- User.create!(avatar: File.open("image.jpg"))
112
- User.create!(avatar: file_fixture("image.jpg"))
113
- ```
114
-
115
- *Dorian Marié*
116
-
117
-
118
- ## Rails 7.1.0.beta1 (September 13, 2023) ##
119
-
120
- * Disables the session in `ActiveStorage::Blobs::ProxyController`
121
- and `ActiveStorage::Representations::ProxyController`
122
- in order to allow caching by default in some CDNs as CloudFlare
123
-
124
- Fixes #44136
125
-
126
- *Bruno Prieto*
127
-
128
- * Add `tags` to `ActiveStorage::Analyzer::AudioAnalyzer` output
129
-
130
- *Keaton Roux*
131
-
132
- * Add an option to preprocess variants
133
-
134
- ActiveStorage variants are processed on the fly when they are needed but
135
- sometimes we're sure that they are accessed and want to processed them
136
- upfront.
137
-
138
- `preprocessed` option is added when declaring variants.
139
-
140
- ```
141
- class User < ApplicationRecord
142
- has_one_attached :avatar do |attachable|
143
- attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
144
- end
145
- end
146
- ```
147
-
148
- *Shouichi Kamiya*
149
-
150
- * Fix variants not included when eager loading multiple records containing a single attachment
151
-
152
- When using the `with_attached_#{name}` scope for a `has_one_attached` relation,
153
- attachment variants were not eagerly loaded.
154
-
155
- *Russell Porter*
156
-
157
- * Allow an ActiveStorage attachment to be removed via a form post
158
-
159
- Attachments can already be removed by updating the attachment to be nil such as:
160
- ```ruby
161
- User.find(params[:id]).update!(avatar: nil)
162
- ```
163
-
164
- However, a form cannot post a nil param, it can only post an empty string. But, posting an
165
- empty string would result in an `ActiveSupport::MessageVerifier::InvalidSignature: mismatched digest`
166
- error being raised, because it's being treated as a signed blob id.
167
-
168
- Now, nil and an empty string are treated as a delete, which allows attachments to be removed via:
169
- ```ruby
170
- User.find(params[:id]).update!(params.require(:user).permit(:avatar))
171
-
172
- ```
173
-
174
- *Nate Matykiewicz*
175
-
176
- * Remove mini_mime usage in favour of marcel.
177
-
178
- We have two libraries that are have similar usage. This change removes
179
- dependency on mini_mime and makes use of similar methods from marcel.
180
-
181
- *Vipul A M*
182
-
183
- * Allow destroying active storage variants
184
-
185
- ```ruby
186
- User.first.avatar.variant(resize_to_limit: [100, 100]).destroy
187
- ```
188
-
189
- *Shouichi Kamiya*, *Yuichiro NAKAGAWA*, *Ryohei UEDA*
190
-
191
- * Add `sample_rate` to `ActiveStorage::Analyzer::AudioAnalyzer` output
192
-
193
- *Matija Čupić*
194
-
195
- * Remove deprecated `purge` and `purge_later` methods from the attachments association.
196
-
197
- *Rafael Mendonça França*
198
-
199
- * Remove deprecated behavior when assigning to a collection of attachments.
200
-
201
- Instead of appending to the collection, the collection is now replaced.
202
-
203
- *Rafael Mendonça França*
204
-
205
- * Remove deprecated `ActiveStorage::Current#host` and `ActiveStorage::Current#host=` methods.
206
-
207
- *Rafael Mendonça França*
208
-
209
- * Remove deprecated invalid default content types in Active Storage configurations.
210
-
211
- *Rafael Mendonça França*
212
-
213
- * Add missing preview event to `ActiveStorage::LogSubscriber`
214
-
215
- A `preview` event is being instrumented in `ActiveStorage::Previewer`.
216
- However it was not added inside ActiveStorage's LogSubscriber class.
217
-
218
- This will allow to have logs for when a preview happens
219
- in the same fashion as all other ActiveStorage events such as
220
- `upload` and `download` inside `Rails.logger`.
221
-
222
- *Chedli Bourguiba*
223
-
224
- * Fix retrieving rotation value from FFmpeg on version 5.0+.
225
-
226
- In FFmpeg version 5.0+ the rotation value has been removed from tags.
227
- Instead the value can be found in side_data_list. Along with
228
- this update it's possible to have values of -90, -270 to denote the video
229
- has been rotated.
230
-
231
- *Haroon Ahmed*
232
-
233
- * Touch all corresponding model records after ActiveStorage::Blob is analyzed
234
-
235
- This fixes a race condition where a record can be requested and have a cache entry built, before
236
- the initial `analyze_later` completes, which will not be invalidated until something else
237
- updates the record. This also invalidates cache entries when a blob is re-analyzed, which
238
- is helpful if a bug is fixed in an analyzer or a new analyzer is added.
239
-
240
- *Nate Matykiewicz*
241
-
242
- * Add ability to use pre-defined variants when calling `preview` or
243
- `representation` on an attachment.
244
-
245
- ```ruby
246
- class User < ActiveRecord::Base
247
- has_one_attached :file do |attachable|
248
- attachable.variant :thumb, resize_to_limit: [100, 100]
249
- end
250
- end
251
-
252
- <%= image_tag user.file.representation(:thumb) %>
253
- ```
254
-
255
- *Richard Böhme*
256
-
257
- * Method `attach` always returns the attachments except when the record
258
- is persisted, unchanged, and saving it fails, in which case it returns `nil`.
259
-
260
- *Santiago Bartesaghi*
261
-
262
- * Fixes multiple `attach` calls within transaction not uploading files correctly.
263
-
264
- In the following example, the code failed to upload all but the last file to the configured service.
265
- ```ruby
266
- ActiveRecord::Base.transaction do
267
- user.attachments.attach({
268
- content_type: "text/plain",
269
- filename: "dummy.txt",
270
- io: ::StringIO.new("dummy"),
271
- })
272
- user.attachments.attach({
273
- content_type: "text/plain",
274
- filename: "dummy2.txt",
275
- io: ::StringIO.new("dummy2"),
276
- })
277
- end
278
-
279
- assert_equal 2, user.attachments.count
280
- assert user.attachments.first.service.exist?(user.attachments.first.key) # Fails
281
- ```
282
-
283
- This was addressed by keeping track of the subchanges pending upload, and uploading them
284
- once the transaction is committed.
285
-
286
- Fixes #41661
287
-
288
- *Santiago Bartesaghi*, *Bruno Vezoli*, *Juan Roig*, *Abhay Nikam*
289
-
290
- * Raise an exception if `config.active_storage.service` is not set.
291
-
292
- If Active Storage is configured and `config.active_storage.service` is not
293
- set in the respective environment's configuration file, then an exception
294
- is raised with a meaningful message when attempting to use Active Storage.
295
-
296
- *Ghouse Mohamed*
297
-
298
- * Fixes proxy downloads of files over 5mb
299
-
300
- Previously, trying to view and/or download files larger than 5mb stored in
301
- services like S3 via proxy mode could return corrupted files at around
302
- 5.2mb or cause random halts in the download. Now,
303
- `ActiveStorage::Blobs::ProxyController` correctly handles streaming these
304
- larger files from the service to the client without any issues.
305
-
306
- Fixes #44679
307
-
308
- *Felipe Raul*
309
-
310
- * Saving attachment(s) to a record returns the blob/blobs object
311
-
312
- Previously, saving attachments did not return the blob/blobs that
313
- were attached. Now, saving attachments to a record with `#attach`
314
- method returns the blob or array of blobs that were attached to
315
- the record. If it fails to save the attachment(s), then it returns
316
- `false`.
317
-
318
- *Ghouse Mohamed*
319
-
320
- * Don't stream responses in redirect mode
321
-
322
- Previously, both redirect mode and proxy mode streamed their
323
- responses which caused a new thread to be created, and could end
324
- up leaking connections in the connection pool. But since redirect
325
- mode doesn't actually send any data, it doesn't need to be
326
- streamed.
327
-
328
- *Luke Lau*
329
-
330
- * Safe for direct upload on Libraries or Frameworks
331
-
332
- Enable the use of custom headers during direct uploads, which allows for
333
- the inclusion of Authorization bearer tokens or other forms of authorization
334
- tokens through headers.
335
-
336
- *Radamés Roriz*
76
+ *Yogesh Khater*
337
77
 
338
- Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activestorage/CHANGELOG.md) for previous changes.
78
+ Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activestorage/CHANGELOG.md) for previous changes.
@@ -22,13 +22,13 @@ class ActiveStorage::Attachment < ActiveStorage::Record
22
22
  # :method:
23
23
  #
24
24
  # Returns the associated record.
25
- belongs_to :record, polymorphic: true, touch: true
25
+ belongs_to :record, polymorphic: true, touch: ActiveStorage.touch_attachment_records
26
26
 
27
27
  ##
28
28
  # :method:
29
29
  #
30
30
  # Returns the associated ActiveStorage::Blob.
31
- belongs_to :blob, class_name: "ActiveStorage::Blob", autosave: true
31
+ belongs_to :blob, class_name: "ActiveStorage::Blob", autosave: true, inverse_of: :attachments
32
32
 
33
33
  delegate_missing_to :blob
34
34
  delegate :signed_id, to: :blob
@@ -138,7 +138,7 @@ class ActiveStorage::Attachment < ActiveStorage::Record
138
138
  end
139
139
  }
140
140
 
141
- if blob.preview_image_needed_before_processing_variants?
141
+ if blob.preview_image_needed_before_processing_variants? && preprocessed_variations.any?
142
142
  blob.create_preview_image_later(preprocessed_variations)
143
143
  else
144
144
  preprocessed_variations.each do |transformations|
@@ -156,7 +156,7 @@ class ActiveStorage::Attachment < ActiveStorage::Record
156
156
  end
157
157
 
158
158
  def named_variants
159
- record.attachment_reflections[name]&.named_variants
159
+ record.attachment_reflections[name]&.named_variants || {}
160
160
  end
161
161
 
162
162
  def transformations_by_name(transformations)
@@ -17,11 +17,6 @@
17
17
  # update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file.
18
18
  # If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one.
19
19
  class ActiveStorage::Blob < ActiveStorage::Record
20
- include Analyzable
21
- include Identifiable
22
- include Representable
23
- include Servable
24
-
25
20
  MINIMUM_TOKEN_LENGTH = 28
26
21
 
27
22
  has_secure_token :key, length: MINIMUM_TOKEN_LENGTH
@@ -46,7 +41,7 @@ class ActiveStorage::Blob < ActiveStorage::Record
46
41
  self.service_name ||= self.class.service&.name
47
42
  end
48
43
 
49
- after_update :touch_attachment_records
44
+ after_update :touch_attachments
50
45
 
51
46
  after_update_commit :update_service_metadata, if: -> { content_type_previously_changed? || metadata_previously_changed? }
52
47
 
@@ -147,18 +142,39 @@ class ActiveStorage::Blob < ActiveStorage::Record
147
142
  end
148
143
 
149
144
  # Concatenate multiple blobs into a single "composed" blob.
150
- def compose(blobs, filename:, content_type: nil, metadata: nil)
145
+ def compose(blobs, key: nil, filename:, content_type: nil, metadata: nil)
151
146
  raise ActiveRecord::RecordNotSaved, "All blobs must be persisted." if blobs.any?(&:new_record?)
152
147
 
153
148
  content_type ||= blobs.pluck(:content_type).compact.first
154
149
 
155
- new(filename: filename, content_type: content_type, metadata: metadata, byte_size: blobs.sum(&:byte_size)).tap do |combined_blob|
150
+ new(key: key, filename: filename, content_type: content_type, metadata: metadata, byte_size: blobs.sum(&:byte_size)).tap do |combined_blob|
156
151
  combined_blob.compose(blobs.pluck(:key))
157
152
  combined_blob.save!
158
153
  end
159
154
  end
155
+
156
+ def validate_service_configuration(service_name, model_class, association_name) # :nodoc:
157
+ if service_name
158
+ services.fetch(service_name) do
159
+ raise ArgumentError, "Cannot configure service #{service_name.inspect} for #{model_class}##{association_name}"
160
+ end
161
+ else
162
+ validate_global_service_configuration
163
+ end
164
+ end
165
+
166
+ def validate_global_service_configuration # :nodoc:
167
+ if connected? && table_exists? && Rails.configuration.active_storage.service.nil?
168
+ raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
169
+ end
170
+ end
160
171
  end
161
172
 
173
+ include Analyzable
174
+ include Identifiable
175
+ include Representable
176
+ include Servable
177
+
162
178
  # Returns a signed ID for this blob that's suitable for reference on the client-side without fear of tampering.
163
179
  def signed_id(purpose: :blob_id, expires_in: nil, expires_at: nil)
164
180
  super
@@ -334,8 +350,9 @@ class ActiveStorage::Blob < ActiveStorage::Record
334
350
  raise ArgumentError, "io must be rewindable" unless io.respond_to?(:rewind)
335
351
 
336
352
  OpenSSL::Digest::MD5.new.tap do |checksum|
337
- while chunk = io.read(5.megabytes)
338
- checksum << chunk
353
+ read_buffer = "".b
354
+ while io.read(5.megabytes, read_buffer)
355
+ checksum << read_buffer
339
356
  end
340
357
 
341
358
  io.rewind
@@ -360,8 +377,14 @@ class ActiveStorage::Blob < ActiveStorage::Record
360
377
  end
361
378
  end
362
379
 
363
- def touch_attachment_records
364
- attachments.includes(:record).each do |attachment|
380
+ def touch_attachments
381
+ attachments.then do |relation|
382
+ if ActiveStorage.touch_attachment_records
383
+ relation.includes(:record)
384
+ else
385
+ relation
386
+ end
387
+ end.each do |attachment|
365
388
  attachment.touch
366
389
  end
367
390
  end
@@ -69,10 +69,6 @@ class ActiveStorage::Filename
69
69
  to_s
70
70
  end
71
71
 
72
- def to_json
73
- to_s
74
- end
75
-
76
72
  def <=>(other)
77
73
  to_s.downcase <=> other.to_s.downcase
78
74
  end
@@ -35,7 +35,7 @@ class ActiveStorage::Preview
35
35
 
36
36
  class UnprocessedError < StandardError; end
37
37
 
38
- delegate :filename, :content_type, to: :variant
38
+ delegate :filename, :content_type, to: :presentation
39
39
 
40
40
  attr_reader :blob, :variation
41
41
 
@@ -51,7 +51,7 @@ class ActiveStorage::Preview
51
51
  # image is stored with the blob, it is only generated once.
52
52
  def processed
53
53
  process unless processed?
54
- variant.processed
54
+ variant.processed if variant?
55
55
  self
56
56
  end
57
57
 
@@ -67,7 +67,7 @@ class ActiveStorage::Preview
67
67
  # a stable URL that redirects to the URL returned by this method.
68
68
  def url(**options)
69
69
  if processed?
70
- variant.url(**options)
70
+ presentation.url(**options)
71
71
  else
72
72
  raise UnprocessedError
73
73
  end
@@ -76,7 +76,7 @@ class ActiveStorage::Preview
76
76
  # Returns a combination key of the blob and the variation that together identifies a specific variant.
77
77
  def key
78
78
  if processed?
79
- variant.key
79
+ presentation.key
80
80
  else
81
81
  raise UnprocessedError
82
82
  end
@@ -89,7 +89,7 @@ class ActiveStorage::Preview
89
89
  # if the preview has not been processed yet.
90
90
  def download(&block)
91
91
  if processed?
92
- variant.download(&block)
92
+ presentation.download(&block)
93
93
  else
94
94
  raise UnprocessedError
95
95
  end
@@ -109,7 +109,15 @@ class ActiveStorage::Preview
109
109
  end
110
110
 
111
111
  def variant
112
- image.variant(variation).processed
112
+ image.variant(variation)
113
+ end
114
+
115
+ def variant?
116
+ variation.transformations.present?
117
+ end
118
+
119
+ def presentation
120
+ variant? ? variant.processed : image
113
121
  end
114
122
 
115
123
 
@@ -51,6 +51,6 @@ class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
51
51
  setting = config.options[config.orm][:primary_key_type]
52
52
  primary_key_type = setting || :primary_key
53
53
  foreign_key_type = setting || :bigint
54
- [primary_key_type, foreign_key_type]
54
+ [ primary_key_type, foreign_key_type ]
55
55
  end
56
56
  end
@@ -19,13 +19,16 @@ module ActiveStorage
19
19
 
20
20
  download_blob_to_tempfile do |file|
21
21
  image = instrument("vips") do
22
+ # ruby-vips will raise Vips::Error if it can't find an appropriate loader for the file
22
23
  ::Vips::Image.new_from_file(file.path, access: :sequential)
24
+ rescue ::Vips::Error
25
+ logger.info "Skipping image analysis because Vips doesn't support the file"
26
+ nil
23
27
  end
24
28
 
25
- if valid_image?(image)
29
+ if image
26
30
  yield image
27
31
  else
28
- logger.info "Skipping image analysis because Vips doesn't support the file"
29
32
  {}
30
33
  end
31
34
  rescue ::Vips::Error => error
@@ -40,12 +43,5 @@ module ActiveStorage
40
43
  rescue ::Vips::Error
41
44
  false
42
45
  end
43
-
44
- def valid_image?(image)
45
- image.avg
46
- true
47
- rescue ::Vips::Error
48
- false
49
- end
50
46
  end
51
47
  end
@@ -55,11 +55,15 @@ module ActiveStorage
55
55
  def angle
56
56
  if tags["rotate"]
57
57
  Integer(tags["rotate"])
58
- elsif side_data && side_data[0] && side_data[0]["rotation"]
59
- Integer(side_data[0]["rotation"])
58
+ elsif display_matrix && display_matrix["rotation"]
59
+ Integer(display_matrix["rotation"])
60
60
  end
61
61
  end
62
62
 
63
+ def display_matrix
64
+ side_data.detect { |data| data["side_data_type"] == "Display Matrix" }
65
+ end
66
+
63
67
  def display_aspect_ratio
64
68
  if descriptor = video_stream["display_aspect_ratio"]
65
69
  if terms = descriptor.split(":", 2)
@@ -118,7 +118,12 @@ module ActiveStorage
118
118
  end
119
119
 
120
120
  def attachment_service_name
121
- record.attachment_reflections[name].options[:service_name]
121
+ service_name = record.attachment_reflections[name].options[:service_name]
122
+ if service_name.is_a?(Proc)
123
+ service_name = service_name.call(record)
124
+ ActiveStorage::Blob.validate_service_configuration(service_name, record.class, name)
125
+ end
126
+ service_name
122
127
  end
123
128
  end
124
129
  end
@@ -4,7 +4,11 @@ module ActiveStorage
4
4
  class Attached::Changes::CreateOneOfMany < Attached::Changes::CreateOne # :nodoc:
5
5
  private
6
6
  def find_attachment
7
- record.public_send("#{name}_attachments").detect { |attachment| attachment.blob_id == blob.id }
7
+ if blob.persisted?
8
+ record.public_send("#{name}_attachments").detect { |attachment| attachment.blob_id == blob.id }
9
+ else
10
+ blob.attachments.find { |attachment| attachment.record == record }
11
+ end
8
12
  end
9
13
  end
10
14
  end
@@ -80,12 +80,18 @@ module ActiveStorage
80
80
  # +purge+ instead.
81
81
  #
82
82
  # If you need the attachment to use a service which differs from the globally configured one,
83
- # pass the +:service+ option. For instance:
83
+ # pass the +:service+ option. For example:
84
84
  #
85
85
  # class User < ActiveRecord::Base
86
86
  # has_one_attached :avatar, service: :s3
87
87
  # end
88
88
  #
89
+ # +:service+ can also be specified as a proc, and it will be called with the model instance:
90
+ #
91
+ # class User < ActiveRecord::Base
92
+ # has_one_attached :avatar, service: ->(user) { user.in_europe_region? ? :s3_europe : :s3_usa }
93
+ # end
94
+ #
89
95
  # If you need to enable +strict_loading+ to prevent lazy loading of attachment,
90
96
  # pass the +:strict_loading+ option. You can do:
91
97
  #
@@ -93,8 +99,12 @@ module ActiveStorage
93
99
  # has_one_attached :avatar, strict_loading: true
94
100
  # end
95
101
  #
102
+ # Note: Active Storage relies on polymorphic associations, which in turn store class names in the database.
103
+ # When renaming classes that use <tt>has_one_attached</tt>, make sure to also update the class names in the
104
+ # <tt>active_storage_attachments.record_type</tt> polymorphic type column of
105
+ # the corresponding rows.
96
106
  def has_one_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
97
- validate_service_configuration(name, service)
107
+ ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
98
108
 
99
109
  generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
100
110
  # frozen_string_literal: true
@@ -170,12 +180,18 @@ module ActiveStorage
170
180
  # +purge+ instead.
171
181
  #
172
182
  # If you need the attachment to use a service which differs from the globally configured one,
173
- # pass the +:service+ option. For instance:
183
+ # pass the +:service+ option. For example:
174
184
  #
175
185
  # class Gallery < ActiveRecord::Base
176
186
  # has_many_attached :photos, service: :s3
177
187
  # end
178
188
  #
189
+ # +:service+ can also be specified as a proc, and it will be called with the model instance:
190
+ #
191
+ # class Gallery < ActiveRecord::Base
192
+ # has_many_attached :photos, service: ->(gallery) { gallery.personal? ? :personal_s3 : :s3 }
193
+ # end
194
+ #
179
195
  # If you need to enable +strict_loading+ to prevent lazy loading of attachments,
180
196
  # pass the +:strict_loading+ option. You can do:
181
197
  #
@@ -183,8 +199,12 @@ module ActiveStorage
183
199
  # has_many_attached :photos, strict_loading: true
184
200
  # end
185
201
  #
202
+ # Note: Active Storage relies on polymorphic associations, which in turn store class names in the database.
203
+ # When renaming classes that use <tt>has_many</tt>, make sure to also update the class names in the
204
+ # <tt>active_storage_attachments.record_type</tt> polymorphic type column of
205
+ # the corresponding rows.
186
206
  def has_many_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
187
- validate_service_configuration(name, service)
207
+ ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
188
208
 
189
209
  generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
190
210
  # frozen_string_literal: true
@@ -233,23 +253,6 @@ module ActiveStorage
233
253
  yield reflection if block_given?
234
254
  ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
235
255
  end
236
-
237
- private
238
- def validate_service_configuration(association_name, service)
239
- if service.present?
240
- ActiveStorage::Blob.services.fetch(service) do
241
- raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
242
- end
243
- else
244
- validate_global_service_configuration
245
- end
246
- end
247
-
248
- def validate_global_service_configuration
249
- if connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
250
- raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
251
- end
252
- end
253
256
  end
254
257
 
255
258
  def attachment_changes # :nodoc:
@@ -65,6 +65,8 @@ module ActiveStorage
65
65
  )
66
66
 
67
67
  config.active_storage.content_types_allowed_inline = %w(
68
+ image/webp
69
+ image/avif
68
70
  image/png
69
71
  image/gif
70
72
  image/jpeg
@@ -110,6 +112,7 @@ module ActiveStorage
110
112
  ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
111
113
  ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
112
114
  ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
115
+ ActiveStorage.touch_attachment_records = app.config.active_storage.touch_attachment_records != false
113
116
  ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
114
117
  ActiveStorage.urls_expire_in = app.config.active_storage.urls_expire_in
115
118
  ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
@@ -8,8 +8,8 @@ module ActiveStorage
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 1
12
- TINY = 4
11
+ MINOR = 2
12
+ TINY = 0
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -4,7 +4,11 @@ module ActiveStorage
4
4
  class Previewer::MuPDFPreviewer < Previewer
5
5
  class << self
6
6
  def accept?(blob)
7
- blob.content_type == "application/pdf" && mutool_exists?
7
+ pdf?(blob.content_type) && mutool_exists?
8
+ end
9
+
10
+ def pdf?(content_type)
11
+ Marcel::Magic.child? content_type, "application/pdf"
8
12
  end
9
13
 
10
14
  def mutool_path
@@ -12,7 +16,7 @@ module ActiveStorage
12
16
  end
13
17
 
14
18
  def mutool_exists?
15
- return @mutool_exists if defined?(@mutool_exists) && !@mutool_exists.nil?
19
+ return @mutool_exists unless @mutool_exists.nil?
16
20
 
17
21
  system mutool_path, out: File::NULL, err: File::NULL
18
22
 
@@ -4,7 +4,11 @@ module ActiveStorage
4
4
  class Previewer::PopplerPDFPreviewer < Previewer
5
5
  class << self
6
6
  def accept?(blob)
7
- blob.content_type == "application/pdf" && pdftoppm_exists?
7
+ pdf?(blob.content_type) && pdftoppm_exists?
8
+ end
9
+
10
+ def pdf?(content_type)
11
+ Marcel::Magic.child? content_type, "application/pdf"
8
12
  end
9
13
 
10
14
  def pdftoppm_path
@@ -12,7 +16,7 @@ module ActiveStorage
12
16
  end
13
17
 
14
18
  def pdftoppm_exists?
15
- return @pdftoppm_exists if defined?(@pdftoppm_exists)
19
+ return @pdftoppm_exists unless @pdftoppm_exists.nil?
16
20
 
17
21
  @pdftoppm_exists = system(pdftoppm_path, "-v", out: File::NULL, err: File::NULL)
18
22
  end
@@ -10,7 +10,7 @@ module ActiveStorage
10
10
  end
11
11
 
12
12
  def ffmpeg_exists?
13
- return @ffmpeg_exists if defined?(@ffmpeg_exists)
13
+ return @ffmpeg_exists unless @ffmpeg_exists.nil?
14
14
 
15
15
  @ffmpeg_exists = system(ffmpeg_path, "-version", out: File::NULL, err: File::NULL)
16
16
  end
@@ -5,7 +5,7 @@ begin
5
5
  rescue LoadError
6
6
  raise LoadError, <<~ERROR.squish
7
7
  Generating image variants require the image_processing gem.
8
- Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
8
+ Please add `gem "image_processing", "~> 1.2"` to your Gemfile.
9
9
  ERROR
10
10
  end
11
11
 
@@ -354,6 +354,7 @@ module ActiveStorage
354
354
  mattr_accessor :unsupported_image_processing_arguments
355
355
 
356
356
  mattr_accessor :service_urls_expire_in, default: 5.minutes
357
+ mattr_accessor :touch_attachment_records, default: true
357
358
  mattr_accessor :urls_expire_in
358
359
 
359
360
  mattr_accessor :routes_prefix, default: "/rails/active_storage"
@@ -364,22 +365,6 @@ module ActiveStorage
364
365
 
365
366
  mattr_accessor :video_preview_arguments, default: "-y -vframes 1 -f image2"
366
367
 
367
- def self.replace_on_assign_to_many
368
- ActiveStorage.deprecator.warn("config.active_storage.replace_on_assign_to_many is deprecated and has no effect.")
369
- end
370
-
371
- def self.replace_on_assign_to_many=(value)
372
- ActiveStorage.deprecator.warn("config.active_storage.replace_on_assign_to_many is deprecated and has no effect.")
373
- end
374
-
375
- def self.silence_invalid_content_types_warning
376
- ActiveStorage.deprecator.warn("config.active_storage.silence_invalid_content_types_warning is deprecated and has no effect.")
377
- end
378
-
379
- def self.silence_invalid_content_types_warning=(value)
380
- ActiveStorage.deprecator.warn("config.active_storage.silence_invalid_content_types_warning is deprecated and has no effect.")
381
- end
382
-
383
368
  module Transformers
384
369
  extend ActiveSupport::Autoload
385
370
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activestorage
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.4
4
+ version: 7.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-22 00:00:00.000000000 Z
11
+ date: 2024-08-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.4
19
+ version: 7.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 7.1.4
26
+ version: 7.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: actionpack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 7.1.4
33
+ version: 7.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 7.1.4
40
+ version: 7.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: activejob
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 7.1.4
47
+ version: 7.2.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 7.1.4
54
+ version: 7.2.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: activerecord
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 7.1.4
61
+ version: 7.2.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: 7.1.4
68
+ version: 7.2.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: marcel
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -190,10 +190,10 @@ licenses:
190
190
  - MIT
191
191
  metadata:
192
192
  bug_tracker_uri: https://github.com/rails/rails/issues
193
- changelog_uri: https://github.com/rails/rails/blob/v7.1.4/activestorage/CHANGELOG.md
194
- documentation_uri: https://api.rubyonrails.org/v7.1.4/
193
+ changelog_uri: https://github.com/rails/rails/blob/v7.2.0/activestorage/CHANGELOG.md
194
+ documentation_uri: https://api.rubyonrails.org/v7.2.0/
195
195
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
196
- source_code_uri: https://github.com/rails/rails/tree/v7.1.4/activestorage
196
+ source_code_uri: https://github.com/rails/rails/tree/v7.2.0/activestorage
197
197
  rubygems_mfa_required: 'true'
198
198
  post_install_message:
199
199
  rdoc_options: []
@@ -203,7 +203,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
203
203
  requirements:
204
204
  - - ">="
205
205
  - !ruby/object:Gem::Version
206
- version: 2.7.0
206
+ version: 3.1.0
207
207
  required_rubygems_version: !ruby/object:Gem::Requirement
208
208
  requirements:
209
209
  - - ">="