class Star::File

Attributes

content_type[R]

Public Class Methods

new(options = {}) click to toggle source
# File lib/star/file.rb, line 11
def initialize(options = {})
  @name = options.fetch :name, 'attachment'
  @content_type = options.fetch :content_type, 'application/octet-stream'
  @folder = options.fetch :folder, 'attachments'
end

Public Instance Methods

copy_from(source) click to toggle source
# File lib/star/file.rb, line 45
def copy_from(source)
  Star.remote? ? copy_from_remote(source) : copy_from_local(source)
end
delete() click to toggle source
# File lib/star/file.rb, line 41
def delete
  Star.remote? ? delete_remote : delete_local
end
open() { |tmp_file| ... } click to toggle source
# File lib/star/file.rb, line 17
def open
  Tempfile.open 'tmp_file' do |tmp_file|
    yield tmp_file
    tmp_file.flush
    store tmp_file
  end
end
path() click to toggle source
# File lib/star/file.rb, line 29
def path
  [Star.configuration.location, @folder, @name].compact.join('/')
end
remote_path() click to toggle source
# File lib/star/file.rb, line 49
def remote_path
  URI.escape path
end
store(tmp_file) click to toggle source
# File lib/star/file.rb, line 33
def store(tmp_file)
  if Star.remote?
    retry_on_error {store_remote tmp_file}
  else
    store_local(tmp_file)
  end
end
url() click to toggle source
# File lib/star/file.rb, line 25
def url
  "https://#{host}/#{bucket}#{remote_path}?#{url_params}"
end

Private Instance Methods

bucket() click to toggle source
# File lib/star/file.rb, line 175
def bucket
  Star.configuration.bucket
end
copy_file(signature, timestamp, source) click to toggle source
# File lib/star/file.rb, line 111
def copy_file(signature, timestamp, source)
  Net::HTTP::Put.new("/#{bucket}#{remote_path}").tap do |request|
    request.add_field 'Date', timestamp
    request.add_field 'Content-Type', @content_type
    request.add_field 'x-amz-copy-source', "/#{bucket}#{source.remote_path}"
    request.add_field 'Authorization', "AWS #{key}:#{signature}"
  end
end
copy_from_local(source) click to toggle source
# File lib/star/file.rb, line 124
def copy_from_local(source)
  FileUtils.mkdir_p ::File.dirname(path)
  FileUtils.cp source.path, path
end
copy_from_remote(source) click to toggle source
# File lib/star/file.rb, line 99
def copy_from_remote(source)
  timestamp = Time.now.utc.strftime "%a, %d %b %Y %H:%M:%S UTC"
  extra = "x-amz-copy-source:/#{bucket}#{source.remote_path}"
  signature = sign "PUT\n\n#{@content_type}\n#{timestamp}\n#{extra}"
  request = copy_file signature, timestamp, source
  response = Net::HTTP.start(host, 443, use_ssl: true) do |http|
    http.request request
  end
  response.error! unless response.is_a? Net::HTTPSuccess
  sleep 3 # See https://forums.aws.amazon.com/message.jspa?messageID=370480
end
delete_file(signature, timestamp) click to toggle source
# File lib/star/file.rb, line 92
def delete_file(signature, timestamp)
  Net::HTTP::Delete.new("/#{bucket}#{remote_path}").tap do |request|
    request.add_field 'Authorization', "AWS #{key}:#{signature}"
    request.add_field 'Date', timestamp
  end
end
delete_local() click to toggle source
# File lib/star/file.rb, line 120
def delete_local
  FileUtils.rm_f path
end
delete_remote() click to toggle source
# File lib/star/file.rb, line 82
def delete_remote
  timestamp = Time.now.utc.strftime "%a, %d %b %Y %H:%M:%S UTC"
  signature = sign "DELETE\n\n\n#{timestamp}"
  request = delete_file signature, timestamp
  response = Net::HTTP.start(host, 443, use_ssl: true) do |http|
    http.request request
  end
  response.error! unless response.is_a? Net::HTTPNoContent
end
escape(string) click to toggle source
# File lib/star/file.rb, line 157
def escape(string)
  s = {'+' => "%2B", '=' => "%3D", '?' => '%3F', '@' => '%40', '$' => '%24',
       '&' => '%26', ',' => '%2C', '/' => '%2F', ':' => '%3A', ';' => '%3B'}
  URI.escape(string.strip).gsub(/./) {|c| s.fetch(c, c)}
end
host() click to toggle source
# File lib/star/file.rb, line 163
def host
  "s3.amazonaws.com"
end
key() click to toggle source
# File lib/star/file.rb, line 167
def key
  Star.configuration.access_key_id
end
put_file(body, signature, timestamp) click to toggle source
# File lib/star/file.rb, line 140
def put_file(body, signature, timestamp)
  Net::HTTP::Put.new("/#{bucket}#{remote_path}").tap do |request|
    request.body_stream = body
    request.initialize_http_header 'Content-Length' => body.size.to_s
    request.add_field 'Host', host
    request.add_field 'Date', timestamp
    request.add_field 'Content-Type', @content_type
    request.add_field 'Authorization', "AWS #{key}:#{signature}"
  end
end
retry_on_error() { || ... } click to toggle source
# File lib/star/file.rb, line 55
def retry_on_error
  yield
rescue Net::HTTPFatalError => e
  raise if @retried
  sleep 5
  @retried = true
  retry
end
secret() click to toggle source
# File lib/star/file.rb, line 171
def secret
  Star.configuration.secret_access_key
end
sign(action) click to toggle source
# File lib/star/file.rb, line 151
def sign(action)
  digest = OpenSSL::Digest.new 'sha1'
  string = "#{action}\n/#{bucket}#{remote_path}"
  Base64.strict_encode64 OpenSSL::HMAC.digest(digest, secret, string)
end
store_local(tmp_file) click to toggle source
# File lib/star/file.rb, line 77
def store_local(tmp_file)
  FileUtils.mkdir_p ::File.dirname(path)
  FileUtils.mv tmp_file.path, path
end
store_remote(tmp_file) click to toggle source
# File lib/star/file.rb, line 64
def store_remote(tmp_file)
  timestamp = Time.now.utc.strftime "%a, %d %b %Y %H:%M:%S UTC"
  signature = sign "PUT\n\n#{@content_type}\n#{timestamp}"
  ::File.open(tmp_file) do |body|
    request = put_file body, signature, timestamp
    response = Net::HTTP.start(host, 443, use_ssl: true) do |http|
      http.request request
    end
    response.error! unless response.is_a? Net::HTTPSuccess
  end
  sleep 3 # See https://forums.aws.amazon.com/message.jspa?messageID=370480
end
url_params() click to toggle source
# File lib/star/file.rb, line 129
def url_params
  expires_at = Time.now.to_i + Star.configuration.duration
  digest = OpenSSL::Digest.new 'sha1'
  params = "response-content-disposition=attachment"
  string = "GET\n\n\n#{expires_at}\n/#{bucket}#{remote_path}?#{params}"
  hmac = OpenSSL::HMAC.digest digest, secret, string
  code = escape Base64.encode64(hmac)

  "#{params}&AWSAccessKeyId=#{key}&Expires=#{expires_at}&Signature=#{code}"
end