diff options
Diffstat (limited to 'activestorage')
-rw-r--r-- | activestorage/CHANGELOG.md | 87 | ||||
-rw-r--r-- | activestorage/config/routes.rb | 2 | ||||
-rw-r--r-- | activestorage/lib/active_storage.rb | 19 | ||||
-rw-r--r-- | activestorage/lib/active_storage/attached/model.rb | 17 | ||||
-rw-r--r-- | activestorage/lib/active_storage/engine.rb | 3 | ||||
-rw-r--r-- | activestorage/lib/active_storage/service/azure_storage_service.rb | 4 | ||||
-rw-r--r-- | activestorage/lib/active_storage/service/disk_service.rb | 6 | ||||
-rw-r--r-- | activestorage/test/analyzer/video_analyzer_test.rb | 1 | ||||
-rw-r--r-- | activestorage/test/models/attached/many_test.rb | 26 | ||||
-rw-r--r-- | activestorage/test/service/azure_storage_service_test.rb | 14 | ||||
-rw-r--r-- | activestorage/test/service/disk_service_test.rb | 10 |
11 files changed, 131 insertions, 58 deletions
diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md index 2d9fe56858..1475a7a786 100644 --- a/activestorage/CHANGELOG.md +++ b/activestorage/CHANGELOG.md @@ -1,59 +1,64 @@ -* Image analysis is skipped if ImageMagick returns an error. +* Add `config.active_storage.draw_routes` to disable Active Storage routes. - `ActiveStorage::Analyzer::ImageAnalyzer#metadata` would previously raise a - `MiniMagick::Error`, which caused persistent `ActiveStorage::AnalyzeJob` - failures. It now logs the error and returns `{}`, resulting in no metadata - being added to the offending image blob. + *Gannon McGibbon* - *George Claghorn* +* Image analysis is skipped if ImageMagick returns an error. -* Method calls on singular attachments return `nil` when no file is attached. + `ActiveStorage::Analyzer::ImageAnalyzer#metadata` would previously raise a + `MiniMagick::Error`, which caused persistent `ActiveStorage::AnalyzeJob` + failures. It now logs the error and returns `{}`, resulting in no metadata + being added to the offending image blob. - Previously, assuming the following User model, `user.avatar.filename` would - raise a `Module::DelegationError` if no avatar was attached: + *George Claghorn* - ```ruby - class User < ApplicationRecord - has_one_attached :avatar - end - ``` +* Method calls on singular attachments return `nil` when no file is attached. - They now return `nil`. + Previously, assuming the following User model, `user.avatar.filename` would + raise a `Module::DelegationError` if no avatar was attached: - *Matthew Tanous* + ```ruby + class User < ApplicationRecord + has_one_attached :avatar + end + ``` -* The mirror service supports direct uploads. + They now return `nil`. - New files are directly uploaded to the primary service. When a - directly-uploaded file is attached to a record, a background job is enqueued - to copy it to each secondary service. + *Matthew Tanous* - Configure the queue used to process mirroring jobs by setting - `config.active_storage.queues.mirror`. The default is `:active_storage_mirror`. +* The mirror service supports direct uploads. - *George Claghorn* + New files are directly uploaded to the primary service. When a + directly-uploaded file is attached to a record, a background job is enqueued + to copy it to each secondary service. -* The S3 service now permits uploading files larger than 5 gigabytes. + Configure the queue used to process mirroring jobs by setting + `config.active_storage.queues.mirror`. The default is `:active_storage_mirror`. - When uploading a file greater than 100 megabytes in size, the service - transparently switches to [multipart uploads](https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html) - using a part size computed from the file's total size and S3's part count limit. + *George Claghorn* - No application changes are necessary to take advantage of this feature. You - can customize the default 100 MB multipart upload threshold in your S3 - service's configuration: +* The S3 service now permits uploading files larger than 5 gigabytes. - ```yaml - production: - service: s3 - access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> - secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> - region: us-east-1 - bucket: my-bucket - upload: - multipart_threshold: <%= 250.megabytes %> - ``` + When uploading a file greater than 100 megabytes in size, the service + transparently switches to [multipart uploads](https://docs.aws.amazon.com/AmazonS3/latest/dev/mpuoverview.html) + using a part size computed from the file's total size and S3's part count limit. + + No application changes are necessary to take advantage of this feature. You + can customize the default 100 MB multipart upload threshold in your S3 + service's configuration: + + ```yaml + production: + service: s3 + access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> + secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> + region: us-east-1 + bucket: my-bucket + upload: + multipart_threshold: <%= 250.megabytes %> + ``` + + *George Claghorn* - *George Claghorn* Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/activestorage/CHANGELOG.md) for previous changes. diff --git a/activestorage/config/routes.rb b/activestorage/config/routes.rb index 3af7361cff..bde53e72f3 100644 --- a/activestorage/config/routes.rb +++ b/activestorage/config/routes.rb @@ -29,4 +29,4 @@ Rails.application.routes.draw do resolve("ActiveStorage::Blob") { |blob, options| route_for(:rails_blob, blob, options) } resolve("ActiveStorage::Attachment") { |attachment, options| route_for(:rails_blob, attachment.blob, options) } -end +end if ActiveStorage.draw_routes diff --git a/activestorage/lib/active_storage.rb b/activestorage/lib/active_storage.rb index 5c5da551ae..c35a9920d6 100644 --- a/activestorage/lib/active_storage.rb +++ b/activestorage/lib/active_storage.rb @@ -43,17 +43,26 @@ module ActiveStorage mattr_accessor :logger mattr_accessor :verifier + mattr_accessor :variant_processor, default: :mini_magick + mattr_accessor :queues, default: {} + mattr_accessor :previewers, default: [] - mattr_accessor :analyzers, default: [] - mattr_accessor :variant_processor, default: :mini_magick + mattr_accessor :analyzers, default: [] + mattr_accessor :paths, default: {} - mattr_accessor :variable_content_types, default: [] + + mattr_accessor :variable_content_types, default: [] + mattr_accessor :binary_content_type, default: "application/octet-stream" mattr_accessor :content_types_to_serve_as_binary, default: [] - mattr_accessor :content_types_allowed_inline, default: [] - mattr_accessor :binary_content_type, default: "application/octet-stream" + mattr_accessor :content_types_allowed_inline, default: [] + mattr_accessor :service_urls_expire_in, default: 5.minutes + mattr_accessor :routes_prefix, default: "/rails/active_storage" + mattr_accessor :draw_routes, default: true + + mattr_accessor :replace_on_assign_to_many, default: false module Transformers extend ActiveSupport::Autoload diff --git a/activestorage/lib/active_storage/attached/model.rb b/activestorage/lib/active_storage/attached/model.rb index ae7f0685f2..06864a846f 100644 --- a/activestorage/lib/active_storage/attached/model.rb +++ b/activestorage/lib/active_storage/attached/model.rb @@ -93,12 +93,19 @@ module ActiveStorage end def #{name}=(attachables) - attachment_changes["#{name}"] = - if attachables.nil? || Array(attachables).none? - ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self) - else - ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables) + if ActiveStorage.replace_on_assign_to_many + attachment_changes["#{name}"] = + if attachables.nil? || Array(attachables).none? + ActiveStorage::Attached::Changes::DeleteMany.new("#{name}", self) + else + ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, attachables) + end + else + if !attachables.nil? || Array(attachables).any? + attachment_changes["#{name}"] = + ActiveStorage::Attached::Changes::CreateMany.new("#{name}", self, #{name}.blobs + attachables) end + end end CODE diff --git a/activestorage/lib/active_storage/engine.rb b/activestorage/lib/active_storage/engine.rb index f70d0a512a..9d9cd02d12 100644 --- a/activestorage/lib/active_storage/engine.rb +++ b/activestorage/lib/active_storage/engine.rb @@ -73,12 +73,15 @@ module ActiveStorage ActiveStorage.analyzers = app.config.active_storage.analyzers || [] ActiveStorage.paths = app.config.active_storage.paths || {} ActiveStorage.routes_prefix = app.config.active_storage.routes_prefix || "/rails/active_storage" + ActiveStorage.draw_routes = app.config.active_storage.draw_routes != false ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || [] ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || [] ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || [] ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream" + + ActiveStorage.replace_on_assign_to_many = app.config.active_storage.replace_on_assign_to_many || false end end diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index 993cc0e5f7..8d77e9b20f 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -17,10 +17,10 @@ module ActiveStorage @container = container end - def upload(key, io, checksum: nil, **) + def upload(key, io, checksum: nil, content_type: nil, **) instrument :upload, key: key, checksum: checksum do handle_errors do - blobs.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum) + blobs.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum, content_type: content_type) end end end diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index 67892d43b2..764a447c69 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -84,8 +84,12 @@ module ActiveStorage purpose: :blob_key } ) + current_uri = URI.parse(current_host) + generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration, - host: current_host, + protocol: current_uri.scheme, + host: current_uri.host, + port: current_uri.port, disposition: content_disposition, content_type: content_type, filename: filename diff --git a/activestorage/test/analyzer/video_analyzer_test.rb b/activestorage/test/analyzer/video_analyzer_test.rb index d30f49315a..172a2f0aae 100644 --- a/activestorage/test/analyzer/video_analyzer_test.rb +++ b/activestorage/test/analyzer/video_analyzer_test.rb @@ -24,7 +24,6 @@ class ActiveStorage::Analyzer::VideoAnalyzerTest < ActiveSupport::TestCase assert_equal 480, metadata[:width] assert_equal 640, metadata[:height] assert_equal [4, 3], metadata[:display_aspect_ratio] - assert_equal 5.227975, metadata[:duration] assert_equal 90, metadata[:angle] end diff --git a/activestorage/test/models/attached/many_test.rb b/activestorage/test/models/attached/many_test.rb index 878e284049..39ddecb041 100644 --- a/activestorage/test/models/attached/many_test.rb +++ b/activestorage/test/models/attached/many_test.rb @@ -269,6 +269,24 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase end end + test "updating an existing record with attachments when appending on assign" do + append_on_assign do + @user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") + + assert_difference -> { @user.reload.highlights.count }, +2 do + @user.update! highlights: [ create_blob(filename: "whenever.jpg"), create_blob(filename: "wherever.jpg") ] + end + + assert_no_difference -> { @user.reload.highlights.count } do + @user.update! highlights: [ ] + end + + assert_no_difference -> { @user.reload.highlights.count } do + @user.update! highlights: nil + end + end + end + test "attaching existing blobs to a new record" do User.new(name: "Jason").tap do |user| user.highlights.attach create_blob(filename: "funky.jpg"), create_blob(filename: "town.jpg") @@ -538,4 +556,12 @@ class ActiveStorage::ManyAttachedTest < ActiveSupport::TestCase User.remove_method :highlights end end + + private + def append_on_assign + ActiveStorage.replace_on_assign_to_many, previous = false, ActiveStorage.replace_on_assign_to_many + yield + ensure + ActiveStorage.replace_on_assign_to_many = previous + end end diff --git a/activestorage/test/service/azure_storage_service_test.rb b/activestorage/test/service/azure_storage_service_test.rb index 2b07902d07..fc7b86ccb0 100644 --- a/activestorage/test/service/azure_storage_service_test.rb +++ b/activestorage/test/service/azure_storage_service_test.rb @@ -9,6 +9,20 @@ if SERVICE_CONFIGURATIONS[:azure] include ActiveStorage::Service::SharedServiceTests + test "upload with content_type" do + key = SecureRandom.base58(24) + data = "Foobar" + + @service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data), filename: ActiveStorage::Filename.new("test.txt"), content_type: "text/plain") + + url = @service.url(key, expires_in: 2.minutes, disposition: :attachment, content_type: nil, filename: ActiveStorage::Filename.new("test.html")) + response = Net::HTTP.get_response(URI(url)) + assert_equal "text/plain", response.content_type + assert_match(/attachment;.*test\.html/, response["Content-Disposition"]) + ensure + @service.delete key + end + test "signed URL generation" do url = @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png") diff --git a/activestorage/test/service/disk_service_test.rb b/activestorage/test/service/disk_service_test.rb index f3c4dd26bd..b766cc3f56 100644 --- a/activestorage/test/service/disk_service_test.rb +++ b/activestorage/test/service/disk_service_test.rb @@ -8,8 +8,14 @@ class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase include ActiveStorage::Service::SharedServiceTests test "URL generation" do - assert_match(/^https:\/\/example.com\/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/, - @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")) + original_url_options = Rails.application.routes.default_url_options.dup + Rails.application.routes.default_url_options.merge!(protocol: "http", host: "test.example.com", port: 3001) + begin + assert_match(/^https:\/\/example.com\/rails\/active_storage\/disk\/.*\/avatar\.png\?content_type=image%2Fpng&disposition=inline/, + @service.url(@key, expires_in: 5.minutes, disposition: :inline, filename: ActiveStorage::Filename.new("avatar.png"), content_type: "image/png")) + ensure + Rails.application.routes.default_url_options = original_url_options + end end test "headers_for_direct_upload generation" do |