activestorage 6.1.4.1 → 7.0.0.rc2

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.

Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +144 -204
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +25 -11
  5. data/app/assets/javascripts/activestorage.esm.js +856 -0
  6. data/app/assets/javascripts/activestorage.js +270 -377
  7. data/app/controllers/active_storage/base_controller.rb +1 -10
  8. data/app/controllers/active_storage/blobs/proxy_controller.rb +14 -4
  9. data/app/controllers/active_storage/blobs/redirect_controller.rb +6 -4
  10. data/app/controllers/active_storage/direct_uploads_controller.rb +7 -1
  11. data/app/controllers/active_storage/disk_controller.rb +1 -0
  12. data/app/controllers/active_storage/representations/base_controller.rb +5 -1
  13. data/app/controllers/active_storage/representations/proxy_controller.rb +6 -4
  14. data/app/controllers/active_storage/representations/redirect_controller.rb +6 -4
  15. data/app/controllers/concerns/active_storage/set_blob.rb +6 -2
  16. data/app/controllers/concerns/active_storage/set_current.rb +3 -3
  17. data/app/controllers/concerns/active_storage/streaming.rb +65 -0
  18. data/app/javascript/activestorage/blob_record.js +10 -3
  19. data/app/javascript/activestorage/direct_upload.js +4 -2
  20. data/app/javascript/activestorage/direct_upload_controller.js +9 -1
  21. data/app/javascript/activestorage/ujs.js +1 -1
  22. data/app/models/active_storage/attachment.rb +36 -3
  23. data/app/models/active_storage/blob/representable.rb +7 -5
  24. data/app/models/active_storage/blob.rb +92 -36
  25. data/app/models/active_storage/current.rb +12 -2
  26. data/app/models/active_storage/preview.rb +6 -4
  27. data/app/models/active_storage/record.rb +1 -1
  28. data/app/models/active_storage/variant.rb +3 -6
  29. data/app/models/active_storage/variant_record.rb +2 -0
  30. data/app/models/active_storage/variant_with_record.rb +9 -5
  31. data/app/models/active_storage/variation.rb +2 -2
  32. data/config/routes.rb +10 -10
  33. data/db/migrate/20170806125915_create_active_storage_tables.rb +32 -11
  34. data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +15 -2
  35. data/db/update_migrate/20211119233751_remove_not_null_on_active_storage_blobs_checksum.rb +5 -0
  36. data/lib/active_storage/analyzer/audio_analyzer.rb +65 -0
  37. data/lib/active_storage/analyzer/image_analyzer/image_magick.rb +39 -0
  38. data/lib/active_storage/analyzer/image_analyzer/vips.rb +49 -0
  39. data/lib/active_storage/analyzer/image_analyzer.rb +2 -30
  40. data/lib/active_storage/analyzer/video_analyzer.rb +26 -11
  41. data/lib/active_storage/analyzer.rb +8 -4
  42. data/lib/active_storage/attached/changes/create_many.rb +7 -3
  43. data/lib/active_storage/attached/changes/create_one.rb +1 -1
  44. data/lib/active_storage/attached/changes/create_one_of_many.rb +1 -1
  45. data/lib/active_storage/attached/changes/delete_many.rb +1 -1
  46. data/lib/active_storage/attached/changes/delete_one.rb +1 -1
  47. data/lib/active_storage/attached/changes/detach_many.rb +18 -0
  48. data/lib/active_storage/attached/changes/detach_one.rb +24 -0
  49. data/lib/active_storage/attached/changes/purge_many.rb +27 -0
  50. data/lib/active_storage/attached/changes/purge_one.rb +27 -0
  51. data/lib/active_storage/attached/changes.rb +7 -1
  52. data/lib/active_storage/attached/many.rb +27 -15
  53. data/lib/active_storage/attached/model.rb +31 -5
  54. data/lib/active_storage/attached/one.rb +32 -27
  55. data/lib/active_storage/direct_upload_token.rb +59 -0
  56. data/lib/active_storage/downloader.rb +4 -4
  57. data/lib/active_storage/engine.rb +30 -1
  58. data/lib/active_storage/errors.rb +3 -0
  59. data/lib/active_storage/fixture_set.rb +76 -0
  60. data/lib/active_storage/gem_version.rb +4 -4
  61. data/lib/active_storage/previewer.rb +4 -4
  62. data/lib/active_storage/reflection.rb +12 -2
  63. data/lib/active_storage/service/azure_storage_service.rb +28 -6
  64. data/lib/active_storage/service/configurator.rb +1 -1
  65. data/lib/active_storage/service/disk_service.rb +24 -19
  66. data/lib/active_storage/service/gcs_service.rb +109 -11
  67. data/lib/active_storage/service/mirror_service.rb +2 -2
  68. data/lib/active_storage/service/registry.rb +1 -1
  69. data/lib/active_storage/service/s3_service.rb +37 -15
  70. data/lib/active_storage/service.rb +13 -5
  71. data/lib/active_storage/transformers/image_processing_transformer.rb +1 -1
  72. data/lib/active_storage/transformers/transformer.rb +1 -1
  73. data/lib/active_storage.rb +6 -1
  74. metadata +31 -19
  75. 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
- generated_url = bucket.signed_url key, method: "PUT", expires: expires_in, content_md5: checksum
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
- file_for(key).signed_url expires: expires_in, query: {
104
- "response-content-disposition" => content_disposition_with(type: disposition, filename: filename),
105
- "response-content-type" => content_type
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) #:nodoc:
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 }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveStorage
4
- class Service::Registry #:nodoc:
4
+ class Service::Registry # :nodoc:
5
5
  def initialize(configurations)
6
6
  @configurations = configurations.deep_symbolize_keys
7
7
  @services = {}
@@ -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
- # :Disk,
39
- # root: Pathname("/foo/bar/storage")
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) #:nodoc:
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 ImageMagick command.
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:) #:doc:
34
+ def process(file, format:) # :doc:
35
35
  raise NotImplementedError
36
36
  end
37
37
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2017-2020 David Heinemeier Hansson, Basecamp
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: 6.1.4.1
4
+ version: 7.0.0.rc2
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-08-19 00:00:00.000000000 Z
11
+ date: 2021-12-14 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: 6.1.4.1
19
+ version: 7.0.0.rc2
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: 6.1.4.1
26
+ version: 7.0.0.rc2
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: 6.1.4.1
33
+ version: 7.0.0.rc2
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: 6.1.4.1
40
+ version: 7.0.0.rc2
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: 6.1.4.1
47
+ version: 7.0.0.rc2
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: 6.1.4.1
54
+ version: 7.0.0.rc2
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: 6.1.4.1
61
+ version: 7.0.0.rc2
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: 6.1.4.1
68
+ version: 7.0.0.rc2
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.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.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/set_headers.rb
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/v6.1.4.1/activestorage/CHANGELOG.md
192
- documentation_uri: https://api.rubyonrails.org/v6.1.4.1/
202
+ changelog_uri: https://github.com/rails/rails/blob/v7.0.0.rc2/activestorage/CHANGELOG.md
203
+ documentation_uri: https://api.rubyonrails.org/v7.0.0.rc2/
193
204
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
194
- source_code_uri: https://github.com/rails/rails/tree/v6.1.4.1/activestorage
205
+ source_code_uri: https://github.com/rails/rails/tree/v7.0.0.rc2/activestorage
206
+ rubygems_mfa_required: 'true'
195
207
  post_install_message:
196
208
  rdoc_options: []
197
209
  require_paths:
@@ -200,12 +212,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
212
  requirements:
201
213
  - - ">="
202
214
  - !ruby/object:Gem::Version
203
- version: 2.5.0
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: '0'
220
+ version: 1.3.1
209
221
  requirements: []
210
222
  rubygems_version: 3.2.15
211
223
  signing_key:
@@ -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