Class: Moneta::Adapters::Couch

Inherits:
Moneta::Adapter show all
Defined in:
lib/moneta/adapters/couch.rb

Overview

CouchDB backend

You can store hashes directly using this adapter.

Examples:

Store hashes

db = Moneta::Adapters::Couch.new
db['key'] = {a: 1, b: 2}

Defined Under Namespace

Classes: HTTPError

Instance Attribute Summary

Attributes inherited from Moneta::Adapter

#backend

Instance Method Summary collapse

Methods inherited from Moneta::Adapter

backend, backend_block, backend_required?

Methods included from Config

#config, included

Methods included from Defaults

#[], #[]=, #close, #decrement, #features, #fetch, #fetch_values, included, #increment, #supports?, #update

Methods included from OptionSupport

#expires, #prefix, #raw, #with

Constructor Details

#initialize(options = {}) ⇒ Couch

Returns a new instance of Couch.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :host (String) — default: '127.0.0.1'

    Couch host

  • :port (String) — default: 5984

    Couch port

  • :db (String) — default: 'moneta'

    Couch database

  • :scheme (String) — default: 'http'

    HTTP scheme to use

  • :value_field (String) — default: 'value'

    Document field to store value

  • :type_field (String) — default: 'type'

    Document field to store value type

  • :login (String)

    Login name to use for HTTP basic authentication

  • :password (String)

    Password to use for HTTP basic authentication

  • :adapter (Symbol)

    Adapter to use with Faraday

  • :backend (Faraday::Connecton)

    Use existing backend instance

  • Other (Object)

    options passed to Faraday::new (unless :backend option is provided).



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/moneta/adapters/couch.rb', line 57

def initialize(options = {})
  super

  if config. && config.password
    # Faraday 1.x had a `basic_auth` function
    if backend.respond_to? :basic_auth
      backend.basic_auth(config., config.password)
    else
      backend.request :authorization, :basic, config., config.password
    end
  end

  @rev_cache = Moneta.build do
    use :Lock
    adapter :LRUHash
  end
  create_db unless config.skip_create_db
end

Instance Method Details

#clear(options = {}) ⇒ void

This method returns an undefined value.

Clear all keys in this store

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :compact (Boolean) — default: false

    Whether to compact the database after clearing

  • :await_compact (Boolean) — default: false

    Whether to wait for compaction to complete before returning.

  • :full_commit (Boolean) — default: nil

    Set to ‘true` or `false` to override the server’s href="https://docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits">docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits

    commit policy
    


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/moneta/adapters/couch.rb', line 148

def clear(options = {})
  loop do
    docs = all_docs(limit: 10_000)
    break if docs['rows'].empty?
    deletions = docs['rows'].map do |row|
      { _id: row['id'], _rev: row['value']['rev'], _deleted: true }
    end
    bulk_docs(deletions, full_commit: options[:full_commit])
  end

  # Compact the database unless told not to
  if options[:compact]
    post('_compact', expect: 202)

    # Performance won't be great while compaction is happening, so by default we wait for it
    if options[:await_compact]
      loop do
        db_info = get('', expect: 200)
        break unless db_info['compact_running']

        # wait before checking again
        sleep 1
      end
    end
  end

  self
end

#create(key, value, options = {}) ⇒ Boolean

Note:

Not every Moneta store implements this method, a NotImplementedError is raised if it is not supported.

Atomically sets a key to value if it’s not set.

Parameters:

  • key (Object)
  • value (Object)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :expires (Integer)

    Update expiration time (See Expires)

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • :expires (Integer)

    Update expiration time (See Expires)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :cache_rev (Boolean) — default: true

    Whether to cache the rev of the document for faster updating

Returns:

  • (Boolean)

    key was set



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/moneta/adapters/couch.rb', line 179

def create(key, value, options = {})
  cache_rev = options[:cache_rev] != false
  doc = value_to_doc(value, nil)
  response = put(key, doc, cache_rev: cache_rev, returns: :response)
  case response.status
  when 201
    true
  when 409
    false
  else
    raise HTTPError.new(response.status, :put, @backend.create_url(key))
  end
rescue HTTPError
  tries ||= 0
  (tries += 1) < 10 ? retry : raise
end

#delete(key, options = {}) ⇒ Object

Delete the key from the store and return the current value

Parameters:

  • key (Object)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :batch (Boolean) — default: false

    Whether to do a href="https://docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes">docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes

    batch mode write
    
  • :full_commit (Boolean) — default: nil

    Set to ‘true` or `false` to override the server’s href="https://docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits">docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits

    commit policy
    

Returns:

  • (Object)

    current value



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/moneta/adapters/couch.rb', line 121

def delete(key, options = {})
  get_response = get(key, returns: :response)
  if get_response.success?
    value = body_to_value(get_response.body)
    existing_rev = parse_rev(get_response)
    query = { rev: existing_rev }
    query[:batch] = 'ok' if options[:batch]
    request(:delete, key,
            headers: full_commit_header(options[:full_commit]),
            query: query,
            expect: options[:batch] ? 202 : 200)
    delete_cached_rev(key)
    value
  end
rescue HTTPError
  tries ||= 0
  (tries += 1) < 10 ? retry : raise
end

#each_keyEnumerator #each_key {|key| ... } ⇒ self

Note:

Not every Moneta store implements this method, a NotImplementedError is raised if it is not supported.

Calls block once for each key in store, passing the key as a parameter. If no block is given, an enumerator is returned instead.

Overloads:

  • #each_keyEnumerator

    Returns An all-the-keys enumerator.

    Returns:

    • (Enumerator)

      An all-the-keys enumerator

  • #each_key {|key| ... } ⇒ self

    Yield Parameters:

    • key (Object)

      Each key is yielded to the supplied block

    Returns:

    • (self)


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/moneta/adapters/couch.rb', line 197

def each_key
  return enum_for(:each_key) unless block_given?

  skip = 0
  limit = 1000
  loop do
    docs = all_docs(limit: limit, skip: skip)
    break if docs['rows'].empty?
    skip += docs['rows'].length
    docs['rows'].each do |row|
      key = row['id']
      @rev_cache[key] = row['value']['rev']
      yield key
    end
  end
  self
end

#key?(key, options = {}) ⇒ Boolean

Exists the value with key

Parameters:

  • key (Object)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :expires (Integer)

    Update expiration time (See Expires)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :cache_rev (Boolean) — default: true

    Whether to cache the rev of the document for faster updating

Returns:

  • (Boolean)


79
80
81
82
# File 'lib/moneta/adapters/couch.rb', line 79

def key?(key, options = {})
  cache_rev = options[:cache_rev] != false
  head(key, cache_rev: cache_rev)
end

#load(key, options = {}) ⇒ Object

Fetch value with key. Return nil if the key doesn’t exist

Parameters:

  • key (Object)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :expires (Integer)

    Update expiration time (See Expires)

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • :sync (Boolean)

    Synchronized load (Cache reloads from adapter, Daybreak syncs with file)

  • Other (Object)

    options as defined by the adapters or middleware

  • :expires (Integer)

    Update expiration time (See Expires)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :cache_rev (Boolean) — default: true

    Whether to cache the rev of the document for faster updating

Returns:

  • (Object)

    value



86
87
88
89
90
# File 'lib/moneta/adapters/couch.rb', line 86

def load(key, options = {})
  cache_rev = options[:cache_rev] != false
  doc = get(key, cache_rev: cache_rev)
  doc ? doc_to_value(doc) : nil
end

#merge!(pairs, options = {}) ⇒ self #merge!(pairs, options = {}) {|key, old_value, new_value| ... } ⇒ self

Note:

Some adapters may implement this method atomically, or in two passes when a block is provided. The default implmentation uses #key?, #load and #store.

Stores the pairs in the key-value store, and returns itself. When a block is provided, it will be called before overwriting any existing values with the key, old value and supplied value, and the return value of the block will be used in place of the supplied value.

Overloads:

  • #merge!(pairs, options = {}) ⇒ self

    Parameters:

    • pairs (<(Object, Object)>)

      A collection of key-value pairs to store

    • options (Hash) (defaults to: {})

    Returns:

    • (self)
  • #merge!(pairs, options = {}) {|key, old_value, new_value| ... } ⇒ self

    Parameters:

    • pairs (<(Object, Object)>)

      A collection of key-value pairs to store

    • options (Hash) (defaults to: {})

    Yield Parameters:

    • key (Object)

      A key that whose value is being overwritten

    • old_value (Object)

      The existing value which is being overwritten

    • new_value (Object)

      The value supplied in the method call

    Yield Returns:

    • (Object)

      The value to use for overwriting

    Returns:

    • (self)

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :full_commit (Boolean) — default: nil

    Set to ‘true` or `false` to override the server’s href="https://docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits">docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits

    commit policy
    


235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/moneta/adapters/couch.rb', line 235

def merge!(pairs, options = {})
  keys = pairs.map { |key, _| key }.to_a
  cache_revs(*keys.reject { |key| @rev_cache[key] })

  if block_given?
    existing = Hash[slice(*keys, **options)]
    pairs = pairs.map do |key, new_value|
      [
        key,
        if existing.key?(key)
          yield(key, existing[key], new_value)
        else
          new_value
        end
      ]
    end
  end

  docs = pairs.map { |key, value| value_to_doc(value, @rev_cache[key], key) }.to_a
  results = bulk_docs(docs, full_commit: options[:full_commit], returns: :doc)
  retries = results.each_with_object([]) do |row, retries|
    ok, id = row.values_at('ok', 'id')
    if ok
      @rev_cache[id] = row['rev']
    elsif row['error'] == 'conflict'
      delete_cached_rev(id)
      retries << pairs.find { |key,| key == id }
    else
      raise "Unrecognised response: #{row}"
    end
  end

  # Recursive call with all conflicts
  if retries.empty?
    self
  else
    merge!(retries, options)
  end
end

#slice(*keys, **options) ⇒ <(Object, Object)>

Note:

The keys in the return value may be the same objects that were supplied (i.e. Object#equal?), or may simply be equal (i.e. Object#==).

Note:

Some adapters may implement this method atomically. The default implmentation uses #values_at.

Returns a collection of key-value pairs corresponding to those supplied keys which are present in the key-value store, and their associated values. Only those keys present in the store will have pairs in the return value. The return value can be any enumerable object that yields pairs, so it could be a hash, but needn’t be.

Parameters:

  • keys (<Object>)

    The keys for the values to fetch

  • options (Hash)

Options Hash (**options):

  • :expires (Integer)

    Update expiration time (See Expires)

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • :sync (Boolean)

    Synchronized load (Cache reloads from adapter, Daybreak syncs with file)

  • Other (Object)

    options as defined by the adapters or middleware

Returns:

  • (<(Object, Object)>)

    A collection of key-value pairs



222
223
224
225
226
227
228
# File 'lib/moneta/adapters/couch.rb', line 222

def slice(*keys, **options)
  docs = all_docs(keys: keys, include_docs: true)
  docs["rows"].map do |row|
    next unless doc = row['doc']
    [row['id'], doc_to_value(doc)]
  end.compact
end

#store(key, value, options = {}) ⇒ Object

Store value with key

Parameters:

  • key (Object)
  • value (Object)
  • options (Hash) (defaults to: {})

Options Hash (options):

  • :expires (Integer)

    Set expiration time (See Expires)

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :batch (Boolean) — default: false

    Whether to do a href="https://docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes">docs.couchdb.org/en/stable/api/database/common.html#api-doc-batch-writes

    batch mode write
    
  • :full_commit (Boolean) — default: nil

    Set to ‘true` or `false` to override the server’s href="https://docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits">docs.couchdb.org/en/stable/config/couchdb.html#couchdb/delayed_commits

    commit policy
    
  • :expires (Integer)

    Update expiration time (See Expires)

  • :prefix (String)

    Prefix key (See Transformer)

  • Other (Object)

    options as defined by the adapters or middleware

  • :cache_rev (Boolean) — default: true

    Whether to cache the rev of the document for faster updating

Returns:

  • value



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/moneta/adapters/couch.rb', line 101

def store(key, value, options = {})
  put(key, value_to_doc(value, rev(key)),
      headers: full_commit_header(options[:full_commit]),
      query: options[:batch] ? { batch: 'ok' } : {},
      cache_rev: options[:cache_rev] != false,
      expect: options[:batch] ? 202 : 201)
  value
rescue HTTPError
  tries ||= 0
  (tries += 1) < 10 ? retry : raise
end

#values_at(*keys, **options) ⇒ Array<Object, nil>

Note:

Some adapters may implement this method atomically, but the default implementation simply makes repeated calls to #load.

Returns an array containing the values associated with the given keys, in the same order as the supplied keys. If a key is not present in the key-value-store, nil is returned in its place.

Parameters:

  • keys (<Object>)

    The keys for the values to fetch

  • options (Hash)

Options Hash (**options):

  • :expires (Integer)

    Update expiration time (See Expires)

  • :raw (Boolean)

    Raw access without value transformation (See Transformer)

  • :prefix (String)

    Prefix key (See Transformer)

  • :sync (Boolean)

    Synchronized load (Cache reloads from adapter, Daybreak syncs with file)

  • Other (Object)

    options as defined by the adapters or middleware

Returns:

  • (Array<Object, nil>)

    Array containing the values requested, with nil for missing values



216
217
218
219
# File 'lib/moneta/adapters/couch.rb', line 216

def values_at(*keys, **options)
  hash = Hash[slice(*keys, **options)]
  keys.map { |key| hash[key] }
end