# frozen_string_literal: truerequire"active_storage/log_subscriber"require"active_storage/downloader"require"action_dispatch"require"action_dispatch/http/content_disposition"moduleActiveStorage# 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.# * +AzureStorage+, to manage attachments through Microsoft Azure Storage.# * +Mirror+, to be able to use several services to manage attachments.## Inside a Rails application, you can set-up your services through the# generated <tt>config/storage.yml</tt> 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(# :Disk,# root: Pathname("/foo/bar/storage")# )classServiceextendActiveSupport::Autoloadautoload:Configuratorclass<<self# 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.defconfigure(service_name,configurations)Configurator.build(service_name,configurations)end# 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.defbuild(configurator:,service: nil,**service_config)#:nodoc:new(**service_config)endend# 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.defupload(key,io,checksum: nil,**options)raiseNotImplementedErrorend# 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.defupdate_metadata(key,**metadata)end# Return the content of the file at the +key+.defdownload(key)raiseNotImplementedErrorend# Return the partial content in the byte +range+ of the file at the +key+.defdownload_chunk(key,range)raiseNotImplementedErrorenddefopen(*args,**options,&block)ActiveStorage::Downloader.new(self).open(*args,**options,&block)end# Delete the file at the +key+.defdelete(key)raiseNotImplementedErrorend# Delete files at keys starting with the +prefix+.defdelete_prefixed(prefix)raiseNotImplementedErrorend# Return +true+ if a file exists at the +key+.defexist?(key)raiseNotImplementedErrorend# Returns a signed, temporary URL for the file at the +key+. The URL will be valid for the amount# of seconds specified in +expires_in+. You must also provide the +disposition+ (+:inline+ or +:attachment+),# +filename+, and +content_type+ that you wish the file to be served with on request.defurl(key,expires_in:,disposition:,filename:,content_type:)raiseNotImplementedErrorend# 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.defurl_for_direct_upload(key,expires_in:,content_type:,content_length:,checksum:)raiseNotImplementedErrorend# Returns a Hash of headers for +url_for_direct_upload+ requests.defheaders_for_direct_upload(key,filename:,content_type:,content_length:,checksum:){}endprivatedefinstrument(operation,payload={},&block)ActiveSupport::Notifications.instrument("service_#{operation}.active_storage",payload.merge(service: service_name),&block)enddefservice_name# ActiveStorage::Service::DiskService => Diskself.class.name.split("::").third.remove("Service")enddefcontent_disposition_with(type: "inline",filename:)disposition=(type.to_s.presence_in(%w( attachment inline ))||"inline")ActionDispatch::Http::ContentDisposition.format(disposition: disposition,filename: filename.sanitized)endendend