Module: Studio::S3

Defined in:
lib/studio/s3.rb

Defined Under Namespace

Classes: Error, NotConfigured

Class Method Summary collapse

Class Method Details

.bucketObject

Raises:



46
47
48
49
50
# File 'lib/studio/s3.rb', line 46

def bucket
  prefix = Studio.s3_bucket_prefix
  raise NotConfigured, "Studio.s3_bucket_prefix not set in config/initializers/studio.rb" if prefix.nil? || prefix.empty?
  "#{prefix}-#{environment}"
end

.clientObject



90
91
92
93
94
95
# File 'lib/studio/s3.rb', line 90

def client
  @client ||= begin
    require "aws-sdk-s3"
    Aws::S3::Client.new(region: region)
  end
end

.configured?Boolean

Whether this app can touch object storage at all. Callers that must degrade rather than 500 (the /admin/emails uploader on an app whose bucket was never provisioned) ask this instead of rescuing NotConfigured.

Returns:

  • (Boolean)


55
56
57
58
59
60
# File 'lib/studio/s3.rb', line 55

def configured?
  bucket
  true
rescue NotConfigured
  false
end

.delete(key:) ⇒ Object



35
36
37
# File 'lib/studio/s3.rb', line 35

def delete(key:)
  client.delete_object(bucket: bucket, key: full_key(key))
end

.download(key:) ⇒ Object



15
16
17
# File 'lib/studio/s3.rb', line 15

def download(key:)
  client.get_object(bucket: bucket, key: full_key(key)).body.read
end

.exists?(key:) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
31
32
33
# File 'lib/studio/s3.rb', line 28

def exists?(key:)
  client.head_object(bucket: bucket, key: full_key(key))
  true
rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::NoSuchKey
  false
end

.full_key(key) ⇒ Object

Logical key -> the real object key in the bucket.



72
73
74
75
76
# File 'lib/studio/s3.rb', line 72

def full_key(key)
  return key if key.nil?

  "#{key_prefix}#{key}"
end

.key_prefixObject

Studio.s3_key_prefix, normalized to "" or "something/". The namespace a satellite app lives under when it shares another app's bucket.



64
65
66
67
68
69
# File 'lib/studio/s3.rb', line 64

def key_prefix
  prefix = Studio.s3_key_prefix.to_s
  return "" if prefix.empty?

  prefix.end_with?("/") ? prefix : "#{prefix}/"
end

.list(prefix: nil, max: 1000) ⇒ Object

Returns LOGICAL keys (the app's key namespace stripped back off), so a caller can feed any result straight back into download/delete/url.



41
42
43
44
# File 'lib/studio/s3.rb', line 41

def list(prefix: nil, max: 1000)
  resp = client.list_objects_v2(bucket: bucket, prefix: full_key(prefix), max_keys: max)
  resp.contents.map { |object| logical_key(object.key) }
end

.logical_key(key) ⇒ Object

The real object key -> logical key (inverse of full_key).



79
80
81
82
83
84
# File 'lib/studio/s3.rb', line 79

def logical_key(key)
  prefix = key_prefix
  return key if prefix.empty? || !key.to_s.start_with?(prefix)

  key.to_s.delete_prefix(prefix)
end

.regionObject



86
87
88
# File 'lib/studio/s3.rb', line 86

def region
  Studio.s3_region
end

.reset!Object



97
98
99
# File 'lib/studio/s3.rb', line 97

def reset!
  @client = nil
end

.signed_url(key:, expires_in: 3600) ⇒ Object



23
24
25
26
# File 'lib/studio/s3.rb', line 23

def signed_url(key:, expires_in: 3600)
  require "aws-sdk-s3"
  Aws::S3::Presigner.new(client: client).presigned_url(:get_object, bucket: bucket, key: full_key(key), expires_in: expires_in)
end

.upload(key:, body:, content_type: nil, cache_control: nil) ⇒ Object



7
8
9
10
11
12
13
# File 'lib/studio/s3.rb', line 7

def upload(key:, body:, content_type: nil, cache_control: nil)
  opts = { bucket: bucket, key: full_key(key), body: body }
  opts[:content_type] = content_type if content_type
  opts[:cache_control] = cache_control if cache_control
  client.put_object(**opts)
  url(key: key)
end

.url(key:) ⇒ Object



19
20
21
# File 'lib/studio/s3.rb', line 19

def url(key:)
  "https://#{bucket}.s3.#{region}.amazonaws.com/#{full_key(key)}"
end