activestorage 7.1.3.4 → 7.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -276
- data/app/jobs/active_storage/preview_image_job.rb +16 -0
- data/app/models/active_storage/attachment.rb +15 -5
- data/app/models/active_storage/blob/representable.rb +8 -0
- data/app/models/active_storage/blob.rb +35 -12
- data/app/models/active_storage/filename.rb +0 -4
- data/app/models/active_storage/preview.rb +14 -6
- data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
- data/lib/active_storage/analyzer/image_analyzer/vips.rb +5 -9
- data/lib/active_storage/analyzer/video_analyzer.rb +6 -2
- data/lib/active_storage/attached/changes/create_one.rb +6 -1
- data/lib/active_storage/attached/changes/create_one_of_many.rb +5 -1
- data/lib/active_storage/attached/model.rb +32 -25
- data/lib/active_storage/engine.rb +3 -0
- data/lib/active_storage/gem_version.rb +3 -3
- data/lib/active_storage/previewer/mupdf_previewer.rb +6 -2
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +6 -2
- data/lib/active_storage/previewer/video_previewer.rb +1 -1
- data/lib/active_storage/transformers/image_processing_transformer.rb +1 -1
- data/lib/active_storage.rb +2 -17
- metadata +16 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da8d7f2cbc1f8d032c38af7720a92c07f024dc9ccbe0f67ff3daadd8dc2613e9
|
4
|
+
data.tar.gz: ed3bd8cc7f51aa26e1247ce9182ec0f1a81871b5e0c854ef7c974065f6d71134
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19f6c085a1a778c4021f0c66dc2b20ce3062e1f56f05eb38887bb4907dac8272819b5a29ac31aaef924f4ea70ede606da6d29de87a9ea2a4c09b718cfc3e34c6
|
7
|
+
data.tar.gz: 1e0156a1428ed9d779ddfd0bf67222796b10ce969b5affad4432e543dcc37cad736c2d2c484457cf465ab257da700ca92a55679d716c88084f929f1387205d62
|
data/CHANGELOG.md
CHANGED
@@ -1,24 +1,25 @@
|
|
1
|
-
## Rails 7.
|
1
|
+
## Rails 7.2.0 (August 09, 2024) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Remove deprecated `config.active_storage.silence_invalid_content_types_warning`.
|
4
4
|
|
5
|
+
*Rafael Mendonça França*
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
* No changes.
|
9
|
-
|
7
|
+
* Remove deprecated `config.active_storage.replace_on_assign_to_many`.
|
10
8
|
|
11
|
-
|
9
|
+
*Rafael Mendonça França*
|
12
10
|
|
13
|
-
*
|
11
|
+
* Add support for custom `key` in `ActiveStorage::Blob#compose`.
|
14
12
|
|
13
|
+
*Elvin Efendiev*
|
15
14
|
|
16
|
-
|
15
|
+
* Add `image/webp` to `config.active_storage.web_image_content_types` when `load_defaults "7.2"`
|
16
|
+
is set.
|
17
17
|
|
18
|
-
*
|
18
|
+
*Lewis Buckley*
|
19
19
|
|
20
|
+
* Fix JSON-encoding of `ActiveStorage::Filename` instances.
|
20
21
|
|
21
|
-
|
22
|
+
*Jonathan del Strother*
|
22
23
|
|
23
24
|
* Fix N+1 query when fetching preview images for non-image assets.
|
24
25
|
|
@@ -39,16 +40,20 @@
|
|
39
40
|
|
40
41
|
*Chedli Bourguiba*
|
41
42
|
|
42
|
-
* Fix direct upload forms when submit button contains nested elements.
|
43
|
-
|
44
|
-
*Marc Köhlbrugge*
|
45
|
-
|
46
43
|
* When using the `preprocessed: true` option, avoid enqueuing transform jobs
|
47
44
|
for blobs that are not representable.
|
48
45
|
|
49
46
|
*Chedli Bourguiba*
|
50
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
|
+
|
51
55
|
* Process preview image variant when calling `ActiveStorage::Preview#processed`.
|
56
|
+
|
52
57
|
For example, `attached_pdf.preview(:thumb).processed` will now immediately
|
53
58
|
generate the full-sized preview image and the `:thumb` variant of it.
|
54
59
|
Previously, the `:thumb` variant would not be generated until a further call
|
@@ -66,266 +71,8 @@
|
|
66
71
|
|
67
72
|
*Nico Wenterodt*
|
68
73
|
|
74
|
+
* Allow accepting `service` as a proc as well in `has_one_attached` and `has_many_attached`.
|
69
75
|
|
70
|
-
|
71
|
-
|
72
|
-
* No changes.
|
73
|
-
|
74
|
-
|
75
|
-
## Rails 7.1.1 (October 11, 2023) ##
|
76
|
-
|
77
|
-
* No changes.
|
78
|
-
|
79
|
-
|
80
|
-
## Rails 7.1.0 (October 05, 2023) ##
|
81
|
-
|
82
|
-
* No changes.
|
83
|
-
|
84
|
-
|
85
|
-
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
86
|
-
|
87
|
-
* No changes.
|
88
|
-
|
89
|
-
|
90
|
-
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
91
|
-
|
92
|
-
* Add `expires_at` option to `ActiveStorage::Blob#signed_id`.
|
93
|
-
|
94
|
-
```ruby
|
95
|
-
rails_blob_path(user.avatar, disposition: "attachment", expires_at: 30.minutes.from_now)
|
96
|
-
<%= image_tag rails_blob_path(user.avatar.variant(resize: "100x100"), expires_at: 30.minutes.from_now) %>
|
97
|
-
```
|
98
|
-
|
99
|
-
*Aki*
|
100
|
-
|
101
|
-
* Allow attaching File and Pathname when assigning attributes, e.g.
|
102
|
-
|
103
|
-
```ruby
|
104
|
-
User.create!(avatar: File.open("image.jpg"))
|
105
|
-
User.create!(avatar: file_fixture("image.jpg"))
|
106
|
-
```
|
107
|
-
|
108
|
-
*Dorian Marié*
|
109
|
-
|
110
|
-
|
111
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
112
|
-
|
113
|
-
* Disables the session in `ActiveStorage::Blobs::ProxyController`
|
114
|
-
and `ActiveStorage::Representations::ProxyController`
|
115
|
-
in order to allow caching by default in some CDNs as CloudFlare
|
116
|
-
|
117
|
-
Fixes #44136
|
118
|
-
|
119
|
-
*Bruno Prieto*
|
120
|
-
|
121
|
-
* Add `tags` to `ActiveStorage::Analyzer::AudioAnalyzer` output
|
122
|
-
|
123
|
-
*Keaton Roux*
|
124
|
-
|
125
|
-
* Add an option to preprocess variants
|
126
|
-
|
127
|
-
ActiveStorage variants are processed on the fly when they are needed but
|
128
|
-
sometimes we're sure that they are accessed and want to processed them
|
129
|
-
upfront.
|
130
|
-
|
131
|
-
`preprocessed` option is added when declaring variants.
|
132
|
-
|
133
|
-
```
|
134
|
-
class User < ApplicationRecord
|
135
|
-
has_one_attached :avatar do |attachable|
|
136
|
-
attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
|
137
|
-
end
|
138
|
-
end
|
139
|
-
```
|
140
|
-
|
141
|
-
*Shouichi Kamiya*
|
142
|
-
|
143
|
-
* Fix variants not included when eager loading multiple records containing a single attachment
|
144
|
-
|
145
|
-
When using the `with_attached_#{name}` scope for a `has_one_attached` relation,
|
146
|
-
attachment variants were not eagerly loaded.
|
147
|
-
|
148
|
-
*Russell Porter*
|
149
|
-
|
150
|
-
* Allow an ActiveStorage attachment to be removed via a form post
|
151
|
-
|
152
|
-
Attachments can already be removed by updating the attachment to be nil such as:
|
153
|
-
```ruby
|
154
|
-
User.find(params[:id]).update!(avatar: nil)
|
155
|
-
```
|
156
|
-
|
157
|
-
However, a form cannot post a nil param, it can only post an empty string. But, posting an
|
158
|
-
empty string would result in an `ActiveSupport::MessageVerifier::InvalidSignature: mismatched digest`
|
159
|
-
error being raised, because it's being treated as a signed blob id.
|
160
|
-
|
161
|
-
Now, nil and an empty string are treated as a delete, which allows attachments to be removed via:
|
162
|
-
```ruby
|
163
|
-
User.find(params[:id]).update!(params.require(:user).permit(:avatar))
|
164
|
-
|
165
|
-
```
|
166
|
-
|
167
|
-
*Nate Matykiewicz*
|
168
|
-
|
169
|
-
* Remove mini_mime usage in favour of marcel.
|
170
|
-
|
171
|
-
We have two libraries that are have similar usage. This change removes
|
172
|
-
dependency on mini_mime and makes use of similar methods from marcel.
|
173
|
-
|
174
|
-
*Vipul A M*
|
175
|
-
|
176
|
-
* Allow destroying active storage variants
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
User.first.avatar.variant(resize_to_limit: [100, 100]).destroy
|
180
|
-
```
|
181
|
-
|
182
|
-
*Shouichi Kamiya*, *Yuichiro NAKAGAWA*, *Ryohei UEDA*
|
183
|
-
|
184
|
-
* Add `sample_rate` to `ActiveStorage::Analyzer::AudioAnalyzer` output
|
185
|
-
|
186
|
-
*Matija Čupić*
|
187
|
-
|
188
|
-
* Remove deprecated `purge` and `purge_later` methods from the attachments association.
|
189
|
-
|
190
|
-
*Rafael Mendonça França*
|
191
|
-
|
192
|
-
* Remove deprecated behavior when assigning to a collection of attachments.
|
193
|
-
|
194
|
-
Instead of appending to the collection, the collection is now replaced.
|
195
|
-
|
196
|
-
*Rafael Mendonça França*
|
197
|
-
|
198
|
-
* Remove deprecated `ActiveStorage::Current#host` and `ActiveStorage::Current#host=` methods.
|
199
|
-
|
200
|
-
*Rafael Mendonça França*
|
201
|
-
|
202
|
-
* Remove deprecated invalid default content types in Active Storage configurations.
|
203
|
-
|
204
|
-
*Rafael Mendonça França*
|
205
|
-
|
206
|
-
* Add missing preview event to `ActiveStorage::LogSubscriber`
|
207
|
-
|
208
|
-
A `preview` event is being instrumented in `ActiveStorage::Previewer`.
|
209
|
-
However it was not added inside ActiveStorage's LogSubscriber class.
|
210
|
-
|
211
|
-
This will allow to have logs for when a preview happens
|
212
|
-
in the same fashion as all other ActiveStorage events such as
|
213
|
-
`upload` and `download` inside `Rails.logger`.
|
214
|
-
|
215
|
-
*Chedli Bourguiba*
|
216
|
-
|
217
|
-
* Fix retrieving rotation value from FFmpeg on version 5.0+.
|
218
|
-
|
219
|
-
In FFmpeg version 5.0+ the rotation value has been removed from tags.
|
220
|
-
Instead the value can be found in side_data_list. Along with
|
221
|
-
this update it's possible to have values of -90, -270 to denote the video
|
222
|
-
has been rotated.
|
223
|
-
|
224
|
-
*Haroon Ahmed*
|
225
|
-
|
226
|
-
* Touch all corresponding model records after ActiveStorage::Blob is analyzed
|
227
|
-
|
228
|
-
This fixes a race condition where a record can be requested and have a cache entry built, before
|
229
|
-
the initial `analyze_later` completes, which will not be invalidated until something else
|
230
|
-
updates the record. This also invalidates cache entries when a blob is re-analyzed, which
|
231
|
-
is helpful if a bug is fixed in an analyzer or a new analyzer is added.
|
232
|
-
|
233
|
-
*Nate Matykiewicz*
|
234
|
-
|
235
|
-
* Add ability to use pre-defined variants when calling `preview` or
|
236
|
-
`representation` on an attachment.
|
237
|
-
|
238
|
-
```ruby
|
239
|
-
class User < ActiveRecord::Base
|
240
|
-
has_one_attached :file do |attachable|
|
241
|
-
attachable.variant :thumb, resize_to_limit: [100, 100]
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
<%= image_tag user.file.representation(:thumb) %>
|
246
|
-
```
|
247
|
-
|
248
|
-
*Richard Böhme*
|
249
|
-
|
250
|
-
* Method `attach` always returns the attachments except when the record
|
251
|
-
is persisted, unchanged, and saving it fails, in which case it returns `nil`.
|
252
|
-
|
253
|
-
*Santiago Bartesaghi*
|
254
|
-
|
255
|
-
* Fixes multiple `attach` calls within transaction not uploading files correctly.
|
256
|
-
|
257
|
-
In the following example, the code failed to upload all but the last file to the configured service.
|
258
|
-
```ruby
|
259
|
-
ActiveRecord::Base.transaction do
|
260
|
-
user.attachments.attach({
|
261
|
-
content_type: "text/plain",
|
262
|
-
filename: "dummy.txt",
|
263
|
-
io: ::StringIO.new("dummy"),
|
264
|
-
})
|
265
|
-
user.attachments.attach({
|
266
|
-
content_type: "text/plain",
|
267
|
-
filename: "dummy2.txt",
|
268
|
-
io: ::StringIO.new("dummy2"),
|
269
|
-
})
|
270
|
-
end
|
271
|
-
|
272
|
-
assert_equal 2, user.attachments.count
|
273
|
-
assert user.attachments.first.service.exist?(user.attachments.first.key) # Fails
|
274
|
-
```
|
275
|
-
|
276
|
-
This was addressed by keeping track of the subchanges pending upload, and uploading them
|
277
|
-
once the transaction is committed.
|
278
|
-
|
279
|
-
Fixes #41661
|
280
|
-
|
281
|
-
*Santiago Bartesaghi*, *Bruno Vezoli*, *Juan Roig*, *Abhay Nikam*
|
282
|
-
|
283
|
-
* Raise an exception if `config.active_storage.service` is not set.
|
284
|
-
|
285
|
-
If Active Storage is configured and `config.active_storage.service` is not
|
286
|
-
set in the respective environment's configuration file, then an exception
|
287
|
-
is raised with a meaningful message when attempting to use Active Storage.
|
288
|
-
|
289
|
-
*Ghouse Mohamed*
|
290
|
-
|
291
|
-
* Fixes proxy downloads of files over 5mb
|
292
|
-
|
293
|
-
Previously, trying to view and/or download files larger than 5mb stored in
|
294
|
-
services like S3 via proxy mode could return corrupted files at around
|
295
|
-
5.2mb or cause random halts in the download. Now,
|
296
|
-
`ActiveStorage::Blobs::ProxyController` correctly handles streaming these
|
297
|
-
larger files from the service to the client without any issues.
|
298
|
-
|
299
|
-
Fixes #44679
|
300
|
-
|
301
|
-
*Felipe Raul*
|
302
|
-
|
303
|
-
* Saving attachment(s) to a record returns the blob/blobs object
|
304
|
-
|
305
|
-
Previously, saving attachments did not return the blob/blobs that
|
306
|
-
were attached. Now, saving attachments to a record with `#attach`
|
307
|
-
method returns the blob or array of blobs that were attached to
|
308
|
-
the record. If it fails to save the attachment(s), then it returns
|
309
|
-
`false`.
|
310
|
-
|
311
|
-
*Ghouse Mohamed*
|
312
|
-
|
313
|
-
* Don't stream responses in redirect mode
|
314
|
-
|
315
|
-
Previously, both redirect mode and proxy mode streamed their
|
316
|
-
responses which caused a new thread to be created, and could end
|
317
|
-
up leaking connections in the connection pool. But since redirect
|
318
|
-
mode doesn't actually send any data, it doesn't need to be
|
319
|
-
streamed.
|
320
|
-
|
321
|
-
*Luke Lau*
|
322
|
-
|
323
|
-
* Safe for direct upload on Libraries or Frameworks
|
324
|
-
|
325
|
-
Enable the use of custom headers during direct uploads, which allows for
|
326
|
-
the inclusion of Authorization bearer tokens or other forms of authorization
|
327
|
-
tokens through headers.
|
328
|
-
|
329
|
-
*Radamés Roriz*
|
76
|
+
*Yogesh Khater*
|
330
77
|
|
331
|
-
Please check [7-
|
78
|
+
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activestorage/CHANGELOG.md) for previous changes.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActiveStorage::PreviewImageJob < ActiveStorage::BaseJob
|
4
|
+
queue_as { ActiveStorage.queues[:preview_image] }
|
5
|
+
|
6
|
+
discard_on ActiveRecord::RecordNotFound, ActiveStorage::UnrepresentableError
|
7
|
+
retry_on ActiveStorage::IntegrityError, attempts: 10, wait: :polynomially_longer
|
8
|
+
|
9
|
+
def perform(blob, variations)
|
10
|
+
blob.preview({}).processed
|
11
|
+
|
12
|
+
variations.each do |transformations|
|
13
|
+
blob.preprocessed(transformations)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -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:
|
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
|
@@ -132,8 +132,18 @@ class ActiveStorage::Attachment < ActiveStorage::Record
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def transform_variants_later
|
135
|
-
named_variants.
|
136
|
-
|
135
|
+
preprocessed_variations = named_variants.filter_map { |_name, named_variant|
|
136
|
+
if named_variant.preprocessed?(record)
|
137
|
+
named_variant.transformations
|
138
|
+
end
|
139
|
+
}
|
140
|
+
|
141
|
+
if blob.preview_image_needed_before_processing_variants? && preprocessed_variations.any?
|
142
|
+
blob.create_preview_image_later(preprocessed_variations)
|
143
|
+
else
|
144
|
+
preprocessed_variations.each do |transformations|
|
145
|
+
blob.preprocessed(transformations)
|
146
|
+
end
|
137
147
|
end
|
138
148
|
end
|
139
149
|
|
@@ -146,7 +156,7 @@ class ActiveStorage::Attachment < ActiveStorage::Record
|
|
146
156
|
end
|
147
157
|
|
148
158
|
def named_variants
|
149
|
-
record.attachment_reflections[name]&.named_variants
|
159
|
+
record.attachment_reflections[name]&.named_variants || {}
|
150
160
|
end
|
151
161
|
|
152
162
|
def transformations_by_name(transformations)
|
@@ -98,6 +98,14 @@ module ActiveStorage::Blob::Representable
|
|
98
98
|
variable? || previewable?
|
99
99
|
end
|
100
100
|
|
101
|
+
def preview_image_needed_before_processing_variants? # :nodoc:
|
102
|
+
previewable? && !preview_image.attached?
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_preview_image_later(variations) # :nodoc:
|
106
|
+
ActiveStorage::PreviewImageJob.perform_later(self, variations) if representable?
|
107
|
+
end
|
108
|
+
|
101
109
|
def preprocessed(transformations) # :nodoc:
|
102
110
|
ActiveStorage::TransformJob.perform_later(self, transformations) if representable?
|
103
111
|
end
|
@@ -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 :
|
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
|
-
|
338
|
-
|
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
|
364
|
-
attachments.
|
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
|
@@ -35,7 +35,7 @@ class ActiveStorage::Preview
|
|
35
35
|
|
36
36
|
class UnprocessedError < StandardError; end
|
37
37
|
|
38
|
-
delegate :filename, :content_type, to: :
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|
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
|
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
|
59
|
-
Integer(
|
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
|
-
|
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
|
@@ -74,16 +74,24 @@ module ActiveStorage
|
|
74
74
|
# The system has been designed to having you go through the ActiveStorage::Attached::One
|
75
75
|
# proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
|
76
76
|
#
|
77
|
-
#
|
78
|
-
# (i.e. destroyed) whenever the record is destroyed.
|
77
|
+
# The +:dependent+ option defaults to +:purge_later+. This means the attachment will be
|
78
|
+
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
79
|
+
# If an ActiveJob::Backend queue adapter is not set in the application set it to
|
80
|
+
# +purge+ instead.
|
79
81
|
#
|
80
82
|
# If you need the attachment to use a service which differs from the globally configured one,
|
81
|
-
# pass the +:service+ option. For
|
83
|
+
# pass the +:service+ option. For example:
|
82
84
|
#
|
83
85
|
# class User < ActiveRecord::Base
|
84
86
|
# has_one_attached :avatar, service: :s3
|
85
87
|
# end
|
86
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
|
+
#
|
87
95
|
# If you need to enable +strict_loading+ to prevent lazy loading of attachment,
|
88
96
|
# pass the +:strict_loading+ option. You can do:
|
89
97
|
#
|
@@ -91,8 +99,12 @@ module ActiveStorage
|
|
91
99
|
# has_one_attached :avatar, strict_loading: true
|
92
100
|
# end
|
93
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.
|
94
106
|
def has_one_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
95
|
-
validate_service_configuration(
|
107
|
+
ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
|
96
108
|
|
97
109
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
98
110
|
# frozen_string_literal: true
|
@@ -162,16 +174,24 @@ module ActiveStorage
|
|
162
174
|
# The system has been designed to having you go through the ActiveStorage::Attached::Many
|
163
175
|
# proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
|
164
176
|
#
|
165
|
-
#
|
166
|
-
# (i.e. destroyed) whenever the record is destroyed.
|
177
|
+
# The +:dependent+ option defaults to +:purge_later+. This means the attachments will be
|
178
|
+
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
179
|
+
# If an ActiveJob::Backend queue adapter is not set in the application set it to
|
180
|
+
# +purge+ instead.
|
167
181
|
#
|
168
182
|
# If you need the attachment to use a service which differs from the globally configured one,
|
169
|
-
# pass the +:service+ option. For
|
183
|
+
# pass the +:service+ option. For example:
|
170
184
|
#
|
171
185
|
# class Gallery < ActiveRecord::Base
|
172
186
|
# has_many_attached :photos, service: :s3
|
173
187
|
# end
|
174
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
|
+
#
|
175
195
|
# If you need to enable +strict_loading+ to prevent lazy loading of attachments,
|
176
196
|
# pass the +:strict_loading+ option. You can do:
|
177
197
|
#
|
@@ -179,8 +199,12 @@ module ActiveStorage
|
|
179
199
|
# has_many_attached :photos, strict_loading: true
|
180
200
|
# end
|
181
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.
|
182
206
|
def has_many_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
183
|
-
validate_service_configuration(
|
207
|
+
ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
|
184
208
|
|
185
209
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
186
210
|
# frozen_string_literal: true
|
@@ -229,23 +253,6 @@ module ActiveStorage
|
|
229
253
|
yield reflection if block_given?
|
230
254
|
ActiveRecord::Reflection.add_attachment_reflection(self, name, reflection)
|
231
255
|
end
|
232
|
-
|
233
|
-
private
|
234
|
-
def validate_service_configuration(association_name, service)
|
235
|
-
if service.present?
|
236
|
-
ActiveStorage::Blob.services.fetch(service) do
|
237
|
-
raise ArgumentError, "Cannot configure service :#{service} for #{name}##{association_name}"
|
238
|
-
end
|
239
|
-
else
|
240
|
-
validate_global_service_configuration
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def validate_global_service_configuration
|
245
|
-
if connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
|
246
|
-
raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
|
247
|
-
end
|
248
|
-
end
|
249
256
|
end
|
250
257
|
|
251
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 || []
|
@@ -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
|
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
|
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
|
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
|
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
|
@@ -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
|
8
|
+
Please add `gem "image_processing", "~> 1.2"` to your Gemfile.
|
9
9
|
ERROR
|
10
10
|
end
|
11
11
|
|
data/lib/active_storage.rb
CHANGED
@@ -35,7 +35,7 @@ require "active_storage/errors"
|
|
35
35
|
require "marcel"
|
36
36
|
|
37
37
|
# :markup: markdown
|
38
|
-
# :include:
|
38
|
+
# :include: ../README.md
|
39
39
|
module ActiveStorage
|
40
40
|
extend ActiveSupport::Autoload
|
41
41
|
|
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
68
|
+
version: 7.2.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: marcel
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- app/jobs/active_storage/analyze_job.rb
|
117
117
|
- app/jobs/active_storage/base_job.rb
|
118
118
|
- app/jobs/active_storage/mirror_job.rb
|
119
|
+
- app/jobs/active_storage/preview_image_job.rb
|
119
120
|
- app/jobs/active_storage/purge_job.rb
|
120
121
|
- app/jobs/active_storage/transform_job.rb
|
121
122
|
- app/models/active_storage/attachment.rb
|
@@ -189,10 +190,10 @@ licenses:
|
|
189
190
|
- MIT
|
190
191
|
metadata:
|
191
192
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
192
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.
|
193
|
-
documentation_uri: https://api.rubyonrails.org/v7.
|
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/
|
194
195
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
195
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.
|
196
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.2.0/activestorage
|
196
197
|
rubygems_mfa_required: 'true'
|
197
198
|
post_install_message:
|
198
199
|
rdoc_options: []
|
@@ -202,14 +203,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
202
203
|
requirements:
|
203
204
|
- - ">="
|
204
205
|
- !ruby/object:Gem::Version
|
205
|
-
version:
|
206
|
+
version: 3.1.0
|
206
207
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
208
|
requirements:
|
208
209
|
- - ">="
|
209
210
|
- !ruby/object:Gem::Version
|
210
211
|
version: '0'
|
211
212
|
requirements: []
|
212
|
-
rubygems_version: 3.
|
213
|
+
rubygems_version: 3.5.11
|
213
214
|
signing_key:
|
214
215
|
specification_version: 4
|
215
216
|
summary: Local and cloud file storage framework.
|