diff --git a/gems/activestorage/8.1/_test/test.rb b/gems/activestorage/8.1/_test/test.rb
new file mode 100644
index 000000000..a064d78aa
--- /dev/null
+++ b/gems/activestorage/8.1/_test/test.rb
@@ -0,0 +1,41 @@
+class User < ActiveRecord::Base
+ has_one_attached :one_image
+ has_many_attached :many_image
+ has_one_attached :avatar do |attachable|
+ attachable.variant :thumb, resize_to_limit: [100, 100]
+ end
+ has_many_attached :photos do |attachable|
+ attachable.named_variants
+ end
+end
+
+user = User.new
+one = ActiveStorage::Attached::One.new("one_image", user)
+one.url
+ActiveStorage::Blob.new.signed_id(purpose: :download).size
+
+blob = ActiveStorage::Blob.new
+blob.content_type&.start_with?("image/")
+blob.byte_size + 1
+blob.checksum&.size
+blob.metadata["width"]
+blob.service_name.to_sym
+blob.created_at.to_date
+attachment = ActiveStorage::Attachment.new
+attachment.name.to_sym
+attachment.blob.byte_size
+attachment.created_at.to_date
+
+blob.key.size
+blob.filename.extension.upcase
+blob.filename.base.upcase
+blob.filename.to_s.upcase
+blob.url(expires_in: 5.minutes).size
+blob.service_url_for_direct_upload.size
+blob.service_headers_for_direct_upload.each_value(&:size)
+blob.image? || blob.video?
+
+ActiveStorage::Variant.new(blob, { resize_to_limit: [100, 100] }).download
+ActiveStorage::Preview.new(blob, { resize_to_limit: [100, 100] }).download
+
+ActiveStorage::FixtureSet.generate_unique_secure_token(length: 28)
diff --git a/gems/activestorage/8.1/_test/test.rbs b/gems/activestorage/8.1/_test/test.rbs
new file mode 100644
index 000000000..4a57cf079
--- /dev/null
+++ b/gems/activestorage/8.1/_test/test.rbs
@@ -0,0 +1,2 @@
+class User < ActiveRecord::Base
+end
diff --git a/gems/activestorage/8.1/activestorage-generated.rbs b/gems/activestorage/8.1/activestorage-generated.rbs
new file mode 100644
index 000000000..26c268407
--- /dev/null
+++ b/gems/activestorage/8.1/activestorage-generated.rbs
@@ -0,0 +1,2473 @@
+# The generated code is based on Ruby on Rails source code
+# You can find the license of Ruby on Rails from following.
+
+#Copyright (c) David Heinemeier Hansson, 37signals LLC
+#
+#Permission is hereby granted, free of charge, to any person obtaining
+#a copy of this software and associated documentation files (the
+#"Software"), to deal in the Software without restriction, including
+#without limitation the rights to use, copy, modify, merge, publish,
+#distribute, sublicense, and/or sell copies of the Software, and to
+#permit persons to whom the Software is furnished to do so, subject to
+#the following conditions:
+#
+#The above copyright notice and this permission notice shall be
+#included in all copies or substantial portions of the Software.
+#
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+#NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+#LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+#WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# The base class for all Active Storage controllers.
+class ActiveStorage::BaseController < ::ActionController::Base
+ include ::ActiveStorage::SetCurrent
+end
+
+# Finds a blob by a +signed_id+ and proxies the file through the application.
+# The blob is streamed from storage directly to the response. This avoids having
+# a redirect and makes files easier to cache.
+#
+# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
+# The response sets the HTTP cache to public and allows browsers and proxies to cache it indefinitely.
+#
+# The URLs created for this controller are set to never expire by default.
+# To make URLs expire, pass the +expires_in+ option when generating the URL:
+#
+# rails_storage_proxy_url(blob, expires_in: 1.minute)
+#
+# Or set the default for all Active Storage URLs:
+#
+# config.active_storage.urls_expire_in = 1.day
+#
+# WARNING: All Active Storage controllers are publicly accessible by default.
+# Anyone who knows the URL can access the file, even if the rest of your application requires
+# authentication. If your files require access control consider implementing
+# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
+class ActiveStorage::Blobs::ProxyController < ::ActiveStorage::BaseController
+ extend ::ActionController::Live::ClassMethods
+
+ include ::ActiveStorage::SetBlob
+
+ include ::ActiveStorage::Streaming
+
+ include ::ActiveStorage::DisableSession
+
+ def show: () -> untyped
+end
+
+# Finds a blob by a +signed_id+ and redirects to a blob's expiring service URL.
+#
+# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
+#
+# The URLs created for this controller are set to never expire by default.
+# To make URLs expire, pass the +expires_in+ option when generating the URL:
+#
+# rails_storage_redirect_url(blob, expires_in: 1.minute)
+#
+# Or set the default for all Active Storage URLs:
+#
+# config.active_storage.urls_expire_in = 1.day
+#
+# The service URLs are set to expire in 5 minutes by default.
+# The default can be changed for all service URLs:
+#
+# config.active_storage.service_urls_expire_in = 1.hour
+#
+# WARNING: All Active Storage controllers are publicly accessible by default.
+# Anyone who knows the URL can access the file, even if the rest of your application requires
+# authentication. If your files require access control consider implementing
+# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
+class ActiveStorage::Blobs::RedirectController < ::ActiveStorage::BaseController
+ include ::ActiveStorage::SetBlob
+
+ def show: () -> untyped
+end
+
+# Creates a new blob on the server side in anticipation of a direct-to-service upload from the client side.
+# When the client-side upload is completed, the signed_blob_id can be submitted as part of the form to reference
+# the blob that was created up front.
+class ActiveStorage::DirectUploadsController < ::ActiveStorage::BaseController
+ def create: () -> untyped
+
+ private
+
+ def blob_args: () -> untyped
+
+ def direct_upload_json: (untyped blob) -> untyped
+end
+
+# Serves files stored with the disk service in the same way that the cloud services do.
+# This means using expiring, signed URLs that are meant for immediate access, not permanent linking.
+# Always go through the BlobsController, or your own authenticated controller, rather than directly
+# to the service URL.
+class ActiveStorage::DiskController < ::ActiveStorage::BaseController
+ include ::ActiveStorage::FileServer
+
+ def show: () -> untyped
+
+ def update: () -> untyped
+
+ private
+
+ def named_disk_service: (untyped name) -> untyped
+
+ def decode_verified_key: () -> untyped
+
+ def decode_verified_token: () -> untyped
+
+ def acceptable_content?: (untyped token) -> untyped
+end
+
+class ActiveStorage::Representations::BaseController < ::ActiveStorage::BaseController
+ include ::ActiveStorage::SetBlob
+
+ private
+
+ def blob_scope: () -> untyped
+
+ def set_representation: () -> untyped
+end
+
+# Finds a representation by a +signed_id+ and a +variation_key+, and proxies the file through the application.
+# The representation is streamed from storage directly to the response. This avoids having
+# a redirect and makes files easier to cache.
+#
+# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
+# The response sets the HTTP cache to public and allows browsers and proxies to cache it indefinitely.
+#
+# The URLs created for this controller are set to never expire by default.
+# To make URLs expire, pass the +expires_in+ option when generating the URL:
+#
+# rails_storage_proxy_url(representation, expires_in: 1.minute)
+#
+# Or set the default for all Active Storage URLs:
+#
+# config.active_storage.urls_expire_in = 1.day
+#
+# WARNING: All Active Storage controllers are publicly accessible by default.
+# Anyone who knows the URL can access the file, even if the rest of your application requires
+# authentication. If your files require access control consider implementing
+# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
+class ActiveStorage::Representations::ProxyController < ::ActiveStorage::Representations::BaseController
+ extend ::ActionController::Live::ClassMethods
+
+ include ::ActiveStorage::Streaming
+
+ include ::ActiveStorage::DisableSession
+
+ def show: () -> untyped
+end
+
+# Finds a representation by a +signed_id+ and a +variation_key+, and redirects to a representation's expiring
+# service URL.
+#
+# The +signed_id+s make URLs hard to guess but permanent by design, allowing the URLs to be cached.
+#
+# The URLs created for this controller are set to never expire by default.
+# To make URLs expire, pass the +expires_in+ option when generating the URL:
+#
+# rails_storage_redirect_url(representation, expires_in: 1.minute)
+#
+# Or set the default for all Active Storage URLs:
+#
+# config.active_storage.urls_expire_in = 1.day
+#
+# The service URLs are set to expire in 5 minutes by default.
+# The default can be changed for all service URLs:
+#
+# config.active_storage.service_urls_expire_in = 1.hour
+#
+# WARNING: All Active Storage controllers are publicly accessible by default.
+# Anyone who knows the URL can access the file, even if the rest of your application requires
+# authentication. If your files require access control consider implementing
+# {Authenticated Controllers}[https://guides.rubyonrails.org/active_storage_overview.html#authenticated-controllers].
+class ActiveStorage::Representations::RedirectController < ::ActiveStorage::Representations::BaseController
+ def show: () -> untyped
+end
+
+# This concern disables the session in order to allow caching by default in some CDNs as CloudFlare.
+module ActiveStorage::DisableSession
+ extend ::ActiveSupport::Concern
+end
+
+module ActiveStorage::FileServer
+ private
+
+ def serve_file: (untyped path, content_type: untyped, disposition: untyped) -> untyped
+end
+
+module ActiveStorage::SetBlob
+ extend ::ActiveSupport::Concern
+
+ private
+
+ def set_blob: () -> untyped
+
+ def blob_scope: () -> untyped
+end
+
+# Sets the ActiveStorage::Current.url_options attribute, which the disk service uses to generate URLs.
+# Include this concern in custom controllers that call ActiveStorage::Blob#url,
+# ActiveStorage::Variant#url, or ActiveStorage::Preview#url so the disk service can
+# generate URLs using the same host, protocol, and port as the current request.
+module ActiveStorage::SetCurrent
+ extend ::ActiveSupport::Concern
+end
+
+module ActiveStorage::Streaming
+ extend ::ActiveSupport::Concern
+
+ DEFAULT_BLOB_STREAMING_DISPOSITION: "inline"
+
+ include ::ActionController::DataStreaming
+
+ include ::ActionController::Live
+
+ private
+
+ # Stream the blob in byte ranges specified through the header
+ def send_blob_byte_range_data: (untyped blob, untyped range_header, ?disposition: untyped?) -> untyped
+
+ def ranges_valid?: (untyped ranges) -> (false | untyped)
+
+ # Stream the blob from storage directly to the response. The disposition can be controlled by setting +disposition+.
+ # The content type and filename is set directly from the +blob+.
+ def send_blob_stream: (untyped blob, ?disposition: untyped?) -> untyped
+end
+
+# Provides asynchronous analysis of ActiveStorage::Blob records via ActiveStorage::Blob#analyze_later.
+class ActiveStorage::AnalyzeJob < ::ActiveStorage::BaseJob
+ def perform: (untyped blob) -> untyped
+end
+
+class ActiveStorage::BaseJob < ::ActiveJob::Base
+end
+
+# Provides asynchronous mirroring of directly-uploaded blobs.
+class ActiveStorage::MirrorJob < ::ActiveStorage::BaseJob
+ def perform: (untyped key, checksum: untyped) -> untyped
+end
+
+class ActiveStorage::PreviewImageJob < ::ActiveStorage::BaseJob
+ def perform: (untyped blob, untyped variations) -> untyped
+end
+
+# Provides asynchronous purging of ActiveStorage::Blob records via ActiveStorage::Blob#purge_later.
+class ActiveStorage::PurgeJob < ::ActiveStorage::BaseJob
+ def perform: (untyped blob) -> untyped
+end
+
+class ActiveStorage::TransformJob < ::ActiveStorage::BaseJob
+ def perform: (untyped blob, untyped transformations) -> untyped
+end
+
+# = Active Storage \Attachment
+#
+# Attachments associate records with blobs. Usually that's a one record-many blobs relationship,
+# but it is possible to associate many different records with the same blob. A foreign-key constraint
+# on the attachments table prevents blobs from being purged if they’re still attached to any records.
+#
+# Attachments also have access to all methods from ActiveStorage::Blob.
+#
+# If you wish to preload attachments or blobs, you can use these scopes:
+#
+# # preloads attachments, their corresponding blobs, and variant records (if using `ActiveStorage.track_variants`)
+# User.all.with_attached_avatars
+#
+# # preloads blobs and variant records (if using `ActiveStorage.track_variants`)
+# User.first.avatars.with_all_variant_records
+class ActiveStorage::Attachment
+ def signed_id: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ # Returns an ActiveStorage::Variant or ActiveStorage::VariantWithRecord
+ # instance for the attachment with the set of +transformations+ provided.
+ # Example:
+ #
+ # avatar.variant(resize_to_limit: [100, 100]).processed.url
+ #
+ # or if you are using pre-defined variants:
+ #
+ # avatar.variant(:thumb).processed.url
+ #
+ # See ActiveStorage::Blob::Representable#variant for more information.
+ #
+ # Raises an +ArgumentError+ if +transformations+ is a +Symbol+ which is an
+ # unknown pre-defined variant of the attachment.
+ def variant: (untyped transformations) -> untyped
+
+ # Returns an ActiveStorage::Preview instance for the attachment with the set
+ # of +transformations+ provided.
+ # Example:
+ #
+ # video.preview(resize_to_limit: [100, 100]).processed.url
+ #
+ # or if you are using pre-defined variants:
+ #
+ # video.preview(:thumb).processed.url
+ #
+ # See ActiveStorage::Blob::Representable#preview for more information.
+ #
+ # Raises an +ArgumentError+ if +transformations+ is a +Symbol+ which is an
+ # unknown pre-defined variant of the attachment.
+ def preview: (untyped transformations) -> untyped
+
+ # Returns an ActiveStorage::Preview or an ActiveStorage::Variant for the
+ # attachment with set of +transformations+ provided.
+ # Example:
+ #
+ # avatar.representation(resize_to_limit: [100, 100]).processed.url
+ #
+ # or if you are using pre-defined variants:
+ #
+ # avatar.representation(:thumb).processed.url
+ #
+ # See ActiveStorage::Blob::Representable#representation for more information.
+ #
+ # Raises an +ArgumentError+ if +transformations+ is a +Symbol+ which is an
+ # unknown pre-defined variant of the attachment.
+ def representation: (untyped transformations) -> untyped
+
+ private
+
+ def analyze_blob_later: () -> (untyped | nil)
+
+ def mirror_blob_later: () -> untyped
+
+ def transform_variants_later: () -> untyped
+
+ def purge_dependent_blob: () -> (untyped | nil)
+
+ def dependent: () -> untyped
+
+ def named_variants: () -> untyped
+
+ def transformations_by_name: (untyped transformations) -> untyped
+end
+
+# = Active Storage \Blob
+#
+# A blob is a record that contains the metadata about a file and a key for where that file resides on the service.
+# Blobs can be created in two ways:
+#
+# 1. Ahead of the file being uploaded server-side to the service, via create_and_upload!. A rewindable
+# io with the file contents must be available at the server for this operation.
+# 2. Ahead of the file being directly uploaded client-side to the service, via create_before_direct_upload!.
+#
+# The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end
+# service that deals with files. The second option is faster, since you're not using your own server as a staging
+# point for uploads, and can work with deployments like Heroku that do not provide large amounts of disk space.
+#
+# Blobs are intended to be immutable in as-so-far as their reference to a specific file goes. You're allowed to
+# update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file.
+# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one.
+#
+# When using a custom +key+, the value is treated as trusted. Using untrusted user input
+# as the key may result in unexpected behavior.
+class ActiveStorage::Blob
+ def self.services: () -> untyped
+ def self.services=: (untyped value) -> untyped
+ def self.services?: () -> bool
+ def services: () -> untyped
+ def services?: () -> bool
+ def services=: (untyped value) -> untyped
+ def self.service: () -> untyped
+ def self.service=: (untyped value) -> untyped
+ def self.service?: () -> bool
+
+ # FIXME: these property should never have been stored in the metadata.
+ # The blob table should be migrated to have dedicated columns for theses.
+ PROTECTED_METADATA: ::Array["analyzed" | "identified" | "composed"]
+
+ def self.scope_for_strict_loading: () -> untyped
+
+ # Concatenate multiple blobs into a single "composed" blob.
+ def self.compose: (untyped blobs, filename: untyped, ?key: untyped?, ?content_type: untyped?, ?metadata: untyped?) -> untyped
+
+ def self.filter_metadata: (untyped metadata) -> untyped
+
+ include ::ActiveStorage::Blob::Analyzable
+
+ include ::ActiveStorage::Blob::Identifiable
+
+ include ::ActiveStorage::Blob::Representable
+
+ include ::ActiveStorage::Blob::Servable
+
+ def custom_metadata: () -> untyped
+
+ def custom_metadata=: (untyped metadata) -> untyped
+
+ def compose: (untyped keys) -> untyped
+
+ def service_metadata: () -> ({ content_type: untyped, disposition: :attachment, filename: untyped, custom_metadata: untyped } | { content_type: untyped, custom_metadata: untyped })
+
+ # Downloads a part of the file associated with this blob.
+ def download_chunk: (untyped range) -> untyped
+
+ private
+
+ def compute_checksum_in_chunks: (untyped io) -> untyped
+
+ def extract_content_type: (untyped io) -> untyped
+
+ def web_image?: () -> untyped
+
+ def touch_attachments: () -> untyped
+
+ def update_service_metadata: () -> (untyped | nil)
+end
+
+# = Active Storage \Blob \Analyzable
+module ActiveStorage::Blob::Analyzable
+ # Extracts and stores metadata from the file associated with this blob using a relevant analyzer. Active Storage comes
+ # with built-in analyzers for images and videos. See ActiveStorage::Analyzer::ImageAnalyzer and
+ # ActiveStorage::Analyzer::VideoAnalyzer for information about the specific attributes they extract and the third-party
+ # libraries they require.
+ #
+ # To choose the analyzer for a blob, Active Storage calls +accept?+ on each registered analyzer in order. It uses the
+ # first analyzer for which +accept?+ returns true when given the blob. If no registered analyzer accepts the blob, no
+ # metadata is extracted from it.
+ #
+ # In a \Rails application, add or remove analyzers by manipulating +Rails.application.config.active_storage.analyzers+
+ # in an initializer:
+ #
+ # # Add a custom analyzer for Microsoft Office documents:
+ # Rails.application.config.active_storage.analyzers.append DOCXAnalyzer
+ #
+ # # Remove the built-in video analyzer:
+ # Rails.application.config.active_storage.analyzers.delete ActiveStorage::Analyzer::VideoAnalyzer
+ #
+ # Outside of a \Rails application, manipulate +ActiveStorage.analyzers+ instead.
+ #
+ # You won't ordinarily need to call this method from a \Rails application. New blobs are automatically and asynchronously
+ # analyzed via #analyze_later when they're attached for the first time.
+ def analyze: () -> untyped
+
+ # Enqueues an ActiveStorage::AnalyzeJob which calls #analyze, or calls #analyze inline based on analyzer class configuration.
+ #
+ # This method is automatically called for a blob when it's attached for the first time. You can call it to analyze a blob
+ # again (e.g. if you add a new analyzer or modify an existing one).
+ def analyze_later: () -> untyped
+
+ # Returns true if the blob has been analyzed.
+ def analyzed?: () -> untyped
+
+ private
+
+ def extract_metadata_via_analyzer: () -> untyped
+
+ def analyzer: () -> untyped
+
+ def analyzer_class: () -> untyped
+end
+
+# = Active Storage \Blob \Identifiable
+module ActiveStorage::Blob::Identifiable
+ def identify: () -> untyped
+
+ def identify_without_saving: () -> (untyped | nil)
+
+ def identified?: () -> untyped
+
+ private
+
+ def identify_content_type: () -> untyped
+
+ def download_identifiable_chunk: () -> (untyped | "")
+end
+
+module ActiveStorage::Blob::Representable
+ extend ::ActiveSupport::Concern
+
+ # Returns an ActiveStorage::Variant or ActiveStorage::VariantWithRecord instance with the set of +transformations+ provided.
+ # This is only relevant for image files, and it allows any image to be transformed for size, colors, and the like. Example:
+ #
+ # avatar.variant(resize_to_limit: [100, 100]).processed.url
+ #
+ # This will create and process a variant of the avatar blob that's constrained to a height and width of 100px.
+ # Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
+ #
+ # Frequently, though, you don't actually want to transform the variant right away. But rather simply refer to a
+ # specific variant that can be created by a controller on-demand. Like so:
+ #
+ # <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %>
+ #
+ # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::Representations::ProxyController
+ # or ActiveStorage::Representations::RedirectController can then produce on-demand.
+ #
+ # Raises ActiveStorage::InvariableError if the variant processor cannot
+ # transform the blob. To determine whether a blob is variable, call
+ # ActiveStorage::Blob#variable?.
+ #
+ # ==== Options
+ #
+ # Options are defined by the {image_processing gem}[https://github.com/janko/image_processing],
+ # and depend on which variant processor you are using:
+ # {Vips}[https://github.com/janko/image_processing/blob/master/doc/vips.md] or
+ # {MiniMagick}[https://github.com/janko/image_processing/blob/master/doc/minimagick.md].
+ # However, both variant processors support the following options:
+ #
+ # [+:resize_to_limit+]
+ # Downsizes the image to fit within the specified dimensions while retaining
+ # the original aspect ratio. Will only resize the image if it's larger than
+ # the specified dimensions.
+ #
+ # user.avatar.variant(resize_to_limit: [100, 100])
+ #
+ # [+:resize_to_fit+]
+ # Resizes the image to fit within the specified dimensions while retaining
+ # the original aspect ratio. Will downsize the image if it's larger than the
+ # specified dimensions or upsize if it's smaller.
+ #
+ # user.avatar.variant(resize_to_fit: [100, 100])
+ #
+ # [+:resize_to_fill+]
+ # Resizes the image to fill the specified dimensions while retaining the
+ # original aspect ratio. If necessary, will crop the image in the larger
+ # dimension.
+ #
+ # user.avatar.variant(resize_to_fill: [100, 100])
+ #
+ # [+:resize_and_pad+]
+ # Resizes the image to fit within the specified dimensions while retaining
+ # the original aspect ratio. If necessary, will pad the remaining area with
+ # transparent color if source image has alpha channel, black otherwise.
+ #
+ # user.avatar.variant(resize_and_pad: [100, 100])
+ #
+ # [+:crop+]
+ # Extracts an area from an image. The first two arguments are the left and
+ # top edges of area to extract, while the last two arguments are the width
+ # and height of the area to extract.
+ #
+ # user.avatar.variant(crop: [20, 50, 300, 300])
+ #
+ # [+:rotate+]
+ # Rotates the image by the specified angle.
+ #
+ # user.avatar.variant(rotate: 90)
+ #
+ # Some options, including those listed above, can accept additional
+ # processor-specific values which can be passed as a trailing hash:
+ #
+ #
+ # <%= image_tag user.avatar.variant(resize_to_fill: [100, 100, { crop: :centre }]) %>
+ #
+ # If migrating an existing application between MiniMagick and Vips, you will
+ # need to update processor-specific options:
+ #
+ #
+ # <%= image_tag user.avatar.variant(resize_to_limit: [100, 100], format: :jpeg,
+ # sampling_factor: "4:2:0", strip: true, interlace: "JPEG", colorspace: "sRGB", quality: 80) %>
+ #
+ #
+ # <%= image_tag user.avatar.variant(resize_to_limit: [100, 100], format: :jpeg,
+ # saver: { subsample_mode: "on", strip: true, interlace: true, quality: 80 }) %>
+ #
+ def variant: (untyped transformations) -> untyped
+
+ # Returns true if the variant processor can transform the blob (its content
+ # type is in +ActiveStorage.variable_content_types+).
+ def variable?: () -> untyped
+
+ # Returns an ActiveStorage::Preview instance with the set of +transformations+ provided. A preview is an image generated
+ # from a non-image blob. Active Storage comes with built-in previewers for videos and PDF documents. The video previewer
+ # extracts the first frame from a video and the PDF previewer extracts the first page from a PDF document.
+ #
+ # blob.preview(resize_to_limit: [100, 100]).processed.url
+ #
+ # Avoid processing previews synchronously in views. Instead, link to a controller action that processes them on demand.
+ # Active Storage provides one, but you may want to create your own (for example, if you need authentication). Here’s
+ # how to use the built-in version:
+ #
+ # <%= image_tag video.preview(resize_to_limit: [100, 100]) %>
+ #
+ # This method raises ActiveStorage::UnpreviewableError if no previewer accepts the receiving blob. To determine
+ # whether a blob is accepted by any previewer, call ActiveStorage::Blob#previewable?.
+ def preview: (untyped transformations) -> untyped
+
+ # Returns true if any registered previewer accepts the blob. By default, this will return true for videos and PDF documents.
+ def previewable?: () -> untyped
+
+ # Returns an ActiveStorage::Preview for a previewable blob or an ActiveStorage::Variant for a variable image blob.
+ #
+ # blob.representation(resize_to_limit: [100, 100]).processed.url
+ #
+ # Raises ActiveStorage::UnrepresentableError if the receiving blob is neither variable nor previewable. Call
+ # ActiveStorage::Blob#representable? to determine whether a blob is representable.
+ #
+ # See ActiveStorage::Blob#preview and ActiveStorage::Blob#variant for more information.
+ def representation: (untyped transformations) -> untyped
+
+ # Returns true if the blob is variable or previewable.
+ def representable?: () -> untyped
+
+ def preview_image_needed_before_processing_variants?: () -> untyped
+
+ def create_preview_image_later: (untyped variations) -> (untyped | nil)
+
+ def preprocessed: (untyped transformations) -> (untyped | nil)
+
+ private
+
+ def default_variant_transformations: () -> { format: untyped }
+
+ def default_variant_format: () -> (untyped | :png)
+
+ def format: () -> untyped
+
+ def variant_class: () -> untyped
+end
+
+module ActiveStorage::Blob::Servable
+ def content_type_for_serving: () -> untyped
+
+ def forced_disposition_for_serving: () -> (:attachment | nil)
+
+ private
+
+ def forcibly_serve_as_binary?: () -> untyped
+
+ def allowed_inline?: () -> untyped
+end
+
+class ActiveStorage::Current
+end
+
+# = Active Storage \Filename
+#
+# Encapsulates a string representing a filename to provide convenient access to parts of it and sanitization.
+# A Filename instance is returned by ActiveStorage::Blob#filename, and is comparable so it can be used for sorting.
+class ActiveStorage::Filename
+ include ::Comparable
+
+ # Returns a Filename instance based on the given filename. If the filename is a Filename, it is
+ # returned unmodified. If it is a String, it is passed to ActiveStorage::Filename.new.
+ def self.wrap: (untyped filename) -> untyped
+
+ def initialize: (untyped filename) -> void
+
+ def as_json: (*untyped) -> untyped
+
+ def <=>: (untyped other) -> untyped
+end
+
+class ActiveStorage::NamedVariant
+ attr_reader transformations: untyped
+
+ attr_reader preprocessed: untyped
+
+ def initialize: (untyped transformations) -> void
+
+ def preprocessed?: (untyped record) -> untyped
+end
+
+# = Active Storage \Preview
+#
+# Some non-image blobs can be previewed: that is, they can be presented as images. A video blob can be previewed by
+# extracting its first frame, and a PDF blob can be previewed by extracting its first page.
+#
+# A previewer extracts a preview image from a blob. Active Storage provides previewers for videos and PDFs.
+# ActiveStorage::Previewer::VideoPreviewer is used for videos whereas ActiveStorage::Previewer::PopplerPDFPreviewer
+# and ActiveStorage::Previewer::MuPDFPreviewer are used for PDFs. Build custom previewers by
+# subclassing ActiveStorage::Previewer and implementing the requisite methods. Consult the ActiveStorage::Previewer
+# documentation for more details on what's required of previewers.
+#
+# To choose the previewer for a blob, Active Storage calls +accept?+ on each registered previewer in order. It uses the
+# first previewer for which +accept?+ returns true when given the blob. In a \Rails application, add or remove previewers
+# by manipulating +Rails.application.config.active_storage.previewers+ in an initializer:
+#
+# Rails.application.config.active_storage.previewers
+# # => [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer ]
+#
+# # Add a custom previewer for Microsoft Office documents:
+# Rails.application.config.active_storage.previewers << DOCXPreviewer
+# # => [ ActiveStorage::Previewer::PopplerPDFPreviewer, ActiveStorage::Previewer::MuPDFPreviewer, ActiveStorage::Previewer::VideoPreviewer, DOCXPreviewer ]
+#
+# Outside of a \Rails application, modify +ActiveStorage.previewers+ instead.
+#
+# The built-in previewers rely on third-party system libraries. Specifically, the built-in video previewer requires
+# {FFmpeg}[https://www.ffmpeg.org]. Two PDF previewers are provided: one requires {Poppler}[https://poppler.freedesktop.org],
+# and the other requires {muPDF}[https://mupdf.com] (version 1.8 or newer). To preview PDFs, install either Poppler or muPDF.
+#
+# These libraries are not provided by \Rails. You must install them yourself to use the built-in previewers. Before you
+# install and use third-party software, make sure you understand the licensing implications of doing so.
+class ActiveStorage::Preview
+ def filename: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def content_type: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ include ::ActiveStorage::Blob::Servable
+
+ class UnprocessedError < ::StandardError
+ end
+
+ attr_reader blob: untyped
+
+ attr_reader variation: untyped
+
+ def initialize: (untyped blob, untyped variation_or_variation_key) -> void
+
+ # Processes the preview if it has not been processed yet. Returns the receiving +ActiveStorage::Preview+ instance for convenience:
+ #
+ # blob.preview(resize_to_limit: [100, 100]).processed.url
+ #
+ # Processing a preview generates an image from its blob and attaches the preview image to the blob. Because the preview
+ # image is stored with the blob, it is only generated once.
+ def processed: () -> self
+
+ # Returns the blob's attached preview image.
+ def image: () -> untyped
+
+ # Returns the URL of the preview's variant on the service. Raises ActiveStorage::Preview::UnprocessedError if the
+ # preview has not been processed yet.
+ #
+ # This method synchronously processes a variant of the preview image, so do not call it in views. Instead, generate
+ # a stable URL that redirects to the URL returned by this method.
+ def url: (**untyped options) -> untyped
+
+ # Returns a combination key of the blob and the variation that together identifies a specific variant.
+ def key: () -> untyped
+
+ # Downloads the file associated with this preview's variant. If no block is
+ # given, the entire file is read into memory and returned. That'll use a lot
+ # of RAM for very large files. If a block is given, then the download is
+ # streamed and yielded in chunks. Raises ActiveStorage::Preview::UnprocessedError
+ # if the preview has not been processed yet.
+ def download: () ?{ (?) -> untyped } -> untyped
+
+ private
+
+ def processed?: () -> untyped
+
+ def process: () -> untyped
+
+ def variant: () -> untyped
+
+ def variant?: () -> untyped
+
+ def presentation: () -> untyped
+
+ def previewer: () -> untyped
+
+ def previewer_class: () -> untyped
+end
+
+class ActiveStorage::Record
+end
+
+class ActiveStorage::VariantRecord < ::ActiveStorage::Record
+end
+
+# = Active Storage \Variant With Record
+#
+# Like an ActiveStorage::Variant, but keeps detail about the variant in the database as an
+# ActiveStorage::VariantRecord. This is only used if +ActiveStorage.track_variants+ is enabled.
+class ActiveStorage::VariantWithRecord
+ def service: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def content_type: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def key: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def url: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def download: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ include ::ActiveStorage::Blob::Servable
+
+ attr_reader blob: untyped
+
+ attr_reader variation: untyped
+
+ def initialize: (untyped blob, untyped variation) -> void
+
+ def processed: () -> self
+
+ def image: () -> untyped
+
+ def filename: () -> untyped
+
+ # Destroys record and deletes file from service.
+ def destroy: () -> untyped
+
+ private
+
+ def processed?: () -> untyped
+
+ def process: () -> untyped
+
+ def transform_blob: () { (io: untyped, filename: untyped, content_type: untyped, service_name: untyped) -> untyped } -> untyped
+
+ def create_or_find_record: (image: untyped) -> untyped
+
+ def record: () -> untyped
+end
+
+# = Active Storage \Variant
+#
+# Image blobs can have variants that are the result of a set of transformations applied to the original.
+# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
+# original.
+#
+# Variants rely on {ImageProcessing}[https://github.com/janko/image_processing] gem for the actual transformations
+# of the file, so you must add gem "image_processing" to your Gemfile if you wish to use variants. By
+# default, images will be processed with {libvips}[http://libvips.github.io/libvips/] using the
+# {ruby-vips}[https://github.com/libvips/ruby-vips] gem, but you can also switch to the
+# {ImageMagick}[http://imagemagick.org] processor operated by the {MiniMagick}[https://github.com/minimagick/minimagick]
+# gem).
+#
+# Rails.application.config.active_storage.variant_processor
+# # => :vips
+#
+# Rails.application.config.active_storage.variant_processor = :mini_magick
+# # => :mini_magick
+#
+# Note that to create a variant it's necessary to download the entire blob file from the service. Because of this process,
+# you also want to be considerate about when the variant is actually processed. You shouldn't be processing variants inline
+# in a template, for example. Delay the processing to an on-demand controller, like the one provided in
+# ActiveStorage::Representations::ProxyController and ActiveStorage::Representations::RedirectController.
+#
+# To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided
+# by Active Storage like so:
+#
+# <%= image_tag Current.user.avatar.variant(resize_to_limit: [100, 100]) %>
+#
+# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::Representations::ProxyController
+# or ActiveStorage::Representations::RedirectController can then produce on-demand.
+#
+# When you do want to actually produce the variant needed, call +processed+. This will check that the variant
+# has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform
+# the transformations, upload the variant to the service, and return itself again. Example:
+#
+# avatar.variant(resize_to_limit: [100, 100]).processed.url
+#
+# This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
+# Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
+#
+# You can combine any number of ImageMagick/libvips operations into a variant, as well as any macros provided by the
+# ImageProcessing gem (such as +resize_to_limit+):
+#
+# avatar.variant(resize_to_limit: [800, 800], colourspace: "b-w", rotate: "-90")
+#
+# Visit the following links for a list of available ImageProcessing commands and ImageMagick/libvips operations:
+#
+# * {ImageProcessing::MiniMagick}[https://github.com/janko/image_processing/blob/master/doc/minimagick.md#methods]
+# * {ImageMagick reference}[https://www.imagemagick.org/script/mogrify.php]
+# * {ImageProcessing::Vips}[https://github.com/janko/image_processing/blob/master/doc/vips.md#methods]
+# * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image]
+class ActiveStorage::Variant
+ def service: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def content_type: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ include ::ActiveStorage::Blob::Servable
+
+ attr_reader blob: untyped
+
+ attr_reader variation: untyped
+
+ def initialize: (untyped blob, untyped variation_or_variation_key) -> void
+
+ # Returns the variant instance itself after it's been processed or an existing processing has been found on the service.
+ def processed: () -> self
+
+ # Returns a combination key of the blob and the variation that together identifies a specific variant.
+ def key: () -> ::String
+
+ # Returns the URL of the blob variant on the service. See ActiveStorage::Blob#url for details.
+ #
+ # Use url_for(variant) (or the implied form, like link_to variant or redirect_to variant) to get the stable URL
+ # for a variant that points to the ActiveStorage::Representations::ProxyController or ActiveStorage::Representations::RedirectController,
+ # which in turn will use this +url+ method for its redirection.
+ def url: (?expires_in: untyped, ?disposition: ::Symbol) -> untyped
+
+ # Downloads the file associated with this variant. If no block is given, the entire file is read into memory and returned.
+ # That'll use a lot of RAM for very large files. If a block is given, then the download is streamed and yielded in chunks.
+ def download: () ?{ (?) -> untyped } -> untyped
+
+ def filename: () -> untyped
+
+ # Returns the receiving variant. Allows ActiveStorage::Variant and ActiveStorage::Preview instances to be used interchangeably.
+ def image: () -> self
+
+ # Deletes variant file from service.
+ def destroy: () -> untyped
+
+ private
+
+ def processed?: () -> untyped
+
+ def process: () -> untyped
+end
+
+# = Active Storage \Variation
+#
+# A set of transformations that can be applied to a blob to create a variant. This class is exposed via
+# the ActiveStorage::Blob#variant method and should rarely be used directly.
+#
+# In case you do need to use this directly, it's instantiated using a hash of transformations where
+# the key is the command and the value is the arguments. Example:
+#
+# ActiveStorage::Variation.new(resize_to_limit: [100, 100], colourspace: "b-w", rotate: "-90", saver: { trim: true })
+#
+# The options map directly to {ImageProcessing}[https://github.com/janko/image_processing] commands.
+class ActiveStorage::Variation
+ attr_reader transformations: untyped
+
+ # Returns a Variation instance based on the given variator. If the variator is a Variation, it is
+ # returned unmodified. If it is a String, it is passed to ActiveStorage::Variation.decode. Otherwise,
+ # it is assumed to be a transformations Hash and is passed directly to the constructor.
+ def self.wrap: (untyped variator) -> untyped
+
+ # Returns a Variation instance with the transformations that were encoded by +encode+.
+ def self.decode: (untyped key) -> untyped
+
+ # Returns a signed key for the +transformations+, which can be used to refer to a specific
+ # variation in a URL or combined key (like ActiveStorage::Variant#key).
+ def self.encode: (untyped transformations) -> untyped
+
+ def initialize: (untyped transformations) -> void
+
+ def default_to: (untyped defaults) -> untyped
+
+ # Accepts a File object, performs the +transformations+ against it, and
+ # saves the transformed image into a temporary file.
+ def transform: (untyped file) { (?) -> untyped } -> untyped
+
+ def format: () -> untyped
+
+ def content_type: () -> untyped
+
+ # Returns a signed key for all the +transformations+ that this variation was instantiated with.
+ def key: () -> untyped
+
+ def digest: () -> untyped
+
+ private
+
+ def transformer: () -> untyped
+end
+
+# :markup: markdown
+# :include: ../README.md
+module ActiveStorage
+ def self.logger: () -> untyped
+
+ def self.logger=: (untyped value) -> untyped
+
+ def logger: () -> untyped
+
+ def logger=: (untyped value) -> untyped
+
+ def self.verifier: () -> untyped
+
+ def self.verifier=: (untyped value) -> untyped
+
+ def verifier: () -> untyped
+
+ def verifier=: (untyped value) -> untyped
+
+ def self.variant_processor: () -> untyped
+
+ def self.variant_processor=: (untyped value) -> untyped
+
+ def variant_processor: () -> untyped
+
+ def variant_processor=: (untyped value) -> untyped
+
+ def self.variant_transformer: () -> untyped
+
+ def self.variant_transformer=: (untyped value) -> untyped
+
+ def variant_transformer: () -> untyped
+
+ def variant_transformer=: (untyped value) -> untyped
+
+ def self.queues: () -> untyped
+
+ def self.queues=: (untyped value) -> untyped
+
+ def queues: () -> untyped
+
+ def queues=: (untyped value) -> untyped
+
+ def self.previewers: () -> untyped
+
+ def self.previewers=: (untyped value) -> untyped
+
+ def previewers: () -> untyped
+
+ def previewers=: (untyped value) -> untyped
+
+ def self.analyzers: () -> untyped
+
+ def self.analyzers=: (untyped value) -> untyped
+
+ def analyzers: () -> untyped
+
+ def analyzers=: (untyped value) -> untyped
+
+ def self.paths: () -> untyped
+
+ def self.paths=: (untyped value) -> untyped
+
+ def paths: () -> untyped
+
+ def paths=: (untyped value) -> untyped
+
+ def self.variable_content_types: () -> untyped
+
+ def self.variable_content_types=: (untyped value) -> untyped
+
+ def variable_content_types: () -> untyped
+
+ def variable_content_types=: (untyped value) -> untyped
+
+ def self.web_image_content_types: () -> untyped
+
+ def self.web_image_content_types=: (untyped value) -> untyped
+
+ def web_image_content_types: () -> untyped
+
+ def web_image_content_types=: (untyped value) -> untyped
+
+ def self.binary_content_type: () -> untyped
+
+ def self.binary_content_type=: (untyped value) -> untyped
+
+ def binary_content_type: () -> untyped
+
+ def binary_content_type=: (untyped value) -> untyped
+
+ def self.content_types_to_serve_as_binary: () -> untyped
+
+ def self.content_types_to_serve_as_binary=: (untyped value) -> untyped
+
+ def content_types_to_serve_as_binary: () -> untyped
+
+ def content_types_to_serve_as_binary=: (untyped value) -> untyped
+
+ def self.content_types_allowed_inline: () -> untyped
+
+ def self.content_types_allowed_inline=: (untyped value) -> untyped
+
+ def content_types_allowed_inline: () -> untyped
+
+ def content_types_allowed_inline=: (untyped value) -> untyped
+
+ def self.supported_image_processing_methods: () -> untyped
+
+ def self.supported_image_processing_methods=: (untyped value) -> untyped
+
+ def supported_image_processing_methods: () -> untyped
+
+ def supported_image_processing_methods=: (untyped value) -> untyped
+
+ def self.unsupported_image_processing_arguments: () -> untyped
+
+ def self.unsupported_image_processing_arguments=: (untyped value) -> untyped
+
+ def unsupported_image_processing_arguments: () -> untyped
+
+ def unsupported_image_processing_arguments=: (untyped value) -> untyped
+
+ def self.streaming_chunk_max_size: () -> untyped
+
+ def self.streaming_chunk_max_size=: (untyped value) -> untyped
+
+ def streaming_chunk_max_size: () -> untyped
+
+ def streaming_chunk_max_size=: (untyped value) -> untyped
+
+ def self.service_urls_expire_in: () -> untyped
+
+ def self.service_urls_expire_in=: (untyped value) -> untyped
+
+ def service_urls_expire_in: () -> untyped
+
+ def service_urls_expire_in=: (untyped value) -> untyped
+
+ def self.touch_attachment_records: () -> untyped
+
+ def self.touch_attachment_records=: (untyped value) -> untyped
+
+ def touch_attachment_records: () -> untyped
+
+ def touch_attachment_records=: (untyped value) -> untyped
+
+ def self.urls_expire_in: () -> untyped
+
+ def self.urls_expire_in=: (untyped value) -> untyped
+
+ def urls_expire_in: () -> untyped
+
+ def urls_expire_in=: (untyped value) -> untyped
+
+ def self.routes_prefix: () -> untyped
+
+ def self.routes_prefix=: (untyped value) -> untyped
+
+ def routes_prefix: () -> untyped
+
+ def routes_prefix=: (untyped value) -> untyped
+
+ def self.draw_routes: () -> untyped
+
+ def self.draw_routes=: (untyped value) -> untyped
+
+ def draw_routes: () -> untyped
+
+ def draw_routes=: (untyped value) -> untyped
+
+ def self.resolve_model_to_route: () -> untyped
+
+ def self.resolve_model_to_route=: (untyped value) -> untyped
+
+ def resolve_model_to_route: () -> untyped
+
+ def resolve_model_to_route=: (untyped value) -> untyped
+
+ def self.track_variants: () -> untyped
+
+ def self.track_variants=: (untyped value) -> untyped
+
+ def track_variants: () -> untyped
+
+ def track_variants=: (untyped value) -> untyped
+
+ def self.checksum_implementation: () -> untyped
+
+ def self.checksum_implementation=: (untyped value) -> untyped
+
+ def self.streaming_max_ranges: () -> untyped
+
+ def self.streaming_max_ranges=: (untyped value) -> untyped
+
+ def self.video_preview_arguments: () -> untyped
+
+ def self.video_preview_arguments=: (untyped value) -> untyped
+
+ def video_preview_arguments: () -> untyped
+
+ def video_preview_arguments=: (untyped value) -> untyped
+
+ extend ::ActiveSupport::Autoload
+
+ module Transformers
+ extend ::ActiveSupport::Autoload
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Analyzer
+ #
+ # This is an abstract base class for analyzers, which extract metadata from blobs. See
+ # ActiveStorage::Analyzer::VideoAnalyzer for an example of a concrete subclass.
+ class Analyzer
+ attr_reader blob: untyped
+
+ # Implement this method in a concrete subclass. Have it return true when given a blob from which
+ # the analyzer can extract metadata.
+ def self.accept?: (untyped blob) -> false
+
+ # Implement this method in concrete subclasses. It will determine if blob analysis
+ # should be done in a job or performed inline. By default, analysis is enqueued in a job.
+ def self.analyze_later?: () -> true
+
+ def initialize: (untyped blob) -> void
+
+ # Override this method in a concrete subclass. Have it return a Hash of metadata.
+ def metadata: () -> untyped
+
+ private
+
+ # Downloads the blob to a tempfile on disk. Yields the tempfile.
+ def download_blob_to_tempfile: () { (?) -> untyped } -> untyped
+
+ def logger: () -> untyped
+
+ def tmpdir: () -> untyped
+
+ def instrument: (untyped analyzer) ?{ (?) -> untyped } -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage Audio \Analyzer
+ #
+ # Extracts duration (seconds), bit_rate (bits/s), sample_rate (hertz) and tags (internal metadata) from an audio blob.
+ #
+ # Example:
+ #
+ # ActiveStorage::Analyzer::AudioAnalyzer.new(blob).metadata
+ # # => { duration: 5.0, bit_rate: 320340, sample_rate: 44100, tags: { encoder: "Lavc57.64", ... } }
+ #
+ # This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by \Rails.
+ class Analyzer::AudioAnalyzer < ::ActiveStorage::Analyzer
+ def self.accept?: (untyped blob) -> untyped
+
+ def metadata: () -> untyped
+
+ private
+
+ def duration: () -> untyped
+
+ def bit_rate: () -> untyped
+
+ def sample_rate: () -> untyped
+
+ def tags: () -> untyped
+
+ def audio_stream: () -> untyped
+
+ def streams: () -> untyped
+
+ def probe: () -> untyped
+
+ def probe_from: (untyped file) -> untyped
+
+ def ffprobe_path: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage Image \Analyzer
+ #
+ # This is an abstract base class for image analyzers, which extract width and height from an image blob.
+ #
+ # If the image contains EXIF data indicating its angle is 90 or 270 degrees, its width and height are swapped for convenience.
+ #
+ # Example:
+ #
+ # ActiveStorage::Analyzer::ImageAnalyzer::ImageMagick.new(blob).metadata
+ # # => { width: 4104, height: 2736 }
+ class Analyzer::ImageAnalyzer < ::ActiveStorage::Analyzer
+ extend ::ActiveSupport::Autoload
+
+ def self.accept?: (untyped blob) -> untyped
+
+ def metadata: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # This analyzer relies on the third-party {MiniMagick}[https://github.com/minimagick/minimagick] gem. MiniMagick requires
+ # the {ImageMagick}[http://www.imagemagick.org] system library.
+ class Analyzer::ImageAnalyzer::ImageMagick < ::ActiveStorage::Analyzer::ImageAnalyzer
+ def self.accept?: (untyped blob) -> untyped
+
+ private
+
+ def read_image: () { (untyped) -> untyped } -> (::Hash[untyped, untyped] | untyped)
+
+ def rotated_image?: (untyped image) -> untyped
+ end
+end
+
+module ActiveStorage
+ # This analyzer relies on the third-party {ruby-vips}[https://github.com/libvips/ruby-vips] gem. Ruby-vips requires
+ # the {libvips}[https://libvips.github.io/libvips/] system library.
+ class Analyzer::ImageAnalyzer::Vips < ::ActiveStorage::Analyzer::ImageAnalyzer
+ def self.accept?: (untyped blob) -> untyped
+
+ private
+
+ def read_image: () { (untyped) -> untyped } -> untyped
+
+ ROTATIONS: ::Regexp
+
+ def rotated_image?: (untyped image) -> untyped
+ end
+end
+
+module ActiveStorage
+ class Analyzer::NullAnalyzer < ::ActiveStorage::Analyzer
+ def self.accept?: (untyped blob) -> true
+
+ def self.analyze_later?: () -> false
+
+ def metadata: () -> ::Hash[untyped, untyped]
+ end
+end
+
+module ActiveStorage
+ # = Active Storage Video \Analyzer
+ #
+ # Extracts the following from a video blob:
+ #
+ # * Width (pixels)
+ # * Height (pixels)
+ # * Duration (seconds)
+ # * Angle (degrees)
+ # * Display aspect ratio
+ # * Audio (true if file has an audio channel, false if not)
+ # * Video (true if file has an video channel, false if not)
+ #
+ # Example:
+ #
+ # ActiveStorage::Analyzer::VideoAnalyzer.new(blob).metadata
+ # # => { width: 640.0, height: 480.0, duration: 5.0, angle: 0, display_aspect_ratio: [4, 3], audio: true, video: true }
+ #
+ # When a video's angle is 90, -90, 270 or -270 degrees, its width and height are automatically swapped for convenience.
+ #
+ # This analyzer requires the {FFmpeg}[https://www.ffmpeg.org] system library, which is not provided by \Rails.
+ class Analyzer::VideoAnalyzer < ::ActiveStorage::Analyzer
+ def self.accept?: (untyped blob) -> untyped
+
+ def metadata: () -> untyped
+
+ private
+
+ def width: () -> untyped
+
+ def height: () -> untyped
+
+ def duration: () -> untyped
+
+ def angle: () -> (untyped | nil)
+
+ def display_matrix: () -> untyped
+
+ def display_aspect_ratio: () -> (untyped | nil)
+
+ def rotated?: () -> untyped
+
+ def audio?: () -> untyped
+
+ def video?: () -> untyped
+
+ def computed_height: () -> (untyped | nil)
+
+ def encoded_width: () -> (untyped | nil)
+
+ def encoded_height: () -> (untyped | nil)
+
+ def display_height_scale: () -> (untyped | nil)
+
+ def tags: () -> untyped
+
+ def side_data: () -> untyped
+
+ def video_stream: () -> untyped
+
+ def audio_stream: () -> untyped
+
+ def streams: () -> untyped
+
+ def container: () -> untyped
+
+ def probe: () -> untyped
+
+ def probe_from: (untyped file) -> untyped
+
+ def ffprobe_path: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Attached
+ #
+ # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many
+ # classes that both provide proxy access to the blob association for a record.
+ class Attached
+ private
+
+ def change: () -> untyped
+ end
+end
+
+module ActiveStorage
+ module Attached::Changes
+ extend ::ActiveSupport::Autoload
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::CreateMany
+ attr_reader name: untyped
+
+ attr_reader attachables: untyped
+
+ attr_reader pending_uploads: untyped
+
+ attr_accessor record: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachables, ?pending_uploads: untyped) -> void
+
+ def attachments: () -> untyped
+
+ def blobs: () -> untyped
+
+ def upload: () -> untyped
+
+ def save: () -> untyped
+
+ private
+
+ def subchanges: () -> untyped
+
+ def build_subchange_from: (untyped attachable) -> untyped
+
+ def subchanges_without_blobs: () -> untyped
+
+ def assign_associated_attachments: () -> untyped
+
+ def reset_associated_blobs: () -> untyped
+
+ def persisted_or_new_attachments: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::CreateOneOfMany < ::ActiveStorage::Attached::Changes::CreateOne
+ private
+
+ def find_attachment: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::CreateOne
+ attr_reader name: untyped
+
+ attr_reader attachable: untyped
+
+ attr_accessor record: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachable) -> void
+
+ def attachment: () -> untyped
+
+ def blob: () -> untyped
+
+ def upload: () -> untyped
+
+ def save: () -> untyped
+
+ private
+
+ def find_or_build_attachment: () -> untyped
+
+ def find_attachment: () -> (untyped | nil)
+
+ def build_attachment: () -> untyped
+
+ def find_or_build_blob: () -> untyped
+
+ def attachment_service_name: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::DeleteMany
+ attr_reader name: untyped
+
+ attr_accessor record: untyped
+
+ def initialize: (untyped name, untyped record) -> void
+
+ def attachables: () -> ::Array[untyped]
+
+ def attachments: () -> untyped
+
+ def blobs: () -> untyped
+
+ def save: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::DeleteOne
+ attr_reader name: untyped
+
+ attr_accessor record: untyped
+
+ def initialize: (untyped name, untyped record) -> void
+
+ def attachment: () -> nil
+
+ def save: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::DetachMany
+ attr_reader name: untyped
+
+ attr_reader record: untyped
+
+ attr_reader attachments: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachments) -> void
+
+ def detach: () -> (untyped | nil)
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::DetachOne
+ attr_reader name: untyped
+
+ attr_reader record: untyped
+
+ attr_reader attachment: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachment) -> void
+
+ def detach: () -> (untyped | nil)
+
+ private
+
+ def reset: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::PurgeMany
+ attr_reader name: untyped
+
+ attr_reader record: untyped
+
+ attr_reader attachments: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachments) -> void
+
+ def purge: () -> untyped
+
+ def purge_later: () -> untyped
+
+ private
+
+ def reset: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Attached::Changes::PurgeOne
+ attr_reader name: untyped
+
+ attr_reader record: untyped
+
+ attr_reader attachment: untyped
+
+ def initialize: (untyped name, untyped record, untyped attachment) -> void
+
+ def purge: () -> untyped
+
+ def purge_later: () -> untyped
+
+ private
+
+ def reset: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Attached \Many
+ #
+ # Decorated proxy object representing of multiple attachments to a model.
+ class Attached::Many
+ def purge: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def purge_later: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ def as_json: (?untyped? options) -> untyped
+
+ private
+
+ def purge_many: () -> untyped
+
+ def detach_many: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Attached \Model
+ #
+ # Provides the class-level DSL for declaring an Active Record model's attachments.
+ module Attached::Model
+ extend ::ActiveSupport::Concern
+
+ def self.validate_service_configuration: (untyped service_name, untyped model_class, untyped association_name) -> untyped
+
+ def self.validate_global_service_configuration: (untyped model_class) -> (untyped | nil)
+
+ attr_writer attachment_changes: untyped
+
+ def initialize_dup: (*untyped) -> untyped
+
+ def becomes: (untyped klass) -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Attached \One
+ #
+ # Representation of a single attachment to a model.
+ class Attached::One
+ # Returns the attachment record's JSON representation, or +nil+ when no
+ # attachment is present.
+ def as_json: (?untyped? options) -> (untyped | nil)
+
+ private
+
+ def purge_one: () -> untyped
+
+ def detach_one: () -> untyped
+ end
+end
+
+module ActiveStorage
+ def self.deprecator: () -> untyped
+end
+
+module ActiveStorage
+ class Downloader
+ attr_reader service: untyped
+
+ def initialize: (untyped service) -> void
+
+ def open: (untyped key, ?checksum: untyped?, ?verify: bool, ?name: ::String, ?tmpdir: untyped?) { (untyped) -> untyped } -> untyped
+
+ private
+
+ def open_tempfile: (untyped name, ?untyped? tmpdir) { (untyped) -> untyped } -> untyped
+
+ def download: (untyped key, untyped file) -> untyped
+
+ def verify_integrity_of: (untyped file, checksum: untyped) -> (untyped | nil)
+ end
+end
+
+module ActiveStorage
+ class Engine < ::Rails::Engine
+ end
+end
+
+module ActiveStorage
+ # Generic base class for all Active Storage exceptions.
+ class Error < ::StandardError
+ end
+
+ # Raised when ActiveStorage::Blob#variant is called on a blob that isn't variable.
+ # Use ActiveStorage::Blob#variable? to determine whether a blob is variable.
+ class InvariableError < ::ActiveStorage::Error
+ end
+
+ # Raised when ActiveStorage::Blob#preview is called on a blob that isn't previewable.
+ # Use ActiveStorage::Blob#previewable? to determine whether a blob is previewable.
+ class UnpreviewableError < ::ActiveStorage::Error
+ end
+
+ # Raised when ActiveStorage::Blob#representation is called on a blob that isn't representable.
+ # Use ActiveStorage::Blob#representable? to determine whether a blob is representable.
+ class UnrepresentableError < ::ActiveStorage::Error
+ end
+
+ # Raised when uploaded or downloaded data does not match a precomputed checksum.
+ # Indicates that a network error or a software bug caused data corruption.
+ class IntegrityError < ::ActiveStorage::Error
+ end
+
+ # Raised when ActiveStorage::Blob#download is called on a blob where the
+ # backing file is no longer present in its service.
+ class FileNotFoundError < ::ActiveStorage::Error
+ end
+
+ # Raised when a Previewer is unable to generate a preview image.
+ class PreviewError < ::ActiveStorage::Error
+ end
+
+ # Raised when a storage key resolves to a path outside the service's root
+ # directory, indicating a potential path traversal attack.
+ class InvalidKeyError < ::ActiveStorage::Error
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \FixtureSet
+ #
+ # Fixtures are a way of organizing data that you want to test against; in
+ # short, sample data.
+ #
+ # To learn more about fixtures, read the ActiveRecord::FixtureSet documentation.
+ #
+ # === YAML
+ #
+ # Like other Active Record-backed models, ActiveStorage::Attachment and
+ # ActiveStorage::Blob records inherit from ActiveRecord::Base instances and
+ # therefore can be populated by fixtures.
+ #
+ # Consider a hypothetical Article model class, its related
+ # fixture data, as well as fixture data for related ActiveStorage::Attachment
+ # and ActiveStorage::Blob records:
+ #
+ # # app/models/article.rb
+ # class Article < ApplicationRecord
+ # has_one_attached :thumbnail
+ # end
+ #
+ #
+ #
+ # # fixtures/active_storage/blobs.yml
+ # first_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob filename: "first.png" %>
+ #
+ #
+ #
+ # # fixtures/active_storage/attachments.yml
+ # first_thumbnail_attachment:
+ # name: thumbnail
+ # record: first (Article)
+ # blob: first_thumbnail_blob
+ #
+ # When processed, Active Record will insert database records for each fixture
+ # entry and will ensure the Active Storage relationship is intact.
+ class FixtureSet
+ extend ::ActiveRecord::SecureToken::ClassMethods
+
+ include ::ActiveSupport::Testing::FileFixtures
+
+ include ::ActiveRecord::SecureToken
+
+ # Generate a YAML-encoded representation of an ActiveStorage::Blob
+ # instance's attributes, resolve the file relative to the directory mentioned
+ # by ActiveSupport::Testing::FileFixtures.file_fixture, and upload
+ # the file to the Service
+ #
+ # ==== Examples
+ #
+ # # tests/fixtures/active_storage/blobs.yml
+ # second_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob(
+ # filename: "second.svg",
+ # ) %>
+ #
+ # third_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob(
+ # filename: "third.svg",
+ # content_type: "image/svg+xml",
+ # service_name: "public"
+ # ) %>
+ #
+ def self.blob: (filename: untyped, **untyped attributes) -> untyped
+
+ def prepare: (untyped instance, **untyped attributes) -> untyped
+ end
+end
+
+module ActiveStorage
+ # Returns the currently loaded version of Active Storage as a +Gem::Version+.
+ def self.gem_version: () -> untyped
+
+ module VERSION
+ MAJOR: 8
+
+ MINOR: 1
+
+ TINY: 4
+
+ PRE: nil
+
+ STRING: untyped
+ end
+end
+
+module ActiveStorage
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
+ def service_upload: (untyped event) -> untyped
+
+ def service_download: (untyped event) -> untyped
+
+ alias service_streaming_download service_download
+
+ def preview: (untyped event) -> untyped
+
+ def service_delete: (untyped event) -> untyped
+
+ def service_delete_prefixed: (untyped event) -> untyped
+
+ def service_exist: (untyped event) -> untyped
+
+ def service_url: (untyped event) -> untyped
+
+ def service_mirror: (untyped event) -> untyped
+
+ def logger: () -> untyped
+
+ private
+
+ def info: (untyped event, untyped colored_message) -> untyped
+
+ def debug: (untyped event, untyped colored_message) -> untyped
+
+ def log_prefix_for_service: (untyped event) -> untyped
+
+ def key_in: (untyped event) -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Previewer
+ #
+ # This is an abstract base class for previewers, which generate images from blobs. See
+ # ActiveStorage::Previewer::MuPDFPreviewer and ActiveStorage::Previewer::VideoPreviewer for
+ # examples of concrete subclasses.
+ class Previewer
+ attr_reader blob: untyped
+
+ # Implement this method in a concrete subclass. Have it return true when given a blob from which
+ # the previewer can generate an image.
+ def self.accept?: (untyped blob) -> false
+
+ def initialize: (untyped blob) -> void
+
+ # Override this method in a concrete subclass. Have it yield an attachable preview image (i.e.
+ # anything accepted by ActiveStorage::Attached::One#attach). Pass the additional options to
+ # the underlying blob that is created.
+ def preview: (**untyped options) -> untyped
+
+ private
+
+ # Downloads the blob to a tempfile on disk. Yields the tempfile.
+ def download_blob_to_tempfile: () { (?) -> untyped } -> untyped
+
+ # Executes a system command, capturing its binary output in a tempfile. Yields the tempfile.
+ #
+ # Use this method to shell out to a system library (e.g. muPDF or FFmpeg) for preview image
+ # generation. The resulting tempfile can be used as the +:io+ value in an attachable Hash:
+ #
+ # def preview
+ # download_blob_to_tempfile do |input|
+ # draw "my-drawing-command", input.path, "--format", "png", "-" do |output|
+ # yield io: output, filename: "#{blob.filename.base}.png", content_type: "image/png"
+ # end
+ # end
+ # end
+ #
+ # The output tempfile is opened in the directory returned by #tmpdir.
+ def draw: (*untyped argv) { (untyped) -> untyped } -> untyped
+
+ def open_tempfile: () { (untyped) -> untyped } -> untyped
+
+ def instrument: (untyped operation, ?::Hash[untyped, untyped] payload) ?{ (?) -> untyped } -> untyped
+
+ def service_name: () -> untyped
+
+ def capture: (*untyped argv, to: untyped) -> untyped
+
+ def logger: () -> untyped
+
+ def tmpdir: () -> untyped
+ end
+end
+
+module ActiveStorage
+ class Previewer::MuPDFPreviewer < ::ActiveStorage::Previewer
+ def self.accept?: (untyped blob) -> untyped
+
+ def self.pdf?: (untyped content_type) -> untyped
+
+ def self.mutool_path: () -> untyped
+
+ def self.mutool_exists?: () -> untyped
+
+ def preview: (**untyped options) { (untyped) -> untyped } -> untyped
+
+ private
+
+ def draw_first_page_from: (untyped file) { (?) -> untyped } -> untyped
+ end
+end
+
+module ActiveStorage
+ class Previewer::PopplerPDFPreviewer < ::ActiveStorage::Previewer
+ def self.accept?: (untyped blob) -> untyped
+
+ def self.pdf?: (untyped content_type) -> untyped
+
+ def self.pdftoppm_path: () -> untyped
+
+ def self.pdftoppm_exists?: () -> untyped
+
+ def preview: (**untyped options) { (untyped) -> untyped } -> untyped
+
+ private
+
+ def draw_first_page_from: (untyped file) { (?) -> untyped } -> untyped
+ end
+end
+
+module ActiveStorage
+ class Previewer::VideoPreviewer < ::ActiveStorage::Previewer
+ def self.accept?: (untyped blob) -> untyped
+
+ def self.ffmpeg_exists?: () -> untyped
+
+ def self.ffmpeg_path: () -> untyped
+
+ def preview: (**untyped options) { (untyped) -> untyped } -> untyped
+
+ private
+
+ def draw_relevant_frame_from: (untyped file) { (?) -> untyped } -> untyped
+ end
+end
+
+module ActiveStorage
+ module Reflection
+ class HasAttachedReflection < ::ActiveRecord::Reflection::MacroReflection
+ def variant: (untyped name, untyped transformations) -> untyped
+
+ def named_variants: () -> untyped
+ end
+
+ # Holds all the metadata about a has_one_attached attachment as it was
+ # specified in the Active Record class.
+ class HasOneAttachedReflection
+ end
+
+ # Holds all the metadata about a has_many_attached attachment as it was
+ # specified in the Active Record class.
+ class HasManyAttachedReflection
+ end
+
+ module ReflectionExtension
+ def add_attachment_reflection: (untyped model, untyped name, untyped reflection) -> untyped
+
+ private
+
+ def reflection_class_for: (untyped macro) -> untyped
+ end
+
+ module ActiveRecordExtensions
+ def attachment_reflections: () -> untyped
+
+ def attachment_reflections?: () -> bool
+
+ extend ::ActiveSupport::Concern
+
+ module ClassMethods
+ def attachment_reflections: () -> untyped
+
+ def attachment_reflections=: (untyped value) -> untyped
+
+ def attachment_reflections?: () -> bool
+ end
+ end
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Service
+ #
+ # Abstract class serving as an interface for concrete services.
+ #
+ # The available services are:
+ #
+ # * +Disk+, to manage attachments saved directly on the hard drive.
+ # * +GCS+, to manage attachments through Google Cloud Storage.
+ # * +S3+, to manage attachments through Amazon S3.
+ # * +Mirror+, to be able to use several services to manage attachments.
+ #
+ # Inside a \Rails application, you can set-up your services through the
+ # generated config/storage.yml file and reference one
+ # of the aforementioned constant under the +service+ key. For example:
+ #
+ # local:
+ # service: Disk
+ # root: <%= Rails.root.join("storage") %>
+ #
+ # You can checkout the service's constructor to know which keys are required.
+ #
+ # Then, in your application's configuration, you can specify the service to
+ # use like this:
+ #
+ # config.active_storage.service = :local
+ #
+ # If you are using Active Storage outside of a Ruby on \Rails application, you
+ # can configure the service to use like this:
+ #
+ # ActiveStorage::Blob.service = ActiveStorage::Service.configure(
+ # :local,
+ # { local: {service: "Disk", root: Pathname("/tmp/foo/storage") } }
+ # )
+ class Service
+ extend ::ActiveSupport::Autoload
+
+ attr_accessor name: untyped
+
+ # Configure an Active Storage service by name from a set of configurations,
+ # typically loaded from a YAML file. The Active Storage engine uses this
+ # to set the global Active Storage service when the app boots.
+ def self.configure: (untyped service_name, untyped configurations) -> untyped
+
+ # Override in subclasses that stitch together multiple services and hence
+ # need to build additional services using the configurator.
+ #
+ # Passes the configurator and all of the service's config as keyword args.
+ #
+ # See MirrorService for an example.
+ def self.build: (configurator: untyped, name: untyped, ?service: untyped?, **untyped service_config) -> untyped
+
+ # Upload the +io+ to the +key+ specified. If a +checksum+ is provided, the service will
+ # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
+ def upload: (untyped key, untyped io, ?checksum: untyped?, **untyped options) -> untyped
+
+ # Update metadata for the file identified by +key+ in the service.
+ # Override in subclasses only if the service needs to store specific
+ # metadata that has to be updated upon identification.
+ def update_metadata: (untyped key, **untyped metadata) -> nil
+
+ # Return the content of the file at the +key+.
+ def download: (untyped key) -> untyped
+
+ # Return the partial content in the byte +range+ of the file at the +key+.
+ def download_chunk: (untyped key, untyped range) -> untyped
+
+ def open: (*untyped args, **untyped options) { (?) -> untyped } -> untyped
+
+ # Concatenate multiple files into a single "composed" file.
+ def compose: (untyped source_keys, untyped destination_key, ?filename: untyped?, ?content_type: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ # Delete the file at the +key+.
+ def delete: (untyped key) -> untyped
+
+ # Delete files at keys starting with the +prefix+.
+ def delete_prefixed: (untyped prefix) -> untyped
+
+ # Return +true+ if a file exists at the +key+.
+ def exist?: (untyped key) -> untyped
+
+ # Returns the URL for the file at the +key+. This returns a permanent URL for public files, and returns a
+ # short-lived URL for private files. For private files you can provide the +disposition+ (+:inline+ or +:attachment+),
+ # +filename+, and +content_type+ that you wish the file to be served with on request. Additionally, you can also provide
+ # the amount of seconds the URL will be valid for, specified in +expires_in+.
+ def url: (untyped key, **untyped options) -> untyped
+
+ # Returns a signed, temporary URL that a direct upload file can be PUT to on the +key+.
+ # The URL will be valid for the amount of seconds specified in +expires_in+.
+ # You must also provide the +content_type+, +content_length+, and +checksum+ of the file
+ # that will be uploaded. All these attributes will be validated by the service upon upload.
+ def url_for_direct_upload: (untyped key, expires_in: untyped, content_type: untyped, content_length: untyped, checksum: untyped, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ # Returns a Hash of headers for +url_for_direct_upload+ requests.
+ def headers_for_direct_upload: (untyped key, filename: untyped, content_type: untyped, content_length: untyped, checksum: untyped, ?custom_metadata: ::Hash[untyped, untyped]) -> ::Hash[untyped, untyped]
+
+ def public?: () -> untyped
+
+ def inspect: () -> ::String
+
+ private
+
+ def private_url: (untyped key, expires_in: untyped, filename: untyped, disposition: untyped, content_type: untyped, **untyped) -> untyped
+
+ def public_url: (untyped key, **untyped) -> untyped
+
+ def custom_metadata_headers: (untyped metadata) -> untyped
+
+ def instrument: (untyped operation, ?::Hash[untyped, untyped] payload) ?{ (?) -> untyped } -> untyped
+
+ def service_name: () -> untyped
+
+ def content_disposition_with: (filename: untyped, ?type: ::String) -> untyped
+ end
+end
+
+module ActiveStorage
+ class Service::Configurator
+ attr_reader configurations: untyped
+
+ def self.build: (untyped service_name, untyped configurations) -> untyped
+
+ def initialize: (untyped configurations) -> void
+
+ def build: (untyped service_name) -> untyped
+
+ def inspect: () -> ::String
+
+ private
+
+ def config_for: (untyped name) -> untyped
+
+ def resolve: (untyped class_name) -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \Disk \Service
+ #
+ # Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API
+ # documentation that applies to all services.
+ class Service::DiskService < ::ActiveStorage::Service
+ attr_accessor root: untyped
+
+ def initialize: (root: untyped, ?public: bool, **untyped options) -> void
+
+ def upload: (untyped key, untyped io, ?checksum: untyped?, **untyped) -> untyped
+
+ def download: (untyped key) ?{ (?) -> untyped } -> untyped
+
+ def download_chunk: (untyped key, untyped range) -> untyped
+
+ def delete: (untyped key) -> untyped
+
+ def delete_prefixed: (untyped prefix) -> untyped
+
+ def exist?: (untyped key) -> untyped
+
+ def url_for_direct_upload: (untyped key, expires_in: untyped, content_type: untyped, content_length: untyped, checksum: untyped, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def headers_for_direct_upload: (untyped key, content_type: untyped, **untyped) -> { "Content-Type" => untyped }
+
+ # Every filesystem operation in DiskService resolves paths through this method (or through
+ # make_path_for, which delegates here). This is the primary filesystem security check: all
+ # path-traversal protection is enforced here. New methods that touch the filesystem MUST use
+ # path_for or make_path_for -- never construct paths from +root+ directly.
+ def path_for: (untyped key) -> untyped
+
+ def compose: (untyped source_keys, untyped destination_key, **untyped) -> untyped
+
+ private
+
+ def private_url: (untyped key, expires_in: untyped, filename: untyped, content_type: untyped, disposition: untyped, **untyped) -> untyped
+
+ def public_url: (untyped key, filename: untyped, ?content_type: untyped?, ?disposition: ::Symbol, **untyped) -> untyped
+
+ def generate_url: (untyped key, expires_in: untyped, filename: untyped, content_type: untyped, disposition: untyped) -> untyped
+
+ def stream: (untyped key) { (untyped) -> untyped } -> untyped
+
+ def folder_for: (untyped key) -> untyped
+
+ def escape_glob_metacharacters: (untyped path) -> untyped
+
+ def make_path_for: (untyped key) -> untyped
+
+ def ensure_integrity_of: (untyped key, untyped checksum) -> (untyped | nil)
+
+ def url_helpers: () -> untyped
+
+ def url_options: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \GCS \Service
+ #
+ # Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API
+ # documentation that applies to all services.
+ class Service::GCSService < ::ActiveStorage::Service
+ class MetadataServerError < ::ActiveStorage::Error
+ end
+
+ class MetadataServerNotFoundError < ::ActiveStorage::Error
+ end
+
+ def initialize: (?public: bool, **untyped config) -> void
+
+ def upload: (untyped key, untyped io, ?checksum: untyped?, ?content_type: untyped?, ?disposition: untyped?, ?filename: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def download: (untyped key) ?{ (?) -> untyped } -> untyped
+
+ def update_metadata: (untyped key, content_type: untyped, ?disposition: untyped?, ?filename: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def download_chunk: (untyped key, untyped range) -> untyped
+
+ def delete: (untyped key) -> untyped
+
+ def delete_prefixed: (untyped prefix) -> untyped
+
+ def exist?: (untyped key) -> untyped
+
+ def url_for_direct_upload: (untyped key, expires_in: untyped, checksum: untyped, ?custom_metadata: ::Hash[untyped, untyped], **untyped) -> untyped
+
+ def headers_for_direct_upload: (untyped key, checksum: untyped, ?filename: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped], **untyped) -> untyped
+
+ def compose: (untyped source_keys, untyped destination_key, ?filename: untyped?, ?content_type: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ # Returns the IAM client used for direct uploads and signed URLs.
+ # By default, the authorization for the IAM client is set to
+ # {Application Default Credentials}[https://docs.cloud.google.com/docs/authentication/application-default-credentials],
+ # fetched once at instantiation time, then refreshed automatically when expired.
+ # This can be set to a different value to use other authorization methods.
+ #
+ # ActiveStorage::Blob.service.iam_client.authorization = Google::Auth::ImpersonatedServiceAccountCredentials.new(options)
+ def iam_client: () -> untyped
+
+ private
+
+ def private_url: (untyped key, expires_in: untyped, filename: untyped, content_type: untyped, disposition: untyped, **untyped) -> untyped
+
+ def public_url: (untyped key, **untyped) -> untyped
+
+ attr_reader config: untyped
+
+ def file_for: (untyped key, ?skip_lookup: bool) -> untyped
+
+ # Reads the file for the given key in chunks, yielding each to the block.
+ def stream: (untyped key) { (untyped) -> untyped } -> untyped
+
+ def bucket: () -> untyped
+
+ def client: () -> untyped
+
+ def issuer: () -> untyped
+
+ def email_from_metadata_server: () -> untyped
+
+ def signer: () -> untyped
+
+ def custom_metadata_headers: (untyped metadata) -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage Mirror \Service
+ #
+ # Wraps a set of mirror services and provides a single ActiveStorage::Service object that will all
+ # have the files uploaded to them. A +primary+ service is designated to answer calls to:
+ # * +download+
+ # * +exist?+
+ # * +url+
+ # * +url_for_direct_upload+
+ # * +headers_for_direct_upload+
+ class Service::MirrorService < ::ActiveStorage::Service
+ def download: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def download_chunk: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def exist?: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def url: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def url_for_direct_upload: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def headers_for_direct_upload: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def path_for: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+ def compose: (*untyped, **untyped) ?{ (*untyped) -> untyped } -> untyped
+
+ attr_reader primary: untyped
+
+ attr_reader mirrors: untyped
+
+ # Stitch together from named services.
+ def self.build: (primary: untyped, mirrors: untyped, name: untyped, configurator: untyped, **untyped options) -> untyped
+
+ def initialize: (primary: untyped, mirrors: untyped) -> void
+
+ # Upload the +io+ to the +key+ specified to all services. The upload to the primary service is done synchronously
+ # whereas the upload to the mirrors is done asynchronously. If a +checksum+ is provided, all services will
+ # ensure a match when the upload has completed or raise an ActiveStorage::IntegrityError.
+ def upload: (untyped key, untyped io, ?checksum: untyped?, **untyped options) -> untyped
+
+ # Delete the file at the +key+ on all services.
+ def delete: (untyped key) -> untyped
+
+ # Delete files at keys starting with the +prefix+ on all services.
+ def delete_prefixed: (untyped prefix) -> untyped
+
+ def mirror_later: (untyped key, checksum: untyped) -> untyped
+
+ # Copy the file at the +key+ from the primary service to each of the mirrors where it doesn't already exist.
+ def mirror: (untyped key, checksum: untyped) -> untyped
+
+ private
+
+ def each_service: () { (?) -> untyped } -> untyped
+
+ def perform_across_services: (untyped method, *untyped args) -> untyped
+ end
+end
+
+module ActiveStorage
+ class Service::Registry
+ def initialize: (untyped configurations) -> void
+
+ def fetch: (untyped name) ?{ (untyped) -> untyped } -> untyped
+
+ def inspect: () -> ::String
+
+ private
+
+ attr_reader configurations: untyped
+
+ attr_reader services: untyped
+
+ def configurator: () -> untyped
+ end
+end
+
+module ActiveStorage
+ # = Active Storage \S3 \Service
+ #
+ # Wraps the Amazon Simple Storage Service (S3) as an Active Storage service.
+ # See ActiveStorage::Service for the generic API documentation that applies to all services.
+ class Service::S3Service < ::ActiveStorage::Service
+ attr_reader client: untyped
+
+ attr_reader bucket: untyped
+
+ attr_reader multipart_upload_threshold: untyped
+
+ attr_reader upload_options: untyped
+
+ def initialize: (bucket: untyped, ?upload: ::Hash[untyped, untyped], ?public: bool, **untyped options) -> void
+
+ def upload: (untyped key, untyped io, ?checksum: untyped?, ?filename: untyped?, ?content_type: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped], **untyped) -> untyped
+
+ def download: (untyped key) ?{ (?) -> untyped } -> untyped
+
+ def download_chunk: (untyped key, untyped range) -> untyped
+
+ def delete: (untyped key) -> untyped
+
+ def delete_prefixed: (untyped prefix) -> untyped
+
+ def exist?: (untyped key) -> untyped
+
+ def url_for_direct_upload: (untyped key, expires_in: untyped, content_type: untyped, content_length: untyped, checksum: untyped, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def headers_for_direct_upload: (untyped key, content_type: untyped, checksum: untyped, ?filename: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped], **untyped) -> ::Hash["Content-Type" | "Content-MD5" | "Content-Disposition" | untyped, untyped]
+
+ def compose: (untyped source_keys, untyped destination_key, ?filename: untyped?, ?content_type: untyped?, ?disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ private
+
+ def upload_stream: (key: untyped, **untyped options) { (?) -> untyped } -> untyped
+
+ def private_url: (untyped key, expires_in: untyped, filename: untyped, disposition: untyped, content_type: untyped, **untyped client_opts) -> untyped
+
+ def public_url: (untyped key, **untyped client_opts) -> untyped
+
+ MAXIMUM_UPLOAD_PARTS_COUNT: 10000
+
+ MINIMUM_UPLOAD_PART_SIZE: untyped
+
+ def upload_with_single_part: (untyped key, untyped io, ?checksum: untyped?, ?content_type: untyped?, ?content_disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def upload_with_multipart: (untyped key, untyped io, ?content_type: untyped?, ?content_disposition: untyped?, ?custom_metadata: ::Hash[untyped, untyped]) -> untyped
+
+ def object_for: (untyped key) -> untyped
+
+ # Reads the object for the given key in chunks, yielding each to the block.
+ def stream: (untyped key) { (untyped) -> untyped } -> untyped
+
+ def custom_metadata_headers: (untyped metadata) -> untyped
+ end
+end
+
+module ActiveStorage
+ class StructuredEventSubscriber < ::ActiveSupport::StructuredEventSubscriber
+ def service_upload: (untyped event) -> untyped
+
+ def service_download: (untyped event) -> untyped
+
+ def service_streaming_download: (untyped event) -> untyped
+
+ def preview: (untyped event) -> untyped
+
+ def service_delete: (untyped event) -> untyped
+
+ def service_delete_prefixed: (untyped event) -> untyped
+
+ def service_exist: (untyped event) -> untyped
+
+ def service_url: (untyped event) -> untyped
+
+ def service_mirror: (untyped event) -> untyped
+ end
+end
+
+module ActiveStorage
+ module Transformers
+ class ImageMagick < ::ActiveStorage::Transformers::ImageProcessingTransformer
+ private
+
+ def processor: () -> untyped
+
+ def validate_transformation: (untyped name, untyped argument) -> untyped
+
+ def validate_arg_string: (untyped argument) -> untyped
+
+ def validate_arg_array: (untyped argument) -> untyped
+
+ def validate_arg_hash: (untyped argument) -> untyped
+ end
+ end
+end
+
+module ActiveStorage
+ module Transformers
+ class ImageProcessingTransformer < ::ActiveStorage::Transformers::Transformer
+ private
+
+ class UnsupportedImageProcessingMethod < ::StandardError
+ end
+
+ class UnsupportedImageProcessingArgument < ::StandardError
+ end
+
+ def process: (untyped file, format: untyped) -> untyped
+
+ def operations: () -> untyped
+
+ def validate_transformation: (untyped name, untyped argument) -> (untyped | nil)
+ end
+ end
+end
+
+module ActiveStorage
+ module Transformers
+ class NullTransformer < ::ActiveStorage::Transformers::Transformer
+ private
+
+ def process: (untyped file, format: untyped) -> untyped
+ end
+ end
+end
+
+module ActiveStorage
+ module Transformers
+ # = Active Storage \Transformers \Transformer
+ #
+ # A Transformer applies a set of transformations to an image.
+ #
+ # The following concrete subclasses are included in Active Storage:
+ #
+ # * ActiveStorage::Transformers::ImageProcessingTransformer:
+ # backed by ImageProcessing, a common interface for MiniMagick and ruby-vips
+ class Transformer
+ attr_reader transformations: untyped
+
+ def initialize: (untyped transformations) -> void
+
+ # Applies the transformations to the source image in +file+, producing a target image in the
+ # specified +format+. Yields an open Tempfile containing the target image. Closes and unlinks
+ # the output tempfile after yielding to the given block. Returns the result of the block.
+ def transform: (untyped file, format: untyped) { (untyped) -> untyped } -> untyped
+
+ private
+
+ # Returns an open Tempfile containing a transformed image in the given +format+.
+ # All subclasses implement this method.
+ def process: (untyped file, format: untyped) -> untyped
+ end
+ end
+end
+
+module ActiveStorage
+ module Transformers
+ class Vips < ::ActiveStorage::Transformers::ImageProcessingTransformer
+ def processor: () -> untyped
+ end
+ end
+end
+
+module ActiveStorage
+ # Returns the currently loaded version of Active Storage as a +Gem::Version+.
+ def self.version: () -> untyped
+end
+
+module ActiveStorage
+ module Attached::Model
+ module ClassMethods
+ end
+ end
+end
+
+module ActiveStorage::Blobs
+end
+
+module ActiveStorage::Representations
+end
diff --git a/gems/activestorage/8.1/app/models/active_storage/attachment.rbs b/gems/activestorage/8.1/app/models/active_storage/attachment.rbs
new file mode 100644
index 000000000..40ddb3230
--- /dev/null
+++ b/gems/activestorage/8.1/app/models/active_storage/attachment.rbs
@@ -0,0 +1,21 @@
+# Attachments associate records with blobs. Usually that's a one record-many blobs relationship,
+# but it is possible to associate many different records with the same blob. A foreign-key constraint
+# on the attachments table prevents blobs from being purged if they’re still attached to any records.
+#
+# Attachments also have access to all methods from {ActiveStorage::Blob}[rdoc-ref:ActiveStorage::Blob].
+class ActiveStorage::Attachment < ActiveStorage::Record
+ # Synchronously deletes the attachment and {purges the blob}[rdoc-ref:ActiveStorage::Blob#purge].
+ def purge: () -> untyped
+
+ # Deletes the attachment and {enqueues a background job}[rdoc-ref:ActiveStorage::Blob#purge_later] to purge the blob.
+ def purge_later: () -> untyped
+
+ # Columns and associations, which rbs prototype doesn't see
+ def name: () -> String
+
+ def record: () -> untyped
+
+ def blob: () -> ActiveStorage::Blob
+
+ def created_at: () -> ActiveSupport::TimeWithZone
+end
diff --git a/gems/activestorage/8.1/app/models/active_storage/blob.rbs b/gems/activestorage/8.1/app/models/active_storage/blob.rbs
new file mode 100644
index 000000000..311548a22
--- /dev/null
+++ b/gems/activestorage/8.1/app/models/active_storage/blob.rbs
@@ -0,0 +1,197 @@
+# A blob is a record that contains the metadata about a file and a key for where that file resides on the service.
+# Blobs can be created in two ways:
+#
+# 1. Ahead of the file being uploaded server-side to the service, via create_and_upload!. A rewindable
+# io with the file contents must be available at the server for this operation.
+# 2. Ahead of the file being directly uploaded client-side to the service, via create_before_direct_upload!.
+#
+# The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end
+# service that deals with files. The second option is faster, since you're not using your own server as a staging
+# point for uploads, and can work with deployments like Heroku that do not provide large amounts of disk space.
+#
+# Blobs are intended to be immutable in as-so-far as their reference to a specific file goes. You're allowed to
+# update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file.
+# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one.
+class ActiveStorage::Blob < ActiveStorage::Record
+ # We use constant paths in the following include calls to avoid a gotcha of
+ # classic mode: If the parent application defines a top-level Analyzable, for
+ # example, and ActiveStorage::Blob::Analyzable is not yet loaded, a bare
+ #
+ # include Analyzable
+ #
+ # would resolve to the top-level one, const_missing would not be triggered,
+ # and therefore ActiveStorage::Blob::Analyzable would not be autoloaded.
+ #
+ # By using qualified names, we ensure const_missing is invoked if needed.
+ # Please, note that Ruby 2.5 or newer is required, so Object is not checked
+ # when looking up the ancestors of ActiveStorage::Blob.
+ #
+ # Zeitwerk mode does not have this gotcha. If we ever drop classic mode, this
+ # can be simplified, bare constant names would just work.
+ # include ActiveStorage::Blob::Analyzable
+
+ # include ActiveStorage::Blob::Identifiable
+
+ # include ActiveStorage::Blob::Representable
+
+ MINIMUM_TOKEN_LENGTH: ::Integer
+
+ # You can use the signed ID of a blob to refer to it on the client side without fear of tampering.
+ # This is particularly helpful for direct uploads where the client-side needs to refer to the blob
+ # that was created ahead of the upload itself on form submission.
+ #
+ # The signed ID is also used to create stable URLs for the blob through the BlobsController.
+ def self.find_signed: (untyped id, ?record: untyped? record, ?purpose: ::Symbol purpose) -> untyped
+
+ # Works like +find_signed+, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
+ # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
+ # or has been tampered with. It will also raise an +ActiveRecord::RecordNotFound+ exception if
+ # the valid signed id can't find a record.
+ def self.find_signed!: (untyped id, ?record: untyped? record, ?purpose: ::Symbol purpose) -> untyped
+
+ def self.build_after_unfurling: (io: untyped io, filename: untyped filename, ?key: untyped? key, ?content_type: untyped? content_type, ?metadata: untyped? metadata, ?service_name: untyped? service_name, ?identify: bool identify, ?record: untyped? record) -> untyped
+
+ def self.create_after_unfurling!: (io: untyped io, filename: untyped filename, ?key: untyped? key, ?content_type: untyped? content_type, ?metadata: untyped? metadata, ?service_name: untyped? service_name, ?identify: bool identify, ?record: untyped? record) -> untyped
+
+ # Creates a new blob instance and then uploads the contents of
+ # the given io to the service. The blob instance is going to
+ # be saved before the upload begins to prevent the upload clobbering another due to key collisions.
+ # When providing a content type, pass identify: false to bypass
+ # automatic content type inference.
+ def self.create_and_upload!: (io: untyped io, filename: untyped filename, ?key: untyped? key, ?content_type: untyped? content_type, ?metadata: untyped? metadata, ?service_name: untyped? service_name, ?identify: bool identify, ?record: untyped? record) -> untyped
+
+ alias self.create_after_upload! self.create_and_upload!
+
+ # Returns a saved blob _without_ uploading a file to the service. This blob will point to a key where there is
+ # no file yet. It's intended to be used together with a client-side upload, which will first create the blob
+ # in order to produce the signed URL for uploading. This signed URL points to the key generated by the blob.
+ # Once the form using the direct upload is submitted, the blob can be associated with the right record using
+ # the signed ID.
+ def self.create_before_direct_upload!: (filename: untyped filename, byte_size: untyped byte_size, checksum: untyped checksum, ?key: untyped? key, ?content_type: untyped? content_type, ?metadata: untyped? metadata, ?service_name: untyped? service_name, ?record: untyped? record) -> untyped
+
+ # To prevent problems with case-insensitive filesystems, especially in combination
+ # with databases which treat indices as case-sensitive, all blob keys generated are going
+ # to only contain the base-36 character alphabet and will therefore be lowercase. To maintain
+ # the same or higher amount of entropy as in the base-58 encoding used by `has_secure_token`
+ # the number of bytes used is increased to 28 from the standard 24
+ def self.generate_unique_secure_token: (?length: untyped length) -> untyped
+
+ def self.combine_signed_id_purposes: (untyped purpose) -> untyped
+
+ def self.signed_id_verifier: () -> untyped
+
+ # Returns a signed ID for this blob that's suitable for reference on the client-side without fear of tampering.
+ def signed_id: (?purpose: Symbol, ?expires_in: untyped, ?expires_at: untyped) -> String
+
+ # Returns the key pointing to the file on the service that's associated with this blob. The key is the
+ # secure-token format from Rails in lower case. So it'll look like: xtapjjcjiudrlk3tmwyjgpuobabd.
+ # This key is not intended to be revealed directly to the user.
+ # Always refer to blobs using the signed_id or a verified form of the key.
+ def key: () -> String
+
+ # Returns an ActiveStorage::Filename instance of the filename that can be
+ # queried for basename, extension, and a sanitized version of the filename
+ # that's safe to use in URLs.
+ def filename: () -> ActiveStorage::Filename
+
+ # Returns true if the content_type of this blob is in the image range, like image/png.
+ def image?: () -> bool?
+
+ # Returns true if the content_type of this blob is in the audio range, like audio/mpeg.
+ def audio?: () -> bool?
+
+ # Returns true if the content_type of this blob is in the video range, like video/mp4.
+ def video?: () -> bool?
+
+ # Returns true if the content_type of this blob is in the text range, like text/plain.
+ def text?: () -> bool?
+
+ # Returns the URL of the blob on the service. This returns a permanent URL for public files, and returns a
+ # short-lived URL for private files. Private files are signed, and not for public use. Instead,
+ # the URL should only be exposed as a redirect from a stable, possibly authenticated URL. Hiding the
+ # URL behind a redirect also allows you to change services without updating all URLs.
+ def url: (?expires_in: untyped expires_in, ?disposition: ::Symbol disposition, ?filename: untyped? filename, **untyped options) -> String
+
+ # Returns a URL that can be used to directly upload a file for this blob on the service. This URL is intended to be
+ # short-lived for security and only generated on-demand by the client-side JavaScript responsible for doing the uploading.
+ def service_url_for_direct_upload: (?expires_in: untyped expires_in) -> String
+
+ # Returns a Hash of headers for +service_url_for_direct_upload+ requests.
+ def service_headers_for_direct_upload: () -> Hash[String, String]
+
+ def content_type_for_serving: () -> untyped
+
+ def forced_disposition_for_serving: () -> untyped
+
+ # Uploads the +io+ to the service on the +key+ for this blob. Blobs are intended to be immutable, so you shouldn't be
+ # using this method after a file has already been uploaded to fit with a blob. If you want to create a derivative blob,
+ # you should instead simply create a new blob based on the old one.
+ #
+ # Prior to uploading, we compute the checksum, which is sent to the service for transit integrity validation. If the
+ # checksum does not match what the service receives, an exception will be raised. We also measure the size of the +io+
+ # and store that in +byte_size+ on the blob record. The content type is automatically extracted from the +io+ unless
+ # you specify a +content_type+ and pass +identify+ as false.
+ #
+ # Normally, you do not have to call this method directly at all. Use the +create_and_upload!+ class method instead.
+ # If you do use this method directly, make sure you are using it on a persisted Blob as otherwise another blob's
+ # data might get overwritten on the service.
+ def upload: (untyped io, ?identify: bool identify) -> untyped
+
+ def unfurl: (untyped io, ?identify: bool identify) -> untyped
+
+ def upload_without_unfurling: (untyped io) -> untyped
+
+ # Downloads the file associated with this blob. If no block is given, the entire file is read into memory and returned.
+ # That'll use a lot of RAM for very large files. If a block is given, then the download is streamed and yielded in chunks.
+ def download: () { (String) -> void } -> void
+ | () -> String
+
+ # Downloads the blob to a tempfile on disk. Yields the tempfile.
+ #
+ # The tempfile's name is prefixed with +ActiveStorage-+ and the blob's ID. Its extension matches that of the blob.
+ #
+ # By default, the tempfile is created in Dir.tmpdir. Pass +tmpdir:+ to create it in a different directory:
+ #
+ # blob.open(tmpdir: "/path/to/tmp") do |file|
+ # # ...
+ # end
+ #
+ # The tempfile is automatically closed and unlinked after the given block is executed.
+ #
+ # Raises ActiveStorage::IntegrityError if the downloaded data does not match the blob's checksum.
+ def open: (?tmpdir: untyped? tmpdir) { (Tempfile) -> untyped } -> untyped
+
+ def mirror_later: () -> untyped
+
+ # Deletes the files on the service associated with the blob. This should only be done if the blob is going to be
+ # deleted as well or you will essentially have a dead reference. It's recommended to use #purge and #purge_later
+ # methods in most circumstances.
+ def delete: () -> untyped
+
+ # Destroys the blob record and then deletes the file on the service. This is the recommended way to dispose of unwanted
+ # blobs. Note, though, that deleting the file off the service will initiate an HTTP connection to the service, which may
+ # be slow or prevented, so you should not use this method inside a transaction or in callbacks. Use #purge_later instead.
+ def purge: () -> untyped
+
+ # Enqueues an ActiveStorage::PurgeJob to call #purge. This is the recommended way to purge blobs from a transaction,
+ # an Active Record callback, or in any other real-time scenario.
+ def purge_later: () -> untyped
+
+ # Returns an instance of service, which can be configured globally or per attachment
+ def service: () -> untyped
+
+ # Columns and associations, which rbs prototype doesn't see
+ attr_accessor content_type: String?
+
+ attr_accessor metadata: Hash[String, untyped]
+
+ attr_accessor service_name: String
+
+ attr_accessor byte_size: Integer
+
+ attr_accessor checksum: String?
+
+ def created_at: () -> ActiveSupport::TimeWithZone
+
+ def attachments: () -> untyped
+end
diff --git a/gems/activestorage/8.1/app/models/active_storage/current.rbs b/gems/activestorage/8.1/app/models/active_storage/current.rbs
new file mode 100644
index 000000000..682a704ff
--- /dev/null
+++ b/gems/activestorage/8.1/app/models/active_storage/current.rbs
@@ -0,0 +1,8 @@
+module ActiveStorage
+ class Current < ActiveSupport::CurrentAttributes
+ def self.url_options: () -> Hash[Symbol, untyped]
+ def self.url_options=: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
+ def url_options: () -> Hash[Symbol, untyped]
+ def url_options=: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
+ end
+end
diff --git a/gems/activestorage/8.1/app/models/active_storage/filename.rbs b/gems/activestorage/8.1/app/models/active_storage/filename.rbs
new file mode 100644
index 000000000..31c754d73
--- /dev/null
+++ b/gems/activestorage/8.1/app/models/active_storage/filename.rbs
@@ -0,0 +1,15 @@
+class ActiveStorage::Filename
+ def base: () -> String
+
+ def extension_with_delimiter: () -> String
+
+ def extension_without_delimiter: () -> String
+
+ alias extension extension_without_delimiter
+
+ def sanitized: () -> String
+
+ def to_s: () -> String
+
+ alias to_str to_s
+end
diff --git a/gems/activestorage/8.1/app/models/active_storage/record.rbs b/gems/activestorage/8.1/app/models/active_storage/record.rbs
new file mode 100644
index 000000000..db904304e
--- /dev/null
+++ b/gems/activestorage/8.1/app/models/active_storage/record.rbs
@@ -0,0 +1,2 @@
+class ActiveStorage::Record < ActiveRecord::Base #:nodoc:
+end
diff --git a/gems/activestorage/8.1/lib/attached.rbs b/gems/activestorage/8.1/lib/attached.rbs
new file mode 100644
index 000000000..b00f98e95
--- /dev/null
+++ b/gems/activestorage/8.1/lib/attached.rbs
@@ -0,0 +1,11 @@
+module ActiveStorage
+ # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many
+ # classes that both provide proxy access to the blob association for a record.
+ class Attached
+ attr_reader name: String
+
+ attr_reader record: untyped # Model
+
+ def initialize: (String name, untyped record) -> void
+ end
+end
diff --git a/gems/activestorage/8.1/lib/attached/many.rbs b/gems/activestorage/8.1/lib/attached/many.rbs
new file mode 100644
index 000000000..a47f08087
--- /dev/null
+++ b/gems/activestorage/8.1/lib/attached/many.rbs
@@ -0,0 +1,38 @@
+module ActiveStorage
+ # Decorated proxy object representing of multiple attachments to a model.
+ class Attached::Many < Attached
+ include _ActiveRecord_Relation[ActiveStorage::Attachment, Integer]
+
+ # Returns all the associated attachment records.
+ #
+ # All methods called on this proxy object that aren't listed here will automatically be delegated to +attachments+.
+ def attachments: () -> Array[ActiveStorage::Attachment]
+
+ # Returns all attached blobs.
+ def blobs: () -> untyped
+
+ # Attaches one or more +attachables+ to the record.
+ #
+ # If the record is persisted and unchanged, the attachments are saved to
+ # the database immediately. Otherwise, they'll be saved to the DB when the
+ # record is next saved.
+ #
+ # document.images.attach(params[:images]) # Array of ActionDispatch::Http::UploadedFile objects
+ # document.images.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
+ # document.images.attach(io: File.open("/path/to/racecar.jpg"), filename: "racecar.jpg", content_type: "image/jpg")
+ # document.images.attach([ first_blob, second_blob ])
+ def attach: (*untyped attachables) -> untyped
+
+ # Returns true if any attachments has been made.
+ #
+ # class Gallery < ActiveRecord::Base
+ # has_many_attached :photos
+ # end
+ #
+ # Gallery.new.photos.attached? # => false
+ def attached?: () -> bool
+
+ # Deletes associated attachments without purging them, leaving their respective blobs in place.
+ def detach: () -> untyped
+ end
+end
diff --git a/gems/activestorage/8.1/lib/attached/model.rbs b/gems/activestorage/8.1/lib/attached/model.rbs
new file mode 100644
index 000000000..b7b790405
--- /dev/null
+++ b/gems/activestorage/8.1/lib/attached/model.rbs
@@ -0,0 +1,107 @@
+module ActiveStorage
+ # Provides the class-level DSL for declaring an Active Record model's attachments.
+ module Attached::Model
+ extend ActiveSupport::Concern
+
+ # mutate class_methods
+ module ClassMethods
+ # Specifies the relation between a single attachment and the model.
+ #
+ # class User < ApplicationRecord
+ # has_one_attached :avatar
+ # end
+ #
+ # There is no column defined on the model side, Active Storage takes
+ # care of the mapping between your records and the attachment.
+ #
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
+ #
+ # User.with_attached_avatar
+ #
+ # Under the covers, this relationship is implemented as a +has_one+ association to a
+ # ActiveStorage::Attachment record and a +has_one-through+ association to a
+ # ActiveStorage::Blob record. These associations are available as +avatar_attachment+
+ # and +avatar_blob+. But you shouldn't need to work with these associations directly in
+ # most circumstances.
+ #
+ # The system has been designed to having you go through the ActiveStorage::Attached::One
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+.
+ #
+ # If the +:dependent+ option isn't set, the attachment will be purged
+ # (i.e. destroyed) whenever the record is destroyed.
+ #
+ # If you need the attachment to use a service which differs from the globally configured one,
+ # pass the +:service+ option. For instance:
+ #
+ # class User < ActiveRecord::Base
+ # has_one_attached :avatar, service: :s3
+ # end
+ #
+ # If you need to enable +strict_loading+ to prevent lazy loading of attachment,
+ # pass the +:strict_loading+ option. You can do:
+ #
+ # class User < ApplicationRecord
+ # has_one_attached :avatar, strict_loading: true
+ # end
+ #
+ def has_one_attached: (
+ (::String | ::Symbol) name,
+ ?dependent: ::Symbol dependent,
+ ?service: (::String | ::Symbol | nil) service,
+ ?strict_loading: bool strict_loading
+ ) ?{ (ActiveStorage::Reflection::HasOneAttachedReflection reflection) -> void } -> void
+
+ # Specifies the relation between multiple attachments and the model.
+ #
+ # class Gallery < ApplicationRecord
+ # has_many_attached :photos
+ # end
+ #
+ # There are no columns defined on the model side, Active Storage takes
+ # care of the mapping between your records and the attachments.
+ #
+ # To avoid N+1 queries, you can include the attached blobs in your query like so:
+ #
+ # Gallery.where(user: Current.user).with_attached_photos
+ #
+ # Under the covers, this relationship is implemented as a +has_many+ association to a
+ # ActiveStorage::Attachment record and a +has_many-through+ association to a
+ # ActiveStorage::Blob record. These associations are available as +photos_attachments+
+ # and +photos_blobs+. But you shouldn't need to work with these associations directly in
+ # most circumstances.
+ #
+ # The system has been designed to having you go through the ActiveStorage::Attached::Many
+ # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+.
+ #
+ # If the +:dependent+ option isn't set, all the attachments will be purged
+ # (i.e. destroyed) whenever the record is destroyed.
+ #
+ # If you need the attachment to use a service which differs from the globally configured one,
+ # pass the +:service+ option. For instance:
+ #
+ # class Gallery < ActiveRecord::Base
+ # has_many_attached :photos, service: :s3
+ # end
+ #
+ # If you need to enable +strict_loading+ to prevent lazy loading of attachments,
+ # pass the +:strict_loading+ option. You can do:
+ #
+ # class Gallery < ApplicationRecord
+ # has_many_attached :photos, strict_loading: true
+ # end
+ #
+ def has_many_attached: (
+ (::String | ::Symbol) name,
+ ?dependent: ::Symbol dependent,
+ ?service: (::String | ::Symbol | nil) service,
+ ?strict_loading: bool strict_loading
+ ) ?{ (ActiveStorage::Reflection::HasManyAttachedReflection reflection) -> void } -> void
+ end
+
+ def attachment_changes: () -> void
+
+ def changed_for_autosave?: () -> bool
+
+ def reload: (?lock: boolish) -> self
+ end
+end
diff --git a/gems/activestorage/8.1/lib/attached/one.rbs b/gems/activestorage/8.1/lib/attached/one.rbs
new file mode 100644
index 000000000..84909021f
--- /dev/null
+++ b/gems/activestorage/8.1/lib/attached/one.rbs
@@ -0,0 +1,46 @@
+module ActiveStorage
+ # Representation of a single attachment to a model.
+ class Attached::One < Attached
+ # delegated from Blob
+ def url: () -> String
+
+ # Returns the associated attachment record.
+ #
+ # You don't have to call this method to access the attachment's methods as
+ # they are all available at the model level.
+ def attachment: () -> ActiveStorage::Attachment?
+
+ def blank?: () -> bool
+
+ # Attaches an +attachable+ to the record.
+ #
+ # If the record is persisted and unchanged, the attachment is saved to
+ # the database immediately. Otherwise, it'll be saved to the DB when the
+ # record is next saved.
+ #
+ # person.avatar.attach(params[:avatar]) # ActionDispatch::Http::UploadedFile object
+ # person.avatar.attach(params[:signed_blob_id]) # Signed reference to blob from direct upload
+ # person.avatar.attach(io: File.open("/path/to/face.jpg"), filename: "face.jpg", content_type: "image/jpg")
+ # person.avatar.attach(avatar_blob) # ActiveStorage::Blob object
+ def attach: (untyped attachable) -> untyped
+
+ # Returns +true+ if an attachment has been made.
+ #
+ # class User < ActiveRecord::Base
+ # has_one_attached :avatar
+ # end
+ #
+ # User.new.avatar.attached? # => false
+ def attached?: () -> bool
+
+ # Deletes the attachment without purging it, leaving its blob in place.
+ def detach: () -> untyped
+
+ # Directly purges the attachment (i.e. destroys the blob and
+ # attachment and deletes the file on the service).
+ def purge: () -> untyped
+
+ # Purges the attachment through the queuing system.
+ def purge_later: () -> untyped
+ end
+end
diff --git a/gems/activestorage/8.1/lib/engine.rbs b/gems/activestorage/8.1/lib/engine.rbs
new file mode 100644
index 000000000..64e3053e4
--- /dev/null
+++ b/gems/activestorage/8.1/lib/engine.rbs
@@ -0,0 +1,10 @@
+module ActiveRecord
+ class Base
+ include ::ActiveStorage::Attached::Model
+ extend ::ActiveStorage::Attached::Model::ClassMethods
+ include ::ActiveStorage::Reflection::ActiveRecordExtensions
+ # included in ActiveRecordExtensions
+ attr_accessor self.attachment_reflections: Hash[String, ActiveStorage::Reflection::HasOneAttachedReflection | ActiveStorage::Reflection::HasManyAttachedReflection]
+ extend ::ActiveStorage::Reflection::ActiveRecordExtensions::ClassMethods
+ end
+end
diff --git a/gems/activestorage/8.1/lib/reflection.rbs b/gems/activestorage/8.1/lib/reflection.rbs
new file mode 100644
index 000000000..e8cdbd42d
--- /dev/null
+++ b/gems/activestorage/8.1/lib/reflection.rbs
@@ -0,0 +1,32 @@
+module ActiveStorage
+ module Reflection
+ # Holds all the metadata about a has_one_attached attachment as it was
+ # specified in the Active Record class.
+ class HasOneAttachedReflection < HasAttachedReflection #:nodoc:
+ def macro: () -> :has_one_attached
+ end
+
+ # Holds all the metadata about a has_many_attached attachment as it was
+ # specified in the Active Record class.
+ class HasManyAttachedReflection < HasAttachedReflection #:nodoc:
+ def macro: () -> :has_many_attached
+ end
+
+ module ActiveRecordExtensions
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Returns an array of reflection objects for all the attachments in the
+ # class.
+ def reflect_on_all_attachments: () -> Array[ActiveStorage::Reflection::HasOneAttachedReflection | ActiveStorage::Reflection::HasManyAttachedReflection]
+
+ # Returns the reflection object for the named +attachment+.
+ #
+ # User.reflect_on_attachment(:avatar)
+ # # => the avatar reflection
+ #
+ def reflect_on_attachment: ((String | Symbol) attachment) -> (ActiveStorage::Reflection::HasOneAttachedReflection | ActiveStorage::Reflection::HasManyAttachedReflection | nil)
+ end
+ end
+ end
+end
diff --git a/gems/activestorage/8.1/manifest.yaml b/gems/activestorage/8.1/manifest.yaml
new file mode 100644
index 000000000..ab31da323
--- /dev/null
+++ b/gems/activestorage/8.1/manifest.yaml
@@ -0,0 +1,2 @@
+dependencies:
+ - name: tempfile