activestorage 6.0.3.4 → 6.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activestorage might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +144 -162
- data/MIT-LICENSE +1 -1
- data/README.md +35 -3
- data/app/controllers/active_storage/base_controller.rb +11 -0
- data/app/controllers/active_storage/blobs/proxy_controller.rb +14 -0
- data/app/controllers/active_storage/{blobs_controller.rb → blobs/redirect_controller.rb} +2 -2
- data/app/controllers/active_storage/disk_controller.rb +8 -20
- data/app/controllers/active_storage/representations/proxy_controller.rb +19 -0
- data/app/controllers/active_storage/{representations_controller.rb → representations/redirect_controller.rb} +2 -2
- data/app/controllers/concerns/active_storage/file_server.rb +18 -0
- data/app/controllers/concerns/active_storage/set_blob.rb +1 -1
- data/app/controllers/concerns/active_storage/set_current.rb +2 -2
- data/app/controllers/concerns/active_storage/set_headers.rb +12 -0
- data/app/jobs/active_storage/mirror_job.rb +15 -0
- data/app/models/active_storage/attachment.rb +18 -10
- data/app/models/active_storage/blob.rb +114 -59
- data/app/models/active_storage/blob/analyzable.rb +6 -2
- data/app/models/active_storage/blob/identifiable.rb +7 -6
- data/app/models/active_storage/blob/representable.rb +34 -4
- data/app/models/active_storage/preview.rb +31 -10
- data/app/models/active_storage/record.rb +7 -0
- data/app/models/active_storage/variant.rb +28 -41
- data/app/models/active_storage/variant_record.rb +8 -0
- data/app/models/active_storage/variant_with_record.rb +54 -0
- data/app/models/active_storage/variation.rb +25 -20
- data/config/routes.rb +58 -8
- data/db/migrate/20170806125915_create_active_storage_tables.rb +14 -5
- data/db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb +17 -0
- data/db/update_migrate/20191206030411_create_active_storage_variant_records.rb +11 -0
- data/lib/active_storage.rb +5 -2
- data/lib/active_storage/analyzer.rb +6 -0
- data/lib/active_storage/analyzer/image_analyzer.rb +3 -0
- data/lib/active_storage/analyzer/null_analyzer.rb +4 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +14 -3
- data/lib/active_storage/attached/changes/create_many.rb +1 -0
- data/lib/active_storage/attached/changes/create_one.rb +17 -4
- data/lib/active_storage/attached/many.rb +4 -3
- data/lib/active_storage/attached/model.rb +59 -12
- data/lib/active_storage/attached/one.rb +4 -3
- data/lib/active_storage/engine.rb +25 -27
- data/lib/active_storage/gem_version.rb +3 -3
- data/lib/active_storage/log_subscriber.rb +6 -0
- data/lib/active_storage/previewer.rb +3 -2
- data/lib/active_storage/previewer/mupdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +3 -3
- data/lib/active_storage/previewer/video_previewer.rb +2 -2
- data/lib/active_storage/service.rb +36 -7
- data/lib/active_storage/service/azure_storage_service.rb +40 -35
- data/lib/active_storage/service/configurator.rb +3 -1
- data/lib/active_storage/service/disk_service.rb +36 -31
- data/lib/active_storage/service/gcs_service.rb +18 -16
- data/lib/active_storage/service/mirror_service.rb +31 -7
- data/lib/active_storage/service/registry.rb +32 -0
- data/lib/active_storage/service/s3_service.rb +53 -23
- data/lib/active_storage/transformers/image_processing_transformer.rb +13 -7
- data/lib/active_storage/transformers/transformer.rb +0 -3
- metadata +57 -21
- data/db/update_migrate/20180723000244_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.rb +0 -9
- data/lib/active_storage/downloading.rb +0 -47
- data/lib/active_storage/transformers/mini_magick_transformer.rb +0 -38
| @@ -14,7 +14,9 @@ module ActiveStorage | |
| 14 14 |  | 
| 15 15 | 
             
                def build(service_name)
         | 
| 16 16 | 
             
                  config = config_for(service_name.to_sym)
         | 
| 17 | 
            -
                  resolve(config.fetch(:service)).build( | 
| 17 | 
            +
                  resolve(config.fetch(:service)).build(
         | 
| 18 | 
            +
                    **config, configurator: self, name: service_name
         | 
| 19 | 
            +
                  )
         | 
| 18 20 | 
             
                end
         | 
| 19 21 |  | 
| 20 22 | 
             
                private
         | 
| @@ -11,8 +11,9 @@ module ActiveStorage | |
| 11 11 | 
             
              class Service::DiskService < Service
         | 
| 12 12 | 
             
                attr_reader :root
         | 
| 13 13 |  | 
| 14 | 
            -
                def initialize(root:)
         | 
| 14 | 
            +
                def initialize(root:, public: false, **options)
         | 
| 15 15 | 
             
                  @root = root
         | 
| 16 | 
            +
                  @public = public
         | 
| 16 17 | 
             
                end
         | 
| 17 18 |  | 
| 18 19 | 
             
                def upload(key, io, checksum: nil, **)
         | 
| @@ -71,35 +72,6 @@ module ActiveStorage | |
| 71 72 | 
             
                  end
         | 
| 72 73 | 
             
                end
         | 
| 73 74 |  | 
| 74 | 
            -
                def url(key, expires_in:, filename:, disposition:, content_type:)
         | 
| 75 | 
            -
                  instrument :url, key: key do |payload|
         | 
| 76 | 
            -
                    content_disposition = content_disposition_with(type: disposition, filename: filename)
         | 
| 77 | 
            -
                    verified_key_with_expiration = ActiveStorage.verifier.generate(
         | 
| 78 | 
            -
                      {
         | 
| 79 | 
            -
                        key: key,
         | 
| 80 | 
            -
                        disposition: content_disposition,
         | 
| 81 | 
            -
                        content_type: content_type
         | 
| 82 | 
            -
                      },
         | 
| 83 | 
            -
                      expires_in: expires_in,
         | 
| 84 | 
            -
                      purpose: :blob_key
         | 
| 85 | 
            -
                    )
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                    current_uri = URI.parse(current_host)
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                    generated_url = url_helpers.rails_disk_service_url(verified_key_with_expiration,
         | 
| 90 | 
            -
                      protocol: current_uri.scheme,
         | 
| 91 | 
            -
                      host: current_uri.host,
         | 
| 92 | 
            -
                      port: current_uri.port,
         | 
| 93 | 
            -
                      disposition: content_disposition,
         | 
| 94 | 
            -
                      content_type: content_type,
         | 
| 95 | 
            -
                      filename: filename
         | 
| 96 | 
            -
                    )
         | 
| 97 | 
            -
                    payload[:url] = generated_url
         | 
| 98 | 
            -
             | 
| 99 | 
            -
                    generated_url
         | 
| 100 | 
            -
                  end
         | 
| 101 | 
            -
                end
         | 
| 102 | 
            -
             | 
| 103 75 | 
             
                def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
         | 
| 104 76 | 
             
                  instrument :url, key: key do |payload|
         | 
| 105 77 | 
             
                    verified_token_with_expiration = ActiveStorage.verifier.generate(
         | 
| @@ -107,7 +79,8 @@ module ActiveStorage | |
| 107 79 | 
             
                        key: key,
         | 
| 108 80 | 
             
                        content_type: content_type,
         | 
| 109 81 | 
             
                        content_length: content_length,
         | 
| 110 | 
            -
                        checksum: checksum
         | 
| 82 | 
            +
                        checksum: checksum,
         | 
| 83 | 
            +
                        service_name: name
         | 
| 111 84 | 
             
                      },
         | 
| 112 85 | 
             
                      expires_in: expires_in,
         | 
| 113 86 | 
             
                      purpose: :blob_token
         | 
| @@ -130,6 +103,38 @@ module ActiveStorage | |
| 130 103 | 
             
                end
         | 
| 131 104 |  | 
| 132 105 | 
             
                private
         | 
| 106 | 
            +
                  def private_url(key, expires_in:, filename:, content_type:, disposition:, **)
         | 
| 107 | 
            +
                    generate_url(key, expires_in: expires_in, filename: filename, content_type: content_type, disposition: disposition)
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def public_url(key, filename:, content_type: nil, disposition: :attachment, **)
         | 
| 111 | 
            +
                    generate_url(key, expires_in: nil, filename: filename, content_type: content_type, disposition: disposition)
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def generate_url(key, expires_in:, filename:, content_type:, disposition:)
         | 
| 115 | 
            +
                    content_disposition = content_disposition_with(type: disposition, filename: filename)
         | 
| 116 | 
            +
                    verified_key_with_expiration = ActiveStorage.verifier.generate(
         | 
| 117 | 
            +
                      {
         | 
| 118 | 
            +
                        key: key,
         | 
| 119 | 
            +
                        disposition: content_disposition,
         | 
| 120 | 
            +
                        content_type: content_type,
         | 
| 121 | 
            +
                        service_name: name
         | 
| 122 | 
            +
                      },
         | 
| 123 | 
            +
                      expires_in: expires_in,
         | 
| 124 | 
            +
                      purpose: :blob_key
         | 
| 125 | 
            +
                    )
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    current_uri = URI.parse(current_host)
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    url_helpers.rails_disk_service_url(verified_key_with_expiration,
         | 
| 130 | 
            +
                      protocol: current_uri.scheme,
         | 
| 131 | 
            +
                      host: current_uri.host,
         | 
| 132 | 
            +
                      port: current_uri.port,
         | 
| 133 | 
            +
                      filename: filename
         | 
| 134 | 
            +
                    )
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
             | 
| 133 138 | 
             
                  def stream(key)
         | 
| 134 139 | 
             
                    File.open(path_for(key), "rb") do |file|
         | 
| 135 140 | 
             
                      while data = file.read(5.megabytes)
         | 
| @@ -7,8 +7,9 @@ module ActiveStorage | |
| 7 7 | 
             
              # Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
         | 
| 8 8 | 
             
              # documentation that applies to all services.
         | 
| 9 9 | 
             
              class Service::GCSService < Service
         | 
| 10 | 
            -
                def initialize(**config)
         | 
| 10 | 
            +
                def initialize(public: false, **config)
         | 
| 11 11 | 
             
                  @config = config
         | 
| 12 | 
            +
                  @public = public
         | 
| 12 13 | 
             
                end
         | 
| 13 14 |  | 
| 14 15 | 
             
                def upload(key, io, checksum: nil, content_type: nil, disposition: nil, filename: nil)
         | 
| @@ -81,19 +82,6 @@ module ActiveStorage | |
| 81 82 | 
             
                  end
         | 
| 82 83 | 
             
                end
         | 
| 83 84 |  | 
| 84 | 
            -
                def url(key, expires_in:, filename:, content_type:, disposition:)
         | 
| 85 | 
            -
                  instrument :url, key: key do |payload|
         | 
| 86 | 
            -
                    generated_url = file_for(key).signed_url expires: expires_in, query: {
         | 
| 87 | 
            -
                      "response-content-disposition" => content_disposition_with(type: disposition, filename: filename),
         | 
| 88 | 
            -
                      "response-content-type" => content_type
         | 
| 89 | 
            -
                    }
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    payload[:url] = generated_url
         | 
| 92 | 
            -
             | 
| 93 | 
            -
                    generated_url
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
                end
         | 
| 96 | 
            -
             | 
| 97 85 | 
             
                def url_for_direct_upload(key, expires_in:, checksum:, **)
         | 
| 98 86 | 
             
                  instrument :url, key: key do |payload|
         | 
| 99 87 | 
             
                    generated_url = bucket.signed_url key, method: "PUT", expires: expires_in, content_md5: checksum
         | 
| @@ -104,11 +92,25 @@ module ActiveStorage | |
| 104 92 | 
             
                  end
         | 
| 105 93 | 
             
                end
         | 
| 106 94 |  | 
| 107 | 
            -
                def headers_for_direct_upload(key, checksum:, **)
         | 
| 108 | 
            -
                   | 
| 95 | 
            +
                def headers_for_direct_upload(key, checksum:, filename: nil, disposition: nil, **)
         | 
| 96 | 
            +
                  content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  { "Content-MD5" => checksum, "Content-Disposition" => content_disposition }
         | 
| 109 99 | 
             
                end
         | 
| 110 100 |  | 
| 111 101 | 
             
                private
         | 
| 102 | 
            +
                  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
         | 
| 106 | 
            +
                    }
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def public_url(key, **)
         | 
| 110 | 
            +
                    file_for(key).public_url
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
             | 
| 112 114 | 
             
                  attr_reader :config
         | 
| 113 115 |  | 
| 114 116 | 
             
                  def file_for(key, skip_lookup: true)
         | 
| @@ -4,18 +4,26 @@ require "active_support/core_ext/module/delegation" | |
| 4 4 |  | 
| 5 5 | 
             
            module ActiveStorage
         | 
| 6 6 | 
             
              # Wraps a set of mirror services and provides a single ActiveStorage::Service object that will all
         | 
| 7 | 
            -
              # have the files uploaded to them. A +primary+ service is designated to answer calls to | 
| 8 | 
            -
              #  | 
| 7 | 
            +
              # have the files uploaded to them. A +primary+ service is designated to answer calls to:
         | 
| 8 | 
            +
              # * +download+
         | 
| 9 | 
            +
              # * +exists?+
         | 
| 10 | 
            +
              # * +url+
         | 
| 11 | 
            +
              # * +url_for_direct_upload+
         | 
| 12 | 
            +
              # * +headers_for_direct_upload+
         | 
| 9 13 | 
             
              class Service::MirrorService < Service
         | 
| 10 14 | 
             
                attr_reader :primary, :mirrors
         | 
| 11 15 |  | 
| 12 | 
            -
                delegate :download, :download_chunk, :exist?, :url, | 
| 16 | 
            +
                delegate :download, :download_chunk, :exist?, :url,
         | 
| 17 | 
            +
                  :url_for_direct_upload, :headers_for_direct_upload, :path_for, to: :primary
         | 
| 13 18 |  | 
| 14 19 | 
             
                # Stitch together from named services.
         | 
| 15 | 
            -
                def self.build(primary:, mirrors:, configurator:, **options) #:nodoc:
         | 
| 16 | 
            -
                  new | 
| 20 | 
            +
                def self.build(primary:, mirrors:, name:, configurator:, **options) #:nodoc:
         | 
| 21 | 
            +
                  new(
         | 
| 17 22 | 
             
                    primary: configurator.build(primary),
         | 
| 18 | 
            -
                    mirrors: mirrors.collect { | | 
| 23 | 
            +
                    mirrors: mirrors.collect { |mirror_name| configurator.build mirror_name }
         | 
| 24 | 
            +
                  ).tap do |service_instance|
         | 
| 25 | 
            +
                    service_instance.name = name
         | 
| 26 | 
            +
                  end
         | 
| 19 27 | 
             
                end
         | 
| 20 28 |  | 
| 21 29 | 
             
                def initialize(primary:, mirrors:)
         | 
| @@ -26,7 +34,8 @@ module ActiveStorage | |
| 26 34 | 
             
                # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
         | 
| 27 35 | 
             
                def upload(key, io, checksum: nil, **options)
         | 
| 28 36 | 
             
                  each_service.collect do |service|
         | 
| 29 | 
            -
                     | 
| 37 | 
            +
                    io.rewind
         | 
| 38 | 
            +
                    service.upload key, io, checksum: checksum, **options
         | 
| 30 39 | 
             
                  end
         | 
| 31 40 | 
             
                end
         | 
| 32 41 |  | 
| @@ -40,6 +49,21 @@ module ActiveStorage | |
| 40 49 | 
             
                  perform_across_services :delete_prefixed, prefix
         | 
| 41 50 | 
             
                end
         | 
| 42 51 |  | 
| 52 | 
            +
             | 
| 53 | 
            +
                # Copy the file at the +key+ from the primary service to each of the mirrors where it doesn't already exist.
         | 
| 54 | 
            +
                def mirror(key, checksum:)
         | 
| 55 | 
            +
                  instrument :mirror, key: key, checksum: checksum do
         | 
| 56 | 
            +
                    if (mirrors_in_need_of_mirroring = mirrors.select { |service| !service.exist?(key) }).any?
         | 
| 57 | 
            +
                      primary.open(key, checksum: checksum) do |io|
         | 
| 58 | 
            +
                        mirrors_in_need_of_mirroring.each do |service|
         | 
| 59 | 
            +
                          io.rewind
         | 
| 60 | 
            +
                          service.upload key, io, checksum: checksum
         | 
| 61 | 
            +
                        end
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 43 67 | 
             
                private
         | 
| 44 68 | 
             
                  def each_service(&block)
         | 
| 45 69 | 
             
                    [ primary, *mirrors ].each(&block)
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveStorage
         | 
| 4 | 
            +
              class Service::Registry #:nodoc:
         | 
| 5 | 
            +
                def initialize(configurations)
         | 
| 6 | 
            +
                  @configurations = configurations.deep_symbolize_keys
         | 
| 7 | 
            +
                  @services = {}
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def fetch(name)
         | 
| 11 | 
            +
                  services.fetch(name.to_sym) do |key|
         | 
| 12 | 
            +
                    if configurations.include?(key)
         | 
| 13 | 
            +
                      services[key] = configurator.build(key)
         | 
| 14 | 
            +
                    else
         | 
| 15 | 
            +
                      if block_given?
         | 
| 16 | 
            +
                        yield key
         | 
| 17 | 
            +
                      else
         | 
| 18 | 
            +
                        raise KeyError, "Missing configuration for the #{key} Active Storage service. " \
         | 
| 19 | 
            +
                          "Configurations available for the #{configurations.keys.to_sentence} services."
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                private
         | 
| 26 | 
            +
                  attr_reader :configurations, :services
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def configurator
         | 
| 29 | 
            +
                    @configurator ||= ActiveStorage::Service::Configurator.new(configurations)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            gem "aws-sdk-s3", "~> 1.48"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            require "aws-sdk-s3"
         | 
| 4 6 | 
             
            require "active_support/core_ext/numeric/bytes"
         | 
| 5 7 |  | 
| @@ -7,20 +9,29 @@ module ActiveStorage | |
| 7 9 | 
             
              # Wraps the Amazon Simple Storage Service (S3) as an Active Storage service.
         | 
| 8 10 | 
             
              # See ActiveStorage::Service for the generic API documentation that applies to all services.
         | 
| 9 11 | 
             
              class Service::S3Service < Service
         | 
| 10 | 
            -
                attr_reader :client, :bucket | 
| 12 | 
            +
                attr_reader :client, :bucket
         | 
| 13 | 
            +
                attr_reader :multipart_upload_threshold, :upload_options
         | 
| 11 14 |  | 
| 12 | 
            -
                def initialize(bucket:, upload: {}, **options)
         | 
| 15 | 
            +
                def initialize(bucket:, upload: {}, public: false, **options)
         | 
| 13 16 | 
             
                  @client = Aws::S3::Resource.new(**options)
         | 
| 14 17 | 
             
                  @bucket = @client.bucket(bucket)
         | 
| 15 18 |  | 
| 19 | 
            +
                  @multipart_upload_threshold = upload.fetch(:multipart_threshold, 100.megabytes)
         | 
| 20 | 
            +
                  @public = public
         | 
| 21 | 
            +
             | 
| 16 22 | 
             
                  @upload_options = upload
         | 
| 23 | 
            +
                  @upload_options[:acl] = "public-read" if public?
         | 
| 17 24 | 
             
                end
         | 
| 18 25 |  | 
| 19 | 
            -
                def upload(key, io, checksum: nil, content_type: nil, **)
         | 
| 26 | 
            +
                def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, **)
         | 
| 20 27 | 
             
                  instrument :upload, key: key, checksum: checksum do
         | 
| 21 | 
            -
                     | 
| 22 | 
            -
             | 
| 23 | 
            -
                     | 
| 28 | 
            +
                    content_disposition = content_disposition_with(filename: filename, type: disposition) if disposition && filename
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    if io.size < multipart_upload_threshold
         | 
| 31 | 
            +
                      upload_with_single_part key, io, checksum: checksum, content_type: content_type, content_disposition: content_disposition
         | 
| 32 | 
            +
                    else
         | 
| 33 | 
            +
                      upload_with_multipart key, io, content_type: content_type, content_disposition: content_disposition
         | 
| 34 | 
            +
                    end
         | 
| 24 35 | 
             
                  end
         | 
| 25 36 | 
             
                end
         | 
| 26 37 |  | 
| @@ -40,7 +51,7 @@ module ActiveStorage | |
| 40 51 |  | 
| 41 52 | 
             
                def download_chunk(key, range)
         | 
| 42 53 | 
             
                  instrument :download_chunk, key: key, range: range do
         | 
| 43 | 
            -
                    object_for(key).get(range: "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body. | 
| 54 | 
            +
                    object_for(key).get(range: "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body.string.force_encoding(Encoding::BINARY)
         | 
| 44 55 | 
             
                  rescue Aws::S3::Errors::NoSuchKey
         | 
| 45 56 | 
             
                    raise ActiveStorage::FileNotFoundError
         | 
| 46 57 | 
             
                  end
         | 
| @@ -66,23 +77,11 @@ module ActiveStorage | |
| 66 77 | 
             
                  end
         | 
| 67 78 | 
             
                end
         | 
| 68 79 |  | 
| 69 | 
            -
                def url(key, expires_in:, filename:, disposition:, content_type:)
         | 
| 70 | 
            -
                  instrument :url, key: key do |payload|
         | 
| 71 | 
            -
                    generated_url = object_for(key).presigned_url :get, expires_in: expires_in.to_i,
         | 
| 72 | 
            -
                      response_content_disposition: content_disposition_with(type: disposition, filename: filename),
         | 
| 73 | 
            -
                      response_content_type: content_type
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                    payload[:url] = generated_url
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                    generated_url
         | 
| 78 | 
            -
                  end
         | 
| 79 | 
            -
                end
         | 
| 80 | 
            -
             | 
| 81 80 | 
             
                def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
         | 
| 82 81 | 
             
                  instrument :url, key: key do |payload|
         | 
| 83 82 | 
             
                    generated_url = object_for(key).presigned_url :put, expires_in: expires_in.to_i,
         | 
| 84 83 | 
             
                      content_type: content_type, content_length: content_length, content_md5: checksum,
         | 
| 85 | 
            -
                      whitelist_headers: [ | 
| 84 | 
            +
                      whitelist_headers: ["content-length"], **upload_options
         | 
| 86 85 |  | 
| 87 86 | 
             
                    payload[:url] = generated_url
         | 
| 88 87 |  | 
| @@ -90,11 +89,42 @@ module ActiveStorage | |
| 90 89 | 
             
                  end
         | 
| 91 90 | 
             
                end
         | 
| 92 91 |  | 
| 93 | 
            -
                def headers_for_direct_upload(key, content_type:, checksum:, **)
         | 
| 94 | 
            -
                   | 
| 92 | 
            +
                def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, **)
         | 
| 93 | 
            +
                  content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  { "Content-Type" => content_type, "Content-MD5" => checksum, "Content-Disposition" => content_disposition }
         | 
| 95 96 | 
             
                end
         | 
| 96 97 |  | 
| 97 98 | 
             
                private
         | 
| 99 | 
            +
                  def private_url(key, expires_in:, filename:, disposition:, content_type:, **)
         | 
| 100 | 
            +
                    object_for(key).presigned_url :get, expires_in: expires_in.to_i,
         | 
| 101 | 
            +
                      response_content_disposition: content_disposition_with(type: disposition, filename: filename),
         | 
| 102 | 
            +
                      response_content_type: content_type
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  def public_url(key, **)
         | 
| 106 | 
            +
                    object_for(key).public_url
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
             | 
| 110 | 
            +
                  MAXIMUM_UPLOAD_PARTS_COUNT = 10000
         | 
| 111 | 
            +
                  MINIMUM_UPLOAD_PART_SIZE   = 5.megabytes
         | 
| 112 | 
            +
             | 
| 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)
         | 
| 115 | 
            +
                  rescue Aws::S3::Errors::BadDigest
         | 
| 116 | 
            +
                    raise ActiveStorage::IntegrityError
         | 
| 117 | 
            +
                  end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  def upload_with_multipart(key, io, content_type: nil, content_disposition: nil)
         | 
| 120 | 
            +
                    part_size = [ io.size.fdiv(MAXIMUM_UPLOAD_PARTS_COUNT).ceil, MINIMUM_UPLOAD_PART_SIZE ].max
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    object_for(key).upload_stream(content_type: content_type, content_disposition: content_disposition, part_size: part_size, **upload_options) do |out|
         | 
| 123 | 
            +
                      IO.copy_stream(io, out)
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
             | 
| 98 128 | 
             
                  def object_for(key)
         | 
| 99 129 | 
             
                    bucket.object(key)
         | 
| 100 130 | 
             
                  end
         | 
| @@ -109,7 +139,7 @@ module ActiveStorage | |
| 109 139 | 
             
                    raise ActiveStorage::FileNotFoundError unless object.exists?
         | 
| 110 140 |  | 
| 111 141 | 
             
                    while offset < object.content_length
         | 
| 112 | 
            -
                      yield object.get(range: "bytes=#{offset}-#{offset + chunk_size - 1}").body. | 
| 142 | 
            +
                      yield object.get(range: "bytes=#{offset}-#{offset + chunk_size - 1}").body.string.force_encoding(Encoding::BINARY)
         | 
| 113 143 | 
             
                      offset += chunk_size
         | 
| 114 144 | 
             
                    end
         | 
| 115 145 | 
             
                  end
         | 
| @@ -1,6 +1,13 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              require "image_processing"
         | 
| 5 | 
            +
            rescue LoadError
         | 
| 6 | 
            +
              raise LoadError, <<~ERROR.squish
         | 
| 7 | 
            +
                Generating image variants require the image_processing gem.
         | 
| 8 | 
            +
                Please add `gem 'image_processing', '~> 1.2'` to your Gemfile.
         | 
| 9 | 
            +
              ERROR
         | 
| 10 | 
            +
            end
         | 
| 4 11 |  | 
| 5 12 | 
             
            module ActiveStorage
         | 
| 6 13 | 
             
              module Transformers
         | 
| @@ -22,14 +29,13 @@ module ActiveStorage | |
| 22 29 | 
             
                    def operations
         | 
| 23 30 | 
             
                      transformations.each_with_object([]) do |(name, argument), list|
         | 
| 24 31 | 
             
                        if name.to_s == "combine_options"
         | 
| 25 | 
            -
                           | 
| 32 | 
            +
                          raise ArgumentError, <<~ERROR.squish
         | 
| 26 33 | 
             
                            Active Storage's ImageProcessing transformer doesn't support :combine_options,
         | 
| 27 | 
            -
                            as it always generates a single ImageMagick command. | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 34 | 
            +
                            as it always generates a single ImageMagick command.
         | 
| 35 | 
            +
                          ERROR
         | 
| 36 | 
            +
                        end
         | 
| 30 37 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
                        elsif argument.present?
         | 
| 38 | 
            +
                        if argument.present?
         | 
| 33 39 | 
             
                          list << [ name, argument ]
         | 
| 34 40 | 
             
                        end
         | 
| 35 41 | 
             
                      end
         | 
| @@ -8,9 +8,6 @@ module ActiveStorage | |
| 8 8 | 
             
                #
         | 
| 9 9 | 
             
                # * ActiveStorage::Transformers::ImageProcessingTransformer:
         | 
| 10 10 | 
             
                #   backed by ImageProcessing, a common interface for MiniMagick and ruby-vips
         | 
| 11 | 
            -
                #
         | 
| 12 | 
            -
                # * ActiveStorage::Transformers::MiniMagickTransformer:
         | 
| 13 | 
            -
                #   backed by MiniMagick, a wrapper around the ImageMagick CLI
         | 
| 14 11 | 
             
                class Transformer
         | 
| 15 12 | 
             
                  attr_reader :transformations
         | 
| 16 13 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,57 +1,71 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: activestorage
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 6.0. | 
| 4 | 
            +
              version: 6.1.0.rc1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - David Heinemeier Hansson
         | 
| 8 | 
            -
            autorequire:
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-11-02 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: activesupport
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - '='
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 6.1.0.rc1
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - '='
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: 6.1.0.rc1
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: actionpack
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 30 | 
             
                requirements:
         | 
| 17 31 | 
             
                - - '='
         | 
| 18 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: 6.0. | 
| 33 | 
            +
                    version: 6.1.0.rc1
         | 
| 20 34 | 
             
              type: :runtime
         | 
| 21 35 | 
             
              prerelease: false
         | 
| 22 36 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 37 | 
             
                requirements:
         | 
| 24 38 | 
             
                - - '='
         | 
| 25 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: 6.0. | 
| 40 | 
            +
                    version: 6.1.0.rc1
         | 
| 27 41 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 42 | 
             
              name: activejob
         | 
| 29 43 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 44 | 
             
                requirements:
         | 
| 31 45 | 
             
                - - '='
         | 
| 32 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version: 6.0. | 
| 47 | 
            +
                    version: 6.1.0.rc1
         | 
| 34 48 | 
             
              type: :runtime
         | 
| 35 49 | 
             
              prerelease: false
         | 
| 36 50 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 51 | 
             
                requirements:
         | 
| 38 52 | 
             
                - - '='
         | 
| 39 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version: 6.0. | 
| 54 | 
            +
                    version: 6.1.0.rc1
         | 
| 41 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 56 | 
             
              name: activerecord
         | 
| 43 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 58 | 
             
                requirements:
         | 
| 45 59 | 
             
                - - '='
         | 
| 46 60 | 
             
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            -
                    version: 6.0. | 
| 61 | 
            +
                    version: 6.1.0.rc1
         | 
| 48 62 | 
             
              type: :runtime
         | 
| 49 63 | 
             
              prerelease: false
         | 
| 50 64 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 65 | 
             
                requirements:
         | 
| 52 66 | 
             
                - - '='
         | 
| 53 67 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            -
                    version: 6.0. | 
| 68 | 
            +
                    version: 6.1.0.rc1
         | 
| 55 69 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 56 70 | 
             
              name: marcel
         | 
| 57 71 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -66,6 +80,20 @@ dependencies: | |
| 66 80 | 
             
                - - "~>"
         | 
| 67 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 68 82 | 
             
                    version: 0.3.1
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: mimemagic
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - "~>"
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: 0.3.2
         | 
| 90 | 
            +
              type: :runtime
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - "~>"
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: 0.3.2
         | 
| 69 97 | 
             
            description: Attach cloud and local files in Rails applications.
         | 
| 70 98 | 
             
            email: david@loudthinking.com
         | 
| 71 99 | 
             
            executables: []
         | 
| @@ -77,12 +105,16 @@ files: | |
| 77 105 | 
             
            - README.md
         | 
| 78 106 | 
             
            - app/assets/javascripts/activestorage.js
         | 
| 79 107 | 
             
            - app/controllers/active_storage/base_controller.rb
         | 
| 80 | 
            -
            - app/controllers/active_storage/ | 
| 108 | 
            +
            - app/controllers/active_storage/blobs/proxy_controller.rb
         | 
| 109 | 
            +
            - app/controllers/active_storage/blobs/redirect_controller.rb
         | 
| 81 110 | 
             
            - app/controllers/active_storage/direct_uploads_controller.rb
         | 
| 82 111 | 
             
            - app/controllers/active_storage/disk_controller.rb
         | 
| 83 | 
            -
            - app/controllers/active_storage/ | 
| 112 | 
            +
            - app/controllers/active_storage/representations/proxy_controller.rb
         | 
| 113 | 
            +
            - app/controllers/active_storage/representations/redirect_controller.rb
         | 
| 114 | 
            +
            - app/controllers/concerns/active_storage/file_server.rb
         | 
| 84 115 | 
             
            - app/controllers/concerns/active_storage/set_blob.rb
         | 
| 85 116 | 
             
            - app/controllers/concerns/active_storage/set_current.rb
         | 
| 117 | 
            +
            - app/controllers/concerns/active_storage/set_headers.rb
         | 
| 86 118 | 
             
            - app/javascript/activestorage/blob_record.js
         | 
| 87 119 | 
             
            - app/javascript/activestorage/blob_upload.js
         | 
| 88 120 | 
             
            - app/javascript/activestorage/direct_upload.js
         | 
| @@ -94,6 +126,7 @@ files: | |
| 94 126 | 
             
            - app/javascript/activestorage/ujs.js
         | 
| 95 127 | 
             
            - app/jobs/active_storage/analyze_job.rb
         | 
| 96 128 | 
             
            - app/jobs/active_storage/base_job.rb
         | 
| 129 | 
            +
            - app/jobs/active_storage/mirror_job.rb
         | 
| 97 130 | 
             
            - app/jobs/active_storage/purge_job.rb
         | 
| 98 131 | 
             
            - app/models/active_storage/attachment.rb
         | 
| 99 132 | 
             
            - app/models/active_storage/blob.rb
         | 
| @@ -103,11 +136,15 @@ files: | |
| 103 136 | 
             
            - app/models/active_storage/current.rb
         | 
| 104 137 | 
             
            - app/models/active_storage/filename.rb
         | 
| 105 138 | 
             
            - app/models/active_storage/preview.rb
         | 
| 139 | 
            +
            - app/models/active_storage/record.rb
         | 
| 106 140 | 
             
            - app/models/active_storage/variant.rb
         | 
| 141 | 
            +
            - app/models/active_storage/variant_record.rb
         | 
| 142 | 
            +
            - app/models/active_storage/variant_with_record.rb
         | 
| 107 143 | 
             
            - app/models/active_storage/variation.rb
         | 
| 108 144 | 
             
            - config/routes.rb
         | 
| 109 145 | 
             
            - db/migrate/20170806125915_create_active_storage_tables.rb
         | 
| 110 | 
            -
            - db/update_migrate/ | 
| 146 | 
            +
            - db/update_migrate/20190112182829_add_service_name_to_active_storage_blobs.rb
         | 
| 147 | 
            +
            - db/update_migrate/20191206030411_create_active_storage_variant_records.rb
         | 
| 111 148 | 
             
            - lib/active_storage.rb
         | 
| 112 149 | 
             
            - lib/active_storage/analyzer.rb
         | 
| 113 150 | 
             
            - lib/active_storage/analyzer/image_analyzer.rb
         | 
| @@ -124,7 +161,6 @@ files: | |
| 124 161 | 
             
            - lib/active_storage/attached/model.rb
         | 
| 125 162 | 
             
            - lib/active_storage/attached/one.rb
         | 
| 126 163 | 
             
            - lib/active_storage/downloader.rb
         | 
| 127 | 
            -
            - lib/active_storage/downloading.rb
         | 
| 128 164 | 
             
            - lib/active_storage/engine.rb
         | 
| 129 165 | 
             
            - lib/active_storage/errors.rb
         | 
| 130 166 | 
             
            - lib/active_storage/gem_version.rb
         | 
| @@ -140,9 +176,9 @@ files: | |
| 140 176 | 
             
            - lib/active_storage/service/disk_service.rb
         | 
| 141 177 | 
             
            - lib/active_storage/service/gcs_service.rb
         | 
| 142 178 | 
             
            - lib/active_storage/service/mirror_service.rb
         | 
| 179 | 
            +
            - lib/active_storage/service/registry.rb
         | 
| 143 180 | 
             
            - lib/active_storage/service/s3_service.rb
         | 
| 144 181 | 
             
            - lib/active_storage/transformers/image_processing_transformer.rb
         | 
| 145 | 
            -
            - lib/active_storage/transformers/mini_magick_transformer.rb
         | 
| 146 182 | 
             
            - lib/active_storage/transformers/transformer.rb
         | 
| 147 183 | 
             
            - lib/active_storage/version.rb
         | 
| 148 184 | 
             
            - lib/tasks/activestorage.rake
         | 
| @@ -151,11 +187,11 @@ licenses: | |
| 151 187 | 
             
            - MIT
         | 
| 152 188 | 
             
            metadata:
         | 
| 153 189 | 
             
              bug_tracker_uri: https://github.com/rails/rails/issues
         | 
| 154 | 
            -
              changelog_uri: https://github.com/rails/rails/blob/v6.0. | 
| 155 | 
            -
              documentation_uri: https://api.rubyonrails.org/v6.0. | 
| 190 | 
            +
              changelog_uri: https://github.com/rails/rails/blob/v6.1.0.rc1/activestorage/CHANGELOG.md
         | 
| 191 | 
            +
              documentation_uri: https://api.rubyonrails.org/v6.1.0.rc1/
         | 
| 156 192 | 
             
              mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
         | 
| 157 | 
            -
              source_code_uri: https://github.com/rails/rails/tree/v6.0. | 
| 158 | 
            -
            post_install_message:
         | 
| 193 | 
            +
              source_code_uri: https://github.com/rails/rails/tree/v6.1.0.rc1/activestorage
         | 
| 194 | 
            +
            post_install_message: 
         | 
| 159 195 | 
             
            rdoc_options: []
         | 
| 160 196 | 
             
            require_paths:
         | 
| 161 197 | 
             
            - lib
         | 
| @@ -166,12 +202,12 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 166 202 | 
             
                  version: 2.5.0
         | 
| 167 203 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 168 204 | 
             
              requirements:
         | 
| 169 | 
            -
              - - " | 
| 205 | 
            +
              - - ">"
         | 
| 170 206 | 
             
                - !ruby/object:Gem::Version
         | 
| 171 | 
            -
                  version:  | 
| 207 | 
            +
                  version: 1.3.1
         | 
| 172 208 | 
             
            requirements: []
         | 
| 173 209 | 
             
            rubygems_version: 3.1.4
         | 
| 174 | 
            -
            signing_key:
         | 
| 210 | 
            +
            signing_key: 
         | 
| 175 211 | 
             
            specification_version: 4
         | 
| 176 212 | 
             
            summary: Local and cloud file storage framework.
         | 
| 177 213 | 
             
            test_files: []
         |