Class: Dynamoid::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/dynamoid/adapter.rb

Overview

Adapter’s value-add: 1) For the rest of Dynamoid, the gateway to DynamoDB. 2) Allows switching ‘config.adapter` to ease development of a new adapter. 3) Caches the list of tables Dynamoid knows about.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAdapter

Returns a new instance of Adapter.



13
14
15
16
# File 'lib/dynamoid/adapter.rb', line 13

def initialize
  @adapter_ = Concurrent::Atom.new(nil)
  @tables_ = Concurrent::Atom.new(nil)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Delegate all methods that aren’t defind here to the underlying adapter.

Since:

  • 0.2.0



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/dynamoid/adapter.rb', line 157

def method_missing(method, *args, &block)
  # Don't use keywork arguments delegating (with **kw). It works in
  # different way in different Ruby versions: <= 2.6, 2.7, 3.0 and in some
  # future 3.x versions. Providing that there are no downstream methods
  # with keyword arguments in adapter.
  #
  # https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html

  return benchmark(method, *args) { adapter.send(method, *args, &block) } if adapter.respond_to?(method)

  super
end

Class Method Details

.adapter_plugin_classObject



195
196
197
# File 'lib/dynamoid/adapter.rb', line 195

def self.adapter_plugin_class
  Dynamoid::AdapterPlugin.const_get(Dynamoid::Config.adapter.camelcase)
end

Instance Method Details

#adapterObject

The actual adapter currently in use.

Since:

  • 0.2.0



28
29
30
31
32
33
34
35
36
# File 'lib/dynamoid/adapter.rb', line 28

def adapter
  unless @adapter_.value
    adapter = self.class.adapter_plugin_class.new
    adapter.connect!
    @adapter_.compare_and_set(nil, adapter)
    clear_cache!
  end
  @adapter_.value
end

#benchmark(method, *args) { ... } ⇒ Object

Shows how long it takes a method to run on the adapter. Useful for generating logged output.

Parameters:

  • method (Symbol|String)

    the name of the method to appear in the log

  • args (Array)

    the arguments to the method to appear in the log

Yields:

  • the actual code to benchmark

Returns:

  • the result of the yield

Since:

  • 0.2.0



51
52
53
54
55
56
# File 'lib/dynamoid/adapter.rb', line 51

def benchmark(method, *args)
  start = Time.now
  result = yield
  Dynamoid.logger.debug "(#{((Time.now - start) * 1000.0).round(2)} ms) #{method.to_s.split('_').collect(&:upcase).join(' ')}#{" - #{args.inspect}" unless args.nil? || args.empty?}"
  result
end

#clear_cache!Object



38
39
40
# File 'lib/dynamoid/adapter.rb', line 38

def clear_cache!
  @tables_.swap { |_value, _args| nil }
end

#create_table(table_name, key, options = {}) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'lib/dynamoid/adapter.rb', line 125

def create_table(table_name, key, options = {})
  unless tables.include?(table_name)
    result = nil
    benchmark('Create Table') { result = adapter.create_table(table_name, key, options) }
    tables << table_name
    result
  else
    false
  end
end

#delete(table, ids, options = {}) ⇒ Object

Delete an item from a table.

Parameters:

  • table (String)

    the name of the table to write the object to

  • ids (String, Array)

    to delete; can also be a string of just one id

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

    allowed only range_key - range key or array of range keys of the record to delete, can also be a string of just one range_key, and conditions



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/dynamoid/adapter.rb', line 99

def delete(table, ids, options = {})
  range_key = options[:range_key] # array of range keys that matches the ids passed in
  if ids.respond_to?(:each)
    ids = if range_key.respond_to?(:each)
            # turn ids into array of arrays each element being hash_key, range_key
            ids.each_with_index.map { |id, i| [id, range_key[i]] }
          else
            range_key ? ids.map { |id| [id, range_key] } : ids
          end

    batch_delete_item(table => ids)
  else
    delete_item(table, ids, options)
  end
end

#delete_table(table_name, options = {}) ⇒ Object

Since:

  • 0.2.0



137
138
139
140
141
142
143
# File 'lib/dynamoid/adapter.rb', line 137

def delete_table(table_name, options = {})
  if tables.include?(table_name)
    benchmark('Delete Table') { adapter.delete_table(table_name, options) }
    idx = tables.index(table_name)
    tables.delete_at(idx)
  end
end

#query(table_name, key_conditions, non_key_conditions = {}, options = {}) ⇒ Enumerable

Query the DynamoDB table. This employs DynamoDB’s indexes so is generally faster than scanning, but is only really useful for range queries, since it can only find by one hash key at once. Only provide one range key to the hash.

Dynamoid.adapter.query('users', { id: [[:eq, '1']], age: [[:between, [10, 30]]] }, { batch_size: 1000 })

Parameters:

  • table_name (String)

    the name of the table

  • key_conditions (Array[Array])

    conditions for the primary key attributes

  • non_key_conditions (Array[Array]) (defaults to: {})

    (optional) conditions for non-primary key attributes

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

    (optional) the options to query the table with

Options Hash (options):

  • :consistent_read (Boolean)

    You can set the ConsistentRead parameter to true and obtain a strongly consistent result

  • :scan_index_forward (Boolean)

    Specifies the order for index traversal: If true (default), the traversal is performed in ascending order; if false, the traversal is performed in descending order.

  • :select (Symbop)

    The attributes to be returned in the result (one of ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, …)

  • :index_name (Symbol)

    The name of an index to query. This index can be any local secondary index or global secondary index on the table.

  • :exclusive_start_key (Hash)

    The primary key of the first item that this operation will evaluate.

  • :batch_size (Integer)

    The number of items to lazily load one by one

  • :record_limit (Integer)

    The maximum number of items to return (not necessarily the number of evaluated items)

  • :scan_limit (Integer)

    The maximum number of items to evaluate (not necessarily the number of matching items)

  • :project (Array[Symbol])

    The attributes to retrieve from the table

Returns:

  • (Enumerable)

    matching items



191
192
193
# File 'lib/dynamoid/adapter.rb', line 191

def query(table_name, key_conditions, non_key_conditions = {}, options = {})
  adapter.query(table_name, key_conditions, non_key_conditions, options)
end

#read(table, ids, options = {}, &blk) ⇒ Object

Read one or many keys from the selected table. This method intelligently calls batch_get or get on the underlying adapter depending on whether ids is a range or a single key. If a range key is present, it will also interpolate that into the ids so that the batch get will acquire the correct record.

Parameters:

  • table (String)

    the name of the table to write the object to

  • ids (String, Array)

    to fetch; can also be a string of just one id

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

    Passed to the underlying query. The :range_key option is required whenever the table has a range key, unless multiple ids are passed in.

Since:

  • 0.2.0



83
84
85
86
87
88
89
# File 'lib/dynamoid/adapter.rb', line 83

def read(table, ids, options = {}, &blk)
  if ids.respond_to?(:each)
    batch_get_item({ table => ids }, options, &blk)
  else
    get_item(table, ids, options)
  end
end

#scan(table, query = {}, opts = {}) ⇒ Object

Scans a table. Generally quite slow; try to avoid using scan if at all possible.

Parameters:

  • table (String)

    the name of the table to write the object to

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

    a hash of attributes: matching records will be returned by the scan

Since:

  • 0.2.0



121
122
123
# File 'lib/dynamoid/adapter.rb', line 121

def scan(table, query = {}, opts = {})
  benchmark('Scan', table, query) { adapter.scan(table, query, opts) }
end

#tablesObject



18
19
20
21
22
23
# File 'lib/dynamoid/adapter.rb', line 18

def tables
  unless @tables_.value
    @tables_.swap { |_value, _args| benchmark('Cache Tables') { list_tables || [] } }
  end
  @tables_.value
end

#write(table, object, options = nil) ⇒ Object

Write an object to the adapter.

Parameters:

  • table (String)

    the name of the table to write the object to

  • object (Object)

    the object itself

  • options (Hash) (defaults to: nil)

    Options that are passed to the put_item call

Returns:

  • (Object)

    the persisted object

Since:

  • 0.2.0



67
68
69
# File 'lib/dynamoid/adapter.rb', line 67

def write(table, object, options = nil)
  put_item(table, object, options)
end