File: //lib/ruby/vendor_ruby/moneta/adapters/couch.rb
require 'faraday'
require 'multi_json'
module Moneta
module Adapters
# CouchDB backend
#
# You can store hashes directly using this adapter.
#
# @example Store hashes
# db = Moneta::Adapters::Mongo.new
# db['key'] = {a: 1, b: 2}
#
# @api public
class Couch
include Defaults
attr_reader :backend
supports :create
# @param [Hash] options
# @option options [String] :host ('127.0.0.1') Couch host
# @option options [String] :port (5984) Couch port
# @option options [String] :db ('moneta') Couch database
# @option options [String] :value_field ('value') Document field to store value
# @option options [String] :type_field ('type') Document field to store value type
# @option options [Faraday connection] :backend Use existing backend instance
def initialize(options = {})
@value_field = options[:value_field] || 'value'
@type_field = options[:type_field] || 'type'
url = "http://#{options[:host] || '127.0.0.1'}:#{options[:port] || 5984}/#{options[:db] || 'moneta'}"
@backend = options[:backend] || ::Faraday.new(url: url)
create_db
end
# (see Proxy#key?)
def key?(key, options = {})
@backend.head(key).status == 200
end
# (see Proxy#load)
def load(key, options = {})
response = @backend.get(key)
response.status == 200 ? body_to_value(response.body) : nil
end
# (see Proxy#store)
def store(key, value, options = {})
response = @backend.head(key)
body = value_to_body(value, response.status == 200 && response['etag'][1..-2])
response = @backend.put(key, body, 'Content-Type' => 'application/json')
raise "HTTP error #{response.status}" unless response.status == 201
value
rescue
tries ||= 0
(tries += 1) < 10 ? retry : raise
end
# (see Proxy#delete)
def delete(key, options = {})
response = @backend.get(key)
if response.status == 200
value = body_to_value(response.body)
response = @backend.delete("#{key}?rev=#{response['etag'][1..-2]}")
raise "HTTP error #{response.status}" unless response.status == 200
value
end
rescue
tries ||= 0
(tries += 1) < 10 ? retry : raise
end
# (see Proxy#clear)
def clear(options = {})
@backend.delete ''
create_db
self
end
# (see Proxy#create)
def create(key, value, options = {})
body = value_to_body(value, nil)
response = @backend.put(key, body, 'Content-Type' => 'application/json')
case response.status
when 201
true
when 409
false
else
raise "HTTP error #{response.status}"
end
rescue
tries ||= 0
(tries += 1) < 10 ? retry : raise
end
private
def body_to_value(body)
doc = MultiJson.load(body)
case doc[@type_field]
when 'Hash'
doc = doc.dup
doc.delete('_id')
doc.delete('_rev')
doc.delete(@type_field)
doc
else
doc[@value_field]
end
end
def value_to_body(value, rev)
case value
when Hash
doc = value.merge(@type_field => 'Hash')
when String
doc = { @value_field => value, @type_field => 'String' }
when Float, Fixnum
doc = { @value_field => value, @type_field => 'Number' }
else
raise ArgumentError, "Invalid value type: #{value.class}"
end
doc['_rev'] = rev if rev
MultiJson.dump(doc)
end
def create_db
response = @backend.put '', ''
raise "HTTP error #{response.status}" unless response.status == 201 || response.status == 412
end
end
end
end