# file: lib/s3.rb require 'aws-sdk-s3' require 'mime/types' module S3 def config @config ||= Hash.new end def config=(config) @config = config end def config_for(key) config.fetch(key.to_s) { config.fetch(key.to_sym) } end def region config_for(:region) end def access_key_id config_for(:access_key_id) end def secret_access_key config_for(:secret_access_key) end def bucket config_for(:bucket) end def client Aws::S3::Client.new( region:, access_key_id:, secret_access_key:, ) end def url_for(key) "https://#{bucket}.s3.amazonaws.com/#{key}" end def write(key, body, **kws) kws[:content_type] ||= MIME::Types.type_for(key).first.to_s kws[:content_disposition] ||= 'inline' args = { bucket:, key:, body:, **kws } obj = client.put_object(**args) url = url_for(key) { key:, url: }.update(args) end alias_method :put, :write def read(key, &block) obj = client.get_object(bucket:, key: key) if block while (chunk = obj.body.read(1024)) block.call(chunk) end else obj.body.string end end alias_method :get, :read def list(prefix = '', limit: nil, &block) args = { bucket:, prefix: } accum = [] n = 0 client.list_objects_v2(**args).each do |response| response.contents.each do |obj| key = obj.key block ? block.call(key) : accum.push(key) n += 1 break if limit && (n >= limit) end break if limit && (n >= limit) end block ? nil : accum end alias_method :ls, :list def delete(key, *keys) keys.unshift(key) keys.flatten! keys.compact! to_delete = keys.map { { key: } } to_delete.each_slice(1000) do |objects| args = { bucket:, delete: { objects: } } client.delete_objects(args) end keys end alias_method :rm, :delete def exist(key) begin client.head_object(bucket:, key: key) true rescue Aws::S3::Errors::NotFound => e false end end alias_method :exist?, :exist alias_method :exists?, :exist def fetch(key, &block) begin read(key) rescue Aws::S3::Errors::NoSuchKey => _error block.call.tap { |obj| write(key, obj) } end end extend self end