class Dragonfly::S3DataStore

Constants

SUBDOMAIN_PATTERN
VERSION

Attributes

access_key_id[RW]
bucket_name[RW]
fog_storage_options[RW]
region[RW]
root_path[RW]
secret_access_key[RW]
storage_headers[RW]
url_host[RW]
url_scheme[RW]
use_iam_profile[RW]

Public Class Methods

new(opts={}) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 16
def initialize(opts={})
  @bucket_name = opts[:bucket_name]
  @access_key_id = opts[:access_key_id]
  @secret_access_key = opts[:secret_access_key]
  @region = opts[:region]
  @storage_headers = opts[:storage_headers] || {'x-amz-acl' => 'public-read'}
  @url_scheme = opts[:url_scheme] || 'http'
  @url_host = opts[:url_host]
  @use_iam_profile = opts[:use_iam_profile]
  @root_path = opts[:root_path]
  @fog_storage_options = opts[:fog_storage_options] || {}
end

Public Instance Methods

bucket_exists?() click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 97
def bucket_exists?
  rescuing_socket_errors{ storage.get_bucket_location(bucket_name) }
  true
rescue Excon::Errors::NotFound => e
  false
end
destroy(uid) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 56
def destroy(uid)
  rescuing_socket_errors{ storage.delete_object(bucket_name, full_path(uid)) }
rescue Excon::Errors::NotFound, Excon::Errors::Conflict => e
  Dragonfly.warn("#{self.class.name} destroy error: #{e}")
end
domain() click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 74
def domain
  case region
  when 'us-east-1', nil
    's3.amazonaws.com'
  else
    "s3-#{region}.amazonaws.com"
  end
end
read(uid) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 48
def read(uid)
  ensure_configured
  response = rescuing_socket_errors{ storage.get_object(bucket_name, full_path(uid)) }
  [response.body, headers_to_meta(response.headers)]
rescue Excon::Errors::NotFound => e
  nil
end
storage() click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 83
def storage
  @storage ||= begin
    storage = Fog::Storage.new(fog_storage_options.merge({
      :provider => 'AWS',
      :aws_access_key_id => access_key_id,
      :aws_secret_access_key => secret_access_key,
      :region => region,
      :use_iam_profile => use_iam_profile
    }).reject {|name, option| option.nil?})
    storage.sync_clock
    storage
  end
end
url_for(uid, opts={}) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 62
def url_for(uid, opts={})
  if expires = opts[:expires]
    storage.get_object_https_url(bucket_name, full_path(uid), expires, {:query => opts[:query]})
  else
    scheme = opts[:scheme] || url_scheme
    host   = opts[:host]   || url_host || (
      bucket_name =~ SUBDOMAIN_PATTERN ? "#{bucket_name}.s3.amazonaws.com" : "s3.amazonaws.com/#{bucket_name}"
    )
    "#{scheme}://#{host}/#{full_path(uid)}"
  end
end
write(content, opts={}) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 31
def write(content, opts={})
  ensure_configured
  ensure_bucket_initialized

  headers = {'Content-Type' => content.mime_type}
  headers.merge!(opts[:headers]) if opts[:headers]
  uid = opts[:path] || generate_uid(content.name || 'file')

  rescuing_socket_errors do
    content.file do |f|
      storage.put_object(bucket_name, full_path(uid), f, full_storage_headers(headers, content.meta))
    end
  end

  uid
end

Private Instance Methods

ensure_bucket_initialized() click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 119
def ensure_bucket_initialized
  unless @bucket_initialized
    rescuing_socket_errors{ storage.put_bucket(bucket_name, 'LocationConstraint' => region) } unless bucket_exists?
    @bucket_initialized = true
  end
end
ensure_configured() click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 106
def ensure_configured
  unless @configured
    if use_iam_profile
      raise NotConfigured, "You need to configure #{self.class.name} with bucket_name" if bucket_name.nil?
    else
      [:bucket_name, :access_key_id, :secret_access_key].each do |attr|
        raise NotConfigured, "You need to configure #{self.class.name} with #{attr}" if send(attr).nil?
      end
    end
    @configured = true
  end
end
escape_meta_values(meta) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 159
def escape_meta_values(meta)
  meta.inject({}) {|hash, (key, value)|
    hash[key] = value.is_a?(String) ? CGI.escape(value) : value
    hash
  }
end
full_path(uid) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 130
def full_path(uid)
  File.join *[root_path, uid].compact
end
full_storage_headers(headers, meta) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 134
def full_storage_headers(headers, meta)
  storage_headers.merge(meta_to_headers(meta)).merge(headers)
end
generate_uid(name) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 126
def generate_uid(name)
  "#{Time.now.strftime '%Y/%m/%d/%H/%M/%S'}/#{SecureRandom.uuid}/#{name}"
end
headers_to_meta(headers) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 138
def headers_to_meta(headers)
  json = headers['x-amz-meta-json']
  if json && !json.empty?
    unescape_meta_values(Serializer.json_decode(json))
  elsif marshal_data = headers['x-amz-meta-extra']
    Utils.stringify_keys(Serializer.marshal_b64_decode(marshal_data))
  end
end
meta_to_headers(meta) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 147
def meta_to_headers(meta)
  meta = escape_meta_values(meta)
  {'x-amz-meta-json' => Serializer.json_encode(meta)}
end
rescuing_socket_errors() { || ... } click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 152
def rescuing_socket_errors(&block)
  yield
rescue Excon::Errors::SocketError => e
  storage.reload
  yield
end
unescape_meta_values(meta) click to toggle source
# File lib/dragonfly/s3_data_store.rb, line 166
def unescape_meta_values(meta)
  meta.inject({}) {|hash, (key, value)|
    hash[key] = value.is_a?(String) ? CGI.unescape(value) : value
    hash
  }
end