activestorage 6.1.4 → 7.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +143 -200
- data/MIT-LICENSE +1 -1
- data/README.md +25 -11
- data/app/assets/javascripts/activestorage.esm.js +856 -0
- data/app/assets/javascripts/activestorage.js +270 -377
- data/app/controllers/active_storage/base_controller.rb +1 -10
- data/app/controllers/active_storage/blobs/proxy_controller.rb +14 -4
- data/app/controllers/active_storage/blobs/redirect_controller.rb +6 -4
- data/app/controllers/active_storage/direct_uploads_controller.rb +7 -1
- data/app/controllers/active_storage/disk_controller.rb +1 -0
- data/app/controllers/active_storage/representations/base_controller.rb +5 -1
- data/app/controllers/active_storage/representations/proxy_controller.rb +6 -4
- data/app/controllers/active_storage/representations/redirect_controller.rb +6 -4
- data/app/controllers/concerns/active_storage/set_blob.rb +6 -2
- data/app/controllers/concerns/active_storage/set_current.rb +3 -3
- data/app/controllers/concerns/active_storage/streaming.rb +65 -0
- data/app/javascript/activestorage/blob_record.js +10 -3
- data/app/javascript/activestorage/direct_upload.js +4 -2
- data/app/javascript/activestorage/direct_upload_controller.js +9 -1
- data/app/javascript/activestorage/ujs.js +1 -1
- data/app/models/active_storage/attachment.rb +36 -3
- data/app/models/active_storage/blob/representable.rb +7 -5
- data/app/models/active_storage/blob.rb +92 -36
- data/app/models/active_storage/current.rb +12 -2
- data/app/models/active_storage/preview.rb +6 -4
- data/app/models/active_storage/record.rb +1 -1
- data/app/models/active_storage/variant.rb +3 -6
- data/app/models/active_storage/variant_record.rb +2 -0
- data/app/models/active_storage/variant_with_record.rb +9 -5
- data/app/models/active_storage/variation.rb +2 -2
- data/config/routes.rb +10 -10
- data/db/migrate/20170806125915_create_active_storage_tables.rb +32 -11
- data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +15 -2
- data/db/update_migrate/20211119233751_remove_not_null_on_active_storage_blobs_checksum.rb +5 -0
- data/lib/active_storage/analyzer/audio_analyzer.rb +65 -0
- data/lib/active_storage/analyzer/image_analyzer/image_magick.rb +39 -0
- data/lib/active_storage/analyzer/image_analyzer/vips.rb +49 -0
- data/lib/active_storage/analyzer/image_analyzer.rb +2 -30
- data/lib/active_storage/analyzer/video_analyzer.rb +26 -11
- data/lib/active_storage/analyzer.rb +8 -4
- data/lib/active_storage/attached/changes/create_many.rb +7 -3
- data/lib/active_storage/attached/changes/create_one.rb +1 -1
- data/lib/active_storage/attached/changes/create_one_of_many.rb +1 -1
- data/lib/active_storage/attached/changes/delete_many.rb +1 -1
- data/lib/active_storage/attached/changes/delete_one.rb +1 -1
- data/lib/active_storage/attached/changes/detach_many.rb +18 -0
- data/lib/active_storage/attached/changes/detach_one.rb +24 -0
- data/lib/active_storage/attached/changes/purge_many.rb +27 -0
- data/lib/active_storage/attached/changes/purge_one.rb +27 -0
- data/lib/active_storage/attached/changes.rb +7 -1
- data/lib/active_storage/attached/many.rb +27 -15
- data/lib/active_storage/attached/model.rb +31 -5
- data/lib/active_storage/attached/one.rb +32 -27
- data/lib/active_storage/direct_upload_token.rb +59 -0
- data/lib/active_storage/downloader.rb +4 -4
- data/lib/active_storage/engine.rb +30 -1
- data/lib/active_storage/errors.rb +3 -0
- data/lib/active_storage/fixture_set.rb +76 -0
- data/lib/active_storage/gem_version.rb +4 -4
- data/lib/active_storage/previewer.rb +4 -4
- data/lib/active_storage/reflection.rb +12 -2
- data/lib/active_storage/service/azure_storage_service.rb +28 -6
- data/lib/active_storage/service/configurator.rb +1 -1
- data/lib/active_storage/service/disk_service.rb +24 -19
- data/lib/active_storage/service/gcs_service.rb +109 -11
- data/lib/active_storage/service/mirror_service.rb +2 -2
- data/lib/active_storage/service/registry.rb +1 -1
- data/lib/active_storage/service/s3_service.rb +37 -15
- data/lib/active_storage/service.rb +13 -5
- data/lib/active_storage/transformers/image_processing_transformer.rb +1 -1
- data/lib/active_storage/transformers/transformer.rb +1 -1
- data/lib/active_storage.rb +6 -1
- metadata +32 -20
- data/app/controllers/concerns/active_storage/set_headers.rb +0 -12
@@ -1,25 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
gem "google-cloud-storage", "~> 1.11"
|
4
|
+
require "google/apis/iamcredentials_v1"
|
4
5
|
require "google/cloud/storage"
|
5
6
|
|
6
7
|
module ActiveStorage
|
7
8
|
# Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
|
8
9
|
# documentation that applies to all services.
|
9
10
|
class Service::GCSService < Service
|
11
|
+
class MetadataServerError < ActiveStorage::Error; end
|
12
|
+
class MetadataServerNotFoundError < ActiveStorage::Error; end
|
13
|
+
|
10
14
|
def initialize(public: false, **config)
|
11
15
|
@config = config
|
12
16
|
@public = public
|
13
17
|
end
|
14
18
|
|
15
|
-
def upload(key, io, checksum: nil, content_type: nil, disposition: nil, filename: nil)
|
19
|
+
def upload(key, io, checksum: nil, content_type: nil, disposition: nil, filename: nil, custom_metadata: {})
|
16
20
|
instrument :upload, key: key, checksum: checksum do
|
17
21
|
# GCS's signed URLs don't include params such as response-content-type response-content_disposition
|
18
22
|
# in the signature, which means an attacker can modify them and bypass our effort to force these to
|
19
23
|
# binary and attachment when the file's content type requires it. The only way to force them is to
|
20
24
|
# store them as object's metadata.
|
21
25
|
content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
22
|
-
bucket.create_file(io, key, md5: checksum, content_type: content_type, content_disposition: content_disposition)
|
26
|
+
bucket.create_file(io, key, md5: checksum, cache_control: @config[:cache_control], content_type: content_type, content_disposition: content_disposition, metadata: custom_metadata)
|
23
27
|
rescue Google::Cloud::InvalidArgumentError
|
24
28
|
raise ActiveStorage::IntegrityError
|
25
29
|
end
|
@@ -39,11 +43,12 @@ module ActiveStorage
|
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
def update_metadata(key, content_type:, disposition: nil, filename: nil)
|
46
|
+
def update_metadata(key, content_type:, disposition: nil, filename: nil, custom_metadata: {})
|
43
47
|
instrument :update_metadata, key: key, content_type: content_type, disposition: disposition do
|
44
48
|
file_for(key).update do |file|
|
45
49
|
file.content_type = content_type
|
46
50
|
file.content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
51
|
+
file.metadata = custom_metadata
|
47
52
|
end
|
48
53
|
end
|
49
54
|
end
|
@@ -82,9 +87,35 @@ module ActiveStorage
|
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
85
|
-
def url_for_direct_upload(key, expires_in:, checksum:, **)
|
90
|
+
def url_for_direct_upload(key, expires_in:, checksum:, custom_metadata: {}, **)
|
86
91
|
instrument :url, key: key do |payload|
|
87
|
-
|
92
|
+
headers = {}
|
93
|
+
version = :v2
|
94
|
+
|
95
|
+
if @config[:cache_control].present?
|
96
|
+
headers["Cache-Control"] = @config[:cache_control]
|
97
|
+
# v2 signing doesn't support non `x-goog-` headers. Only switch to v4 signing
|
98
|
+
# if necessary for back-compat; v4 limits the expiration of the URL to 7 days
|
99
|
+
# whereas v2 has no limit
|
100
|
+
version = :v4
|
101
|
+
end
|
102
|
+
|
103
|
+
headers.merge!(custom_metadata_headers(custom_metadata))
|
104
|
+
|
105
|
+
args = {
|
106
|
+
content_md5: checksum,
|
107
|
+
expires: expires_in,
|
108
|
+
headers: headers,
|
109
|
+
method: "PUT",
|
110
|
+
version: version,
|
111
|
+
}
|
112
|
+
|
113
|
+
if @config[:iam]
|
114
|
+
args[:issuer] = issuer
|
115
|
+
args[:signer] = signer
|
116
|
+
end
|
117
|
+
|
118
|
+
generated_url = bucket.signed_url(key, **args)
|
88
119
|
|
89
120
|
payload[:url] = generated_url
|
90
121
|
|
@@ -92,18 +123,41 @@ module ActiveStorage
|
|
92
123
|
end
|
93
124
|
end
|
94
125
|
|
95
|
-
def headers_for_direct_upload(key, checksum:, filename: nil, disposition: nil, **)
|
126
|
+
def headers_for_direct_upload(key, checksum:, filename: nil, disposition: nil, custom_metadata: {}, **)
|
96
127
|
content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
|
97
128
|
|
98
|
-
{ "Content-MD5" => checksum, "Content-Disposition" => content_disposition }
|
129
|
+
headers = { "Content-MD5" => checksum, "Content-Disposition" => content_disposition, **custom_metadata_headers(custom_metadata) }
|
130
|
+
if @config[:cache_control].present?
|
131
|
+
headers["Cache-Control"] = @config[:cache_control]
|
132
|
+
end
|
133
|
+
|
134
|
+
headers
|
135
|
+
end
|
136
|
+
|
137
|
+
def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
|
138
|
+
bucket.compose(source_keys, destination_key).update do |file|
|
139
|
+
file.content_type = content_type
|
140
|
+
file.content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
141
|
+
file.metadata = custom_metadata
|
142
|
+
end
|
99
143
|
end
|
100
144
|
|
101
145
|
private
|
102
146
|
def private_url(key, expires_in:, filename:, content_type:, disposition:, **)
|
103
|
-
|
104
|
-
|
105
|
-
|
147
|
+
args = {
|
148
|
+
expires: expires_in,
|
149
|
+
query: {
|
150
|
+
"response-content-disposition" => content_disposition_with(type: disposition, filename: filename),
|
151
|
+
"response-content-type" => content_type
|
152
|
+
}
|
106
153
|
}
|
154
|
+
|
155
|
+
if @config[:iam]
|
156
|
+
args[:issuer] = issuer
|
157
|
+
args[:signer] = signer
|
158
|
+
end
|
159
|
+
|
160
|
+
file_for(key).signed_url(**args)
|
107
161
|
end
|
108
162
|
|
109
163
|
def public_url(key, **)
|
@@ -137,7 +191,51 @@ module ActiveStorage
|
|
137
191
|
end
|
138
192
|
|
139
193
|
def client
|
140
|
-
@client ||= Google::Cloud::Storage.new(**config.except(:bucket))
|
194
|
+
@client ||= Google::Cloud::Storage.new(**config.except(:bucket, :cache_control, :iam, :gsa_email))
|
195
|
+
end
|
196
|
+
|
197
|
+
def issuer
|
198
|
+
@issuer ||= if @config[:gsa_email]
|
199
|
+
@config[:gsa_email]
|
200
|
+
else
|
201
|
+
uri = URI.parse("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email")
|
202
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
203
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
204
|
+
request["Metadata-Flavor"] = "Google"
|
205
|
+
|
206
|
+
begin
|
207
|
+
response = http.request(request)
|
208
|
+
rescue SocketError
|
209
|
+
raise MetadataServerNotFoundError
|
210
|
+
end
|
211
|
+
|
212
|
+
if response.is_a?(Net::HTTPSuccess)
|
213
|
+
response.body
|
214
|
+
else
|
215
|
+
raise MetadataServerError
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def signer
|
221
|
+
# https://googleapis.dev/ruby/google-cloud-storage/latest/Google/Cloud/Storage/Project.html#signed_url-instance_method
|
222
|
+
lambda do |string_to_sign|
|
223
|
+
iam_client = Google::Apis::IamcredentialsV1::IAMCredentialsService.new
|
224
|
+
|
225
|
+
scopes = ["https://www.googleapis.com/auth/iam"]
|
226
|
+
iam_client.authorization = Google::Auth.get_application_default(scopes)
|
227
|
+
|
228
|
+
request = Google::Apis::IamcredentialsV1::SignBlobRequest.new(
|
229
|
+
payload: string_to_sign
|
230
|
+
)
|
231
|
+
resource = "projects/-/serviceAccounts/#{issuer}"
|
232
|
+
response = iam_client.sign_service_account_blob(resource, request)
|
233
|
+
response.signed_blob
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def custom_metadata_headers(metadata)
|
238
|
+
metadata.transform_keys { |key| "x-goog-meta-#{key}" }
|
141
239
|
end
|
142
240
|
end
|
143
241
|
end
|
@@ -14,10 +14,10 @@ module ActiveStorage
|
|
14
14
|
attr_reader :primary, :mirrors
|
15
15
|
|
16
16
|
delegate :download, :download_chunk, :exist?, :url,
|
17
|
-
:url_for_direct_upload, :headers_for_direct_upload, :path_for, to: :primary
|
17
|
+
:url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary
|
18
18
|
|
19
19
|
# Stitch together from named services.
|
20
|
-
def self.build(primary:, mirrors:, name:, configurator:, **options)
|
20
|
+
def self.build(primary:, mirrors:, name:, configurator:, **options) # :nodoc:
|
21
21
|
new(
|
22
22
|
primary: configurator.build(primary),
|
23
23
|
mirrors: mirrors.collect { |mirror_name| configurator.build mirror_name }
|
@@ -23,14 +23,14 @@ module ActiveStorage
|
|
23
23
|
@upload_options[:acl] = "public-read" if public?
|
24
24
|
end
|
25
25
|
|
26
|
-
def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, **)
|
26
|
+
def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, custom_metadata: {}, **)
|
27
27
|
instrument :upload, key: key, checksum: checksum do
|
28
28
|
content_disposition = content_disposition_with(filename: filename, type: disposition) if disposition && filename
|
29
29
|
|
30
30
|
if io.size < multipart_upload_threshold
|
31
|
-
upload_with_single_part key, io, checksum: checksum, content_type: content_type, content_disposition: content_disposition
|
31
|
+
upload_with_single_part key, io, checksum: checksum, content_type: content_type, content_disposition: content_disposition, custom_metadata: custom_metadata
|
32
32
|
else
|
33
|
-
upload_with_multipart key, io, content_type: content_type, content_disposition: content_disposition
|
33
|
+
upload_with_multipart key, io, content_type: content_type, content_disposition: content_disposition, custom_metadata: custom_metadata
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -77,11 +77,11 @@ module ActiveStorage
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
|
80
|
+
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata: {})
|
81
81
|
instrument :url, key: key do |payload|
|
82
82
|
generated_url = object_for(key).presigned_url :put, expires_in: expires_in.to_i,
|
83
83
|
content_type: content_type, content_length: content_length, content_md5: checksum,
|
84
|
-
whitelist_headers: ["content-length"], **upload_options
|
84
|
+
metadata: custom_metadata, whitelist_headers: ["content-length"], **upload_options
|
85
85
|
|
86
86
|
payload[:url] = generated_url
|
87
87
|
|
@@ -89,37 +89,55 @@ module ActiveStorage
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, **)
|
92
|
+
def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, custom_metadata: {}, **)
|
93
93
|
content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
|
94
94
|
|
95
|
-
{ "Content-Type" => content_type, "Content-MD5" => checksum, "Content-Disposition" => content_disposition }
|
95
|
+
{ "Content-Type" => content_type, "Content-MD5" => checksum, "Content-Disposition" => content_disposition, **custom_metadata_headers(custom_metadata) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
|
99
|
+
content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
100
|
+
|
101
|
+
object_for(destination_key).upload_stream(
|
102
|
+
content_type: content_type,
|
103
|
+
content_disposition: content_disposition,
|
104
|
+
part_size: MINIMUM_UPLOAD_PART_SIZE,
|
105
|
+
metadata: custom_metadata,
|
106
|
+
**upload_options
|
107
|
+
) do |out|
|
108
|
+
source_keys.each do |source_key|
|
109
|
+
stream(source_key) do |chunk|
|
110
|
+
IO.copy_stream(StringIO.new(chunk), out)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
96
114
|
end
|
97
115
|
|
98
116
|
private
|
99
|
-
def private_url(key, expires_in:, filename:, disposition:, content_type:, **)
|
117
|
+
def private_url(key, expires_in:, filename:, disposition:, content_type:, **client_opts)
|
100
118
|
object_for(key).presigned_url :get, expires_in: expires_in.to_i,
|
101
119
|
response_content_disposition: content_disposition_with(type: disposition, filename: filename),
|
102
|
-
response_content_type: content_type
|
120
|
+
response_content_type: content_type, **client_opts
|
103
121
|
end
|
104
122
|
|
105
|
-
def public_url(key, **)
|
106
|
-
object_for(key).public_url
|
123
|
+
def public_url(key, **client_opts)
|
124
|
+
object_for(key).public_url(**client_opts)
|
107
125
|
end
|
108
126
|
|
109
127
|
|
110
128
|
MAXIMUM_UPLOAD_PARTS_COUNT = 10000
|
111
129
|
MINIMUM_UPLOAD_PART_SIZE = 5.megabytes
|
112
130
|
|
113
|
-
def upload_with_single_part(key, io, checksum: nil, content_type: nil, content_disposition: nil)
|
114
|
-
object_for(key).put(body: io, content_md5: checksum, content_type: content_type, content_disposition: content_disposition, **upload_options)
|
131
|
+
def upload_with_single_part(key, io, checksum: nil, content_type: nil, content_disposition: nil, custom_metadata: {})
|
132
|
+
object_for(key).put(body: io, content_md5: checksum, content_type: content_type, content_disposition: content_disposition, metadata: custom_metadata, **upload_options)
|
115
133
|
rescue Aws::S3::Errors::BadDigest
|
116
134
|
raise ActiveStorage::IntegrityError
|
117
135
|
end
|
118
136
|
|
119
|
-
def upload_with_multipart(key, io, content_type: nil, content_disposition: nil)
|
137
|
+
def upload_with_multipart(key, io, content_type: nil, content_disposition: nil, custom_metadata: {})
|
120
138
|
part_size = [ io.size.fdiv(MAXIMUM_UPLOAD_PARTS_COUNT).ceil, MINIMUM_UPLOAD_PART_SIZE ].max
|
121
139
|
|
122
|
-
object_for(key).upload_stream(content_type: content_type, content_disposition: content_disposition, part_size: part_size, **upload_options) do |out|
|
140
|
+
object_for(key).upload_stream(content_type: content_type, content_disposition: content_disposition, part_size: part_size, metadata: custom_metadata, **upload_options) do |out|
|
123
141
|
IO.copy_stream(io, out)
|
124
142
|
end
|
125
143
|
end
|
@@ -143,5 +161,9 @@ module ActiveStorage
|
|
143
161
|
offset += chunk_size
|
144
162
|
end
|
145
163
|
end
|
164
|
+
|
165
|
+
def custom_metadata_headers(metadata)
|
166
|
+
metadata.transform_keys { |key| "x-amz-meta-#{key}" }
|
167
|
+
end
|
146
168
|
end
|
147
169
|
end
|
@@ -35,8 +35,8 @@ module ActiveStorage
|
|
35
35
|
# can configure the service to use like this:
|
36
36
|
#
|
37
37
|
# ActiveStorage::Blob.service = ActiveStorage::Service.configure(
|
38
|
-
# :
|
39
|
-
# root: Pathname("/foo/
|
38
|
+
# :local,
|
39
|
+
# { local: {service: "Disk", root: Pathname("/tmp/foo/storage") } }
|
40
40
|
# )
|
41
41
|
class Service
|
42
42
|
extend ActiveSupport::Autoload
|
@@ -57,7 +57,7 @@ module ActiveStorage
|
|
57
57
|
# Passes the configurator and all of the service's config as keyword args.
|
58
58
|
#
|
59
59
|
# See MirrorService for an example.
|
60
|
-
def build(configurator:, name:, service: nil, **service_config)
|
60
|
+
def build(configurator:, name:, service: nil, **service_config) # :nodoc:
|
61
61
|
new(**service_config).tap do |service_instance|
|
62
62
|
service_instance.name = name
|
63
63
|
end
|
@@ -90,6 +90,11 @@ module ActiveStorage
|
|
90
90
|
ActiveStorage::Downloader.new(self).open(*args, **options, &block)
|
91
91
|
end
|
92
92
|
|
93
|
+
# Concatenate multiple files into a single "composed" file.
|
94
|
+
def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
|
95
|
+
raise NotImplementedError
|
96
|
+
end
|
97
|
+
|
93
98
|
# Delete the file at the +key+.
|
94
99
|
def delete(key)
|
95
100
|
raise NotImplementedError
|
@@ -128,12 +133,12 @@ module ActiveStorage
|
|
128
133
|
# The URL will be valid for the amount of seconds specified in +expires_in+.
|
129
134
|
# You must also provide the +content_type+, +content_length+, and +checksum+ of the file
|
130
135
|
# that will be uploaded. All these attributes will be validated by the service upon upload.
|
131
|
-
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
|
136
|
+
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata: {})
|
132
137
|
raise NotImplementedError
|
133
138
|
end
|
134
139
|
|
135
140
|
# Returns a Hash of headers for +url_for_direct_upload+ requests.
|
136
|
-
def headers_for_direct_upload(key, filename:, content_type:, content_length:, checksum:)
|
141
|
+
def headers_for_direct_upload(key, filename:, content_type:, content_length:, checksum:, custom_metadata: {})
|
137
142
|
{}
|
138
143
|
end
|
139
144
|
|
@@ -150,6 +155,9 @@ module ActiveStorage
|
|
150
155
|
raise NotImplementedError
|
151
156
|
end
|
152
157
|
|
158
|
+
def custom_metadata_headers(metadata)
|
159
|
+
raise NotImplementedError
|
160
|
+
end
|
153
161
|
|
154
162
|
def instrument(operation, payload = {}, &block)
|
155
163
|
ActiveSupport::Notifications.instrument(
|
@@ -31,7 +31,7 @@ module ActiveStorage
|
|
31
31
|
if name.to_s == "combine_options"
|
32
32
|
raise ArgumentError, <<~ERROR.squish
|
33
33
|
Active Storage's ImageProcessing transformer doesn't support :combine_options,
|
34
|
-
as it always generates a single
|
34
|
+
as it always generates a single command.
|
35
35
|
ERROR
|
36
36
|
end
|
37
37
|
|
@@ -31,7 +31,7 @@ module ActiveStorage
|
|
31
31
|
private
|
32
32
|
# Returns an open Tempfile containing a transformed image in the given +format+.
|
33
33
|
# All subclasses implement this method.
|
34
|
-
def process(file, format:)
|
34
|
+
def process(file, format:) # :doc:
|
35
35
|
raise NotImplementedError
|
36
36
|
end
|
37
37
|
end
|
data/lib/active_storage.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
#--
|
4
|
-
# Copyright (c) 2017-
|
4
|
+
# Copyright (c) 2017-2021 David Heinemeier Hansson, Basecamp
|
5
5
|
#
|
6
6
|
# Permission is hereby granted, free of charge, to any person obtaining
|
7
7
|
# a copy of this software and associated documentation files (the
|
@@ -37,9 +37,11 @@ module ActiveStorage
|
|
37
37
|
extend ActiveSupport::Autoload
|
38
38
|
|
39
39
|
autoload :Attached
|
40
|
+
autoload :FixtureSet
|
40
41
|
autoload :Service
|
41
42
|
autoload :Previewer
|
42
43
|
autoload :Analyzer
|
44
|
+
autoload :DirectUploadToken
|
43
45
|
|
44
46
|
mattr_accessor :logger
|
45
47
|
mattr_accessor :verifier
|
@@ -59,6 +61,7 @@ module ActiveStorage
|
|
59
61
|
mattr_accessor :content_types_allowed_inline, default: []
|
60
62
|
|
61
63
|
mattr_accessor :service_urls_expire_in, default: 5.minutes
|
64
|
+
mattr_accessor :urls_expire_in
|
62
65
|
|
63
66
|
mattr_accessor :routes_prefix, default: "/rails/active_storage"
|
64
67
|
mattr_accessor :draw_routes, default: true
|
@@ -69,6 +72,8 @@ module ActiveStorage
|
|
69
72
|
|
70
73
|
mattr_accessor :video_preview_arguments, default: "-y -vframes 1 -f image2"
|
71
74
|
|
75
|
+
mattr_accessor :silence_invalid_content_types_warning, default: false
|
76
|
+
|
72
77
|
module Transformers
|
73
78
|
extend ActiveSupport::Autoload
|
74
79
|
|
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:
|
4
|
+
version: 7.0.0.rc1
|
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: 2021-06
|
11
|
+
date: 2021-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,70 +16,70 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 7.0.0.rc1
|
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:
|
26
|
+
version: 7.0.0.rc1
|
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:
|
33
|
+
version: 7.0.0.rc1
|
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:
|
40
|
+
version: 7.0.0.rc1
|
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:
|
47
|
+
version: 7.0.0.rc1
|
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:
|
54
|
+
version: 7.0.0.rc1
|
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:
|
61
|
+
version: 7.0.0.rc1
|
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:
|
68
|
+
version: 7.0.0.rc1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: marcel
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.0
|
75
|
+
version: '1.0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.0
|
82
|
+
version: '1.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: mini_mime
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- CHANGELOG.md
|
104
104
|
- MIT-LICENSE
|
105
105
|
- README.md
|
106
|
+
- app/assets/javascripts/activestorage.esm.js
|
106
107
|
- app/assets/javascripts/activestorage.js
|
107
108
|
- app/controllers/active_storage/base_controller.rb
|
108
109
|
- app/controllers/active_storage/blobs/proxy_controller.rb
|
@@ -115,7 +116,7 @@ files:
|
|
115
116
|
- app/controllers/concerns/active_storage/file_server.rb
|
116
117
|
- app/controllers/concerns/active_storage/set_blob.rb
|
117
118
|
- app/controllers/concerns/active_storage/set_current.rb
|
118
|
-
- app/controllers/concerns/active_storage/
|
119
|
+
- app/controllers/concerns/active_storage/streaming.rb
|
119
120
|
- app/javascript/activestorage/blob_record.js
|
120
121
|
- app/javascript/activestorage/blob_upload.js
|
121
122
|
- app/javascript/activestorage/direct_upload.js
|
@@ -146,9 +147,13 @@ files:
|
|
146
147
|
- db/migrate/20170806125915_create_active_storage_tables.rb
|
147
148
|
- db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb
|
148
149
|
- db/update_migrate/20191206030411_create_active_storage_variant_records.rb
|
150
|
+
- db/update_migrate/20211119233751_remove_not_null_on_active_storage_blobs_checksum.rb
|
149
151
|
- lib/active_storage.rb
|
150
152
|
- lib/active_storage/analyzer.rb
|
153
|
+
- lib/active_storage/analyzer/audio_analyzer.rb
|
151
154
|
- lib/active_storage/analyzer/image_analyzer.rb
|
155
|
+
- lib/active_storage/analyzer/image_analyzer/image_magick.rb
|
156
|
+
- lib/active_storage/analyzer/image_analyzer/vips.rb
|
152
157
|
- lib/active_storage/analyzer/null_analyzer.rb
|
153
158
|
- lib/active_storage/analyzer/video_analyzer.rb
|
154
159
|
- lib/active_storage/attached.rb
|
@@ -158,12 +163,18 @@ files:
|
|
158
163
|
- lib/active_storage/attached/changes/create_one_of_many.rb
|
159
164
|
- lib/active_storage/attached/changes/delete_many.rb
|
160
165
|
- lib/active_storage/attached/changes/delete_one.rb
|
166
|
+
- lib/active_storage/attached/changes/detach_many.rb
|
167
|
+
- lib/active_storage/attached/changes/detach_one.rb
|
168
|
+
- lib/active_storage/attached/changes/purge_many.rb
|
169
|
+
- lib/active_storage/attached/changes/purge_one.rb
|
161
170
|
- lib/active_storage/attached/many.rb
|
162
171
|
- lib/active_storage/attached/model.rb
|
163
172
|
- lib/active_storage/attached/one.rb
|
173
|
+
- lib/active_storage/direct_upload_token.rb
|
164
174
|
- lib/active_storage/downloader.rb
|
165
175
|
- lib/active_storage/engine.rb
|
166
176
|
- lib/active_storage/errors.rb
|
177
|
+
- lib/active_storage/fixture_set.rb
|
167
178
|
- lib/active_storage/gem_version.rb
|
168
179
|
- lib/active_storage/log_subscriber.rb
|
169
180
|
- lib/active_storage/previewer.rb
|
@@ -188,10 +199,11 @@ licenses:
|
|
188
199
|
- MIT
|
189
200
|
metadata:
|
190
201
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
191
|
-
changelog_uri: https://github.com/rails/rails/blob/
|
192
|
-
documentation_uri: https://api.rubyonrails.org/
|
202
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.0.0.rc1/activestorage/CHANGELOG.md
|
203
|
+
documentation_uri: https://api.rubyonrails.org/v7.0.0.rc1/
|
193
204
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
194
|
-
source_code_uri: https://github.com/rails/rails/tree/
|
205
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.0.0.rc1/activestorage
|
206
|
+
rubygems_mfa_required: 'true'
|
195
207
|
post_install_message:
|
196
208
|
rdoc_options: []
|
197
209
|
require_paths:
|
@@ -200,14 +212,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
212
|
requirements:
|
201
213
|
- - ">="
|
202
214
|
- !ruby/object:Gem::Version
|
203
|
-
version: 2.
|
215
|
+
version: 2.7.0
|
204
216
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
217
|
requirements:
|
206
|
-
- - "
|
218
|
+
- - ">"
|
207
219
|
- !ruby/object:Gem::Version
|
208
|
-
version:
|
220
|
+
version: 1.3.1
|
209
221
|
requirements: []
|
210
|
-
rubygems_version: 3.
|
222
|
+
rubygems_version: 3.2.22
|
211
223
|
signing_key:
|
212
224
|
specification_version: 4
|
213
225
|
summary: Local and cloud file storage framework.
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveStorage::SetHeaders #:nodoc:
|
4
|
-
extend ActiveSupport::Concern
|
5
|
-
|
6
|
-
private
|
7
|
-
def set_content_headers_from(blob)
|
8
|
-
response.headers["Content-Type"] = blob.content_type_for_serving
|
9
|
-
response.headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format \
|
10
|
-
disposition: blob.forced_disposition_for_serving || params[:disposition] || "inline", filename: blob.filename.sanitized
|
11
|
-
end
|
12
|
-
end
|