rom-dynamo 0.1.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/.github/workflows/specs.yml +31 -0
- data/.rspec +1 -0
- data/Gemfile +4 -2
- data/README.md +1 -0
- data/Rakefile +5 -0
- data/lib/rom/dynamo/commands.rb +29 -7
- data/lib/rom/dynamo/dataset.rb +196 -0
- data/lib/rom/dynamo/gateway.rb +46 -0
- data/lib/rom/dynamo/relation.rb +12 -131
- data/lib/rom/dynamo/version.rb +1 -1
- data/lib/rom/dynamo.rb +11 -3
- data/lib/rom-dynamo.rb +1 -0
- data/rom-dynamo.gemspec +9 -4
- metadata +69 -27
- data/lib/rom/dynamo/repository.rb +0 -34
checksums.yaml
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
data.tar.gz: !binary |-
|
|
6
|
-
ZTBlOWU4ZGY1NGZiYjM1MmI3MDg1NjMxYmE0NWE5NjViODUxY2JlNw==
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7bbcf8f8498ca2b84b9542512884cc138dc251fc334dfac08412eeff71f9d1fa
|
|
4
|
+
data.tar.gz: eb0a7649d78caea174c0e6aea7ae5545bffd21fa1c6ae6e71ca0a79140dfc2fb
|
|
7
5
|
SHA512:
|
|
8
|
-
metadata.gz:
|
|
9
|
-
|
|
10
|
-
ODg1ZDlkYWEwNGEzODg2Mzk2YjFjMjI0MjUyYTAyODRiNTg3YjQyYjIxNWQ0
|
|
11
|
-
ZGQ1ZjNmNTVhZDkxOWE4ZTk1NGNkMjFmNTE1MmFkYmUzMDliYTM=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
ZGY0ZGRmOThiZDBhYzRiMzA2NjdjMDhhZWM4MzRiMGNhYWEwMzhmYzRiMjhk
|
|
14
|
-
NDdlZWNhMzE2NjMyOTFkMTY4NWUyMDk4ZmU3YmUxM2I5MWI3NDdjNGU4Mzdk
|
|
15
|
-
MTIxNjllNjc1ZTU3NjEyODBhZDc5YjljOGFkNzI1NjNlYTk5YzU=
|
|
6
|
+
metadata.gz: 6ca7ae3a668dd4938a1261916e6404f8a618bf8c0e35cab0e856911111bcef397cedb303c371b3aa13cf83d2f876fccbbf4c3a7e8f0d8710767bcb585b1be2a5
|
|
7
|
+
data.tar.gz: 4361cf505fe065176038d2639c63328efef862e52df9631cf6fcf32910261e33ad4380c3aeb995a2bd6af849de8fe71645f7626e1ff9d49786f53e085f89fb1c
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: specs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: true
|
|
14
|
+
matrix:
|
|
15
|
+
ruby: [2.4, 2.5, 2.6, 2.7, 3.0, 4.0, head, jruby]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
- name: Set up Ruby & run Bundler
|
|
19
|
+
uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
|
22
|
+
bundler-cache: true
|
|
23
|
+
- name: Setup DynamoDB Local
|
|
24
|
+
uses: rrainn/dynamodb-action@v2.0.0
|
|
25
|
+
with:
|
|
26
|
+
port: 8000
|
|
27
|
+
- name: Run specs
|
|
28
|
+
run: bundle exec rake spec
|
|
29
|
+
env:
|
|
30
|
+
AWS_ACCESS_KEY_ID: DEADBEEF
|
|
31
|
+
AWS_SECRET_ACCESS_KEY: FORAWSSDK
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Rom::Dynamo
|
|
5
5
|
|
|
6
6
|
[][gem]
|
|
7
|
+
[](https://github.com/rykov/rom-dynamo/actions/workflows/specs.yml)
|
|
7
8
|
|
|
8
9
|
AWS DynamoDB support for [Ruby Object Mapper](https://github.com/rom-rb/rom).
|
|
9
10
|
|
data/Rakefile
CHANGED
data/lib/rom/dynamo/commands.rb
CHANGED
|
@@ -5,21 +5,43 @@ module Rom
|
|
|
5
5
|
module Commands
|
|
6
6
|
# DynamoDB create command
|
|
7
7
|
class Create < ROM::Commands::Create
|
|
8
|
-
def execute(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
def execute(tuples)
|
|
9
|
+
Array([tuples]).flatten.map do |tuple|
|
|
10
|
+
attributes = input[tuple]
|
|
11
|
+
dataset.insert(attributes.to_h)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def dataset
|
|
16
|
+
relation.dataset
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# DynamoDB update command
|
|
21
|
+
class Update < ROM::Commands::Update
|
|
22
|
+
def execute(params)
|
|
23
|
+
attributes = input[params]
|
|
24
|
+
relation.map do |tuple|
|
|
25
|
+
dataset.update(tuple, attributes.to_h)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def dataset
|
|
30
|
+
relation.dataset
|
|
13
31
|
end
|
|
14
32
|
end
|
|
15
33
|
|
|
16
34
|
# DynamoDB delete command
|
|
17
35
|
class Delete < ROM::Commands::Delete
|
|
18
36
|
def execute
|
|
19
|
-
|
|
20
|
-
tuples.each { |t|
|
|
37
|
+
relation.to_a.tap do |tuples|
|
|
38
|
+
tuples.each { |t| dataset.delete(t) }
|
|
21
39
|
end
|
|
22
40
|
end
|
|
41
|
+
|
|
42
|
+
def dataset
|
|
43
|
+
relation.dataset
|
|
44
|
+
end
|
|
23
45
|
end
|
|
24
46
|
end
|
|
25
47
|
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
module Rom
|
|
2
|
+
module Dynamo
|
|
3
|
+
class Dataset
|
|
4
|
+
include Enumerable
|
|
5
|
+
include Dry::Equalizer(:name, :connection)
|
|
6
|
+
extend Dry::Initializer[undefined: false]
|
|
7
|
+
EmptyQuery = { key_conditions: {}.freeze }.freeze
|
|
8
|
+
|
|
9
|
+
option :connection
|
|
10
|
+
option :name, proc(&:to_s)
|
|
11
|
+
option :logger, optional: true
|
|
12
|
+
option :table_keys, optional: true, reader: false
|
|
13
|
+
option :query, default: proc { EmptyQuery }, reader: false
|
|
14
|
+
alias_method :ddb, :connection
|
|
15
|
+
|
|
16
|
+
######### ENUMERATE ###########
|
|
17
|
+
|
|
18
|
+
def each(&block)
|
|
19
|
+
return enum_for(:each) if block.nil?
|
|
20
|
+
each_page { |p| p.items.each(&block) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def each_page(&block)
|
|
24
|
+
return enum_for(:each_page) if block.nil?
|
|
25
|
+
result = start_query(consistent_read: true)
|
|
26
|
+
result.each_page(&block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
############# QUERY #############
|
|
30
|
+
|
|
31
|
+
def restrict(query = nil)
|
|
32
|
+
return self if query.nil?
|
|
33
|
+
dup_with_query(self.class, query)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def batch_restrict(keys)
|
|
37
|
+
dup_as(BatchGetDataset, keys: keys.map do |k|
|
|
38
|
+
Hash[table_keys.zip(k.is_a?(Array) ? k : [k])]
|
|
39
|
+
end)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def index_restrict(index, query)
|
|
43
|
+
dup_with_query(GlobalIndexDataset, query, index_name: index.to_s)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
############# PAGINATE #############
|
|
47
|
+
|
|
48
|
+
def limit(limit)
|
|
49
|
+
opts = limit.nil? ? {} : { limit: limit.to_i }
|
|
50
|
+
dup_with_query(self.class, nil, opts)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def offset(key)
|
|
54
|
+
opts = key.nil? ? {} : { exclusive_start_key: key }
|
|
55
|
+
dup_with_query(self.class, nil, opts)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def reversed
|
|
59
|
+
dup_with_query(self.class, nil, scan_index_forward: false)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
############# WRITE #############
|
|
63
|
+
def insert(hash)
|
|
64
|
+
opts = { table_name: name, item: stringify_keys(hash) }
|
|
65
|
+
connection.put_item(opts).attributes
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def delete(hash)
|
|
69
|
+
hash = stringify_keys(hash)
|
|
70
|
+
connection.delete_item({
|
|
71
|
+
table_name: name,
|
|
72
|
+
key: hash_to_key(hash),
|
|
73
|
+
expected: to_expected(hash),
|
|
74
|
+
}).attributes
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def update(keys, hash)
|
|
78
|
+
connection.update_item({
|
|
79
|
+
table_name: name, key: hash_to_key(stringify_keys(keys)),
|
|
80
|
+
attribute_updates: hash.each_with_object({}) do |(k, v), out|
|
|
81
|
+
out[k] = { value: dump_value(v), action: 'PUT' } if !keys[k]
|
|
82
|
+
end
|
|
83
|
+
}).attributes
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
############# HELPERS #############
|
|
87
|
+
private
|
|
88
|
+
def batch_get_each_page(keys, &block)
|
|
89
|
+
!keys.empty? && ddb.batch_get_item({
|
|
90
|
+
request_items: { name => { keys: keys } },
|
|
91
|
+
}).each_page do |page|
|
|
92
|
+
block.call(page[:responses][name])
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def dup_with_query(klass, key_hash, opts = {})
|
|
97
|
+
opts = @query.merge(opts)
|
|
98
|
+
|
|
99
|
+
if key_hash && !key_hash.empty?
|
|
100
|
+
conditions = @query[:key_conditions]
|
|
101
|
+
opts[:key_conditions] = conditions.merge(Hash[
|
|
102
|
+
key_hash.map do |key, value|
|
|
103
|
+
[key, {
|
|
104
|
+
attribute_value_list: [value],
|
|
105
|
+
comparison_operator: "EQ"
|
|
106
|
+
}]
|
|
107
|
+
end
|
|
108
|
+
]).freeze
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
dup_as(klass, query: opts.freeze)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def to_expected(hash)
|
|
115
|
+
hash && Hash[hash.map do |k, v|
|
|
116
|
+
[k, { value: v }]
|
|
117
|
+
end]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def hash_to_key(hash)
|
|
121
|
+
table_keys.each_with_object({}) do |k, out|
|
|
122
|
+
out[k] = hash[k] if hash.has_key?(k)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def table_keys
|
|
127
|
+
@table_keys ||= begin
|
|
128
|
+
r = ddb.describe_table(table_name: name)
|
|
129
|
+
r[:table][:key_schema].map(&:attribute_name)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def start_query(opts = {}, &block)
|
|
134
|
+
opts = @query.merge(table_name: name).merge!(opts)
|
|
135
|
+
logger&.debug("Querying DDB: #{opts.inspect}")
|
|
136
|
+
ddb.query(opts)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def dup_as(klass, opts = {})
|
|
140
|
+
table_keys # To populate keys once at top-level Dataset
|
|
141
|
+
attrs = Dataset.dry_initializer.attributes(self)
|
|
142
|
+
klass.new(**attrs.merge(opts))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# String modifiers
|
|
146
|
+
def stringify_keys(hash)
|
|
147
|
+
hash.each_with_object({}) { |(k, v), out| out[k.to_s] = v }
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def dump_value(v)
|
|
151
|
+
return v.new_offset(0).iso8601(6) if v.is_a?(DateTime)
|
|
152
|
+
v.is_a?(Time) ? v.utc.iso8601(6) : v
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Batch get using an array of key queries
|
|
157
|
+
# [{ key => val }, { key => val }, ...]
|
|
158
|
+
class BatchGetDataset < Dataset
|
|
159
|
+
option :keys
|
|
160
|
+
|
|
161
|
+
# Query for records
|
|
162
|
+
def each_page(&block)
|
|
163
|
+
return enum_for(:each_page) if block.nil?
|
|
164
|
+
batch_get_each_page(@keys) do |items|
|
|
165
|
+
klass = Aws::DynamoDB::Types::QueryOutput
|
|
166
|
+
block.call(klass.new(items: items, count: items.size))
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Dataset queried via a Global Secondary Index
|
|
172
|
+
# Paginate through keys from Global Index and
|
|
173
|
+
# call BatchGetItem for keys from each page
|
|
174
|
+
class GlobalIndexDataset < Dataset
|
|
175
|
+
def each_page(&block)
|
|
176
|
+
return enum_for(:each_page) if block.nil?
|
|
177
|
+
if @query[:limit]
|
|
178
|
+
block.call(populated_results(start_query))
|
|
179
|
+
else
|
|
180
|
+
start_query(limit: 100).each_page do |p|
|
|
181
|
+
block.call(populated_results(p))
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private def populated_results(result, &block)
|
|
187
|
+
klass = Aws::DynamoDB::Types::QueryOutput
|
|
188
|
+
keys = result.items.map { |h| hash_to_key(h) }
|
|
189
|
+
klass.new(result.to_hash.merge(items: [].tap do |out|
|
|
190
|
+
batch_get_each_page(keys) { |i| out.concat(i) }
|
|
191
|
+
end))
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'addressable/uri'
|
|
2
|
+
require 'rom/gateway'
|
|
3
|
+
|
|
4
|
+
module Rom
|
|
5
|
+
module Dynamo
|
|
6
|
+
class Gateway < ROM::Gateway
|
|
7
|
+
attr_reader :ddb, :options, :logger
|
|
8
|
+
|
|
9
|
+
def initialize(uri)
|
|
10
|
+
uri = Addressable::URI.parse(uri)
|
|
11
|
+
opts = { region: uri.host }
|
|
12
|
+
opts.merge!(uri.query_values) if uri.query
|
|
13
|
+
opts.keys.each { |k| opts[k.to_sym] = opts.delete(k) }
|
|
14
|
+
|
|
15
|
+
@options = opts
|
|
16
|
+
@ddb = Aws::DynamoDB::Client.new(@options)
|
|
17
|
+
@prefix = uri.path.gsub('/', '')
|
|
18
|
+
@datasets = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def use_logger(logger)
|
|
22
|
+
@logger = logger
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def dataset(name)
|
|
26
|
+
name = "#{@prefix}#{name}"
|
|
27
|
+
@datasets[name] ||= _has?(name) && Dataset.new(connection: @ddb, name: name, logger: @logger)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def dataset?(name)
|
|
31
|
+
!!self[name]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def [](name)
|
|
35
|
+
@datasets["#{@prefix}#{name}"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def _has?(name)
|
|
40
|
+
@ddb.describe_table(table_name: name)
|
|
41
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/rom/dynamo/relation.rb
CHANGED
|
@@ -2,139 +2,20 @@ module Rom
|
|
|
2
2
|
module Dynamo
|
|
3
3
|
class Relation < ROM::Relation
|
|
4
4
|
include Enumerable
|
|
5
|
-
forward :restrict, :index_restrict
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Dataset
|
|
19
|
-
include Equalizer.new(:name, :connection)
|
|
20
|
-
attr_reader :name, :connection
|
|
21
|
-
alias_method :ddb, :connection
|
|
22
|
-
|
|
23
|
-
def initialize(name, ddb, conditions = nil)
|
|
24
|
-
@name, @connection = name, ddb
|
|
25
|
-
@conditions = conditions || {}
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
############# READ #############
|
|
29
|
-
def each(&block)
|
|
30
|
-
each_item({
|
|
31
|
-
consistent_read: true,
|
|
32
|
-
key_conditions: @conditions
|
|
33
|
-
}, &block)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def restrict(query = nil)
|
|
37
|
-
return self if query.nil?
|
|
38
|
-
conds = query_to_conditions(query)
|
|
39
|
-
conds = @conditions.merge(conds)
|
|
40
|
-
dup_as(Dataset, conditions: conds)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def index_restrict(index, query)
|
|
44
|
-
conds = query_to_conditions(query)
|
|
45
|
-
conds = @conditions.merge(conds)
|
|
46
|
-
dup_as(GlobalIndexDataset, index: index, conditions: conds)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
############# WRITE #############
|
|
50
|
-
def insert(hash)
|
|
51
|
-
connection.put_item({
|
|
52
|
-
table_name: name,
|
|
53
|
-
item: hash
|
|
54
|
-
})
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def delete(hash)
|
|
58
|
-
connection.delete_item({
|
|
59
|
-
table_name: name,
|
|
60
|
-
key: hash_to_key(hash),
|
|
61
|
-
expected: to_expected(hash),
|
|
62
|
-
})
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
############# HELPERS #############
|
|
66
|
-
private
|
|
67
|
-
def each_item(options, &block)
|
|
68
|
-
puts "Querying #{name} ...\nWith: #{options.inspect}"
|
|
69
|
-
connection.query(options.merge({
|
|
70
|
-
table_name: name
|
|
71
|
-
})).each_page do |page|
|
|
72
|
-
page[:items].each(&block)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def query_to_conditions(query)
|
|
77
|
-
Hash[query.map do |key, value|
|
|
78
|
-
[key, {
|
|
79
|
-
attribute_value_list: [value],
|
|
80
|
-
comparison_operator: "EQ"
|
|
81
|
-
}]
|
|
82
|
-
end]
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def to_expected(hash)
|
|
86
|
-
hash && Hash[hash.map do |k, v|
|
|
87
|
-
[k, { value: v }]
|
|
88
|
-
end]
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def hash_to_key(hash)
|
|
92
|
-
table_keys.each_with_object({}) do |k, out|
|
|
93
|
-
out[k] = hash[k] if hash.has_key?(k)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def table_keys
|
|
98
|
-
@table_keys ||= begin
|
|
99
|
-
resp = ddb.describe_table(table_name: name)
|
|
100
|
-
keys = resp.first[:table][:key_schema]
|
|
101
|
-
keys.map(&:attribute_name)
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def dup_as(klass, opts = {})
|
|
106
|
-
table_keys # To populate keys once at top-level Dataset
|
|
107
|
-
vars = [:@name, :@connection, :@conditions, :@table_keys]
|
|
108
|
-
klass.allocate.tap do |out|
|
|
109
|
-
vars.each { |k| out.instance_variable_set(k, instance_variable_get(k)) }
|
|
110
|
-
opts.each { |k, v| out.instance_variable_set("@#{k}", v) }
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Dataset queried via a Global Index
|
|
116
|
-
class GlobalIndexDataset < Dataset
|
|
117
|
-
attr_accessor :index
|
|
118
|
-
|
|
119
|
-
def each(&block)
|
|
120
|
-
# Pull record IDs from Global Index
|
|
121
|
-
keys = []; each_item({
|
|
122
|
-
key_conditions: @conditions,
|
|
123
|
-
index_name: @index
|
|
124
|
-
}) { |hash| keys << hash_to_key(hash) }
|
|
125
|
-
|
|
126
|
-
# Bail if we have nothing
|
|
127
|
-
return if keys.empty?
|
|
128
|
-
|
|
129
|
-
# Query for the actual records
|
|
130
|
-
ddb.batch_get_item({
|
|
131
|
-
request_items: { name => { keys: keys } },
|
|
132
|
-
}).each_page do |page|
|
|
133
|
-
out = page[:responses][name]
|
|
134
|
-
out.each(&block)
|
|
5
|
+
forward :restrict, :batch_restrict, :index_restrict
|
|
6
|
+
forward :limit, :reversed, :offset
|
|
7
|
+
adapter :dynamo
|
|
8
|
+
|
|
9
|
+
def each_page(&block)
|
|
10
|
+
return enum_for(:each_page) if block.nil?
|
|
11
|
+
dataset.each_page do |page|
|
|
12
|
+
items = page[:items].map { |t| output_schema[t] }
|
|
13
|
+
items = mapper.(items).to_a if auto_map?
|
|
14
|
+
hash = page.to_hash.merge(items: items)
|
|
15
|
+
hash[:last_evaluated_key] ||= nil
|
|
16
|
+
block.call(ROM::OpenStruct.new(hash))
|
|
135
17
|
end
|
|
136
18
|
end
|
|
137
|
-
|
|
138
19
|
end
|
|
139
20
|
end
|
|
140
21
|
end
|
data/lib/rom/dynamo/version.rb
CHANGED
data/lib/rom/dynamo.rb
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
require 'rom'
|
|
2
|
-
require '
|
|
3
|
-
require
|
|
2
|
+
require 'date'
|
|
3
|
+
require 'aws-sdk-dynamodb'
|
|
4
|
+
require 'rom/dynamo/version'
|
|
5
|
+
require 'rom/dynamo/dataset'
|
|
4
6
|
require 'rom/dynamo/relation'
|
|
5
7
|
require 'rom/dynamo/commands'
|
|
6
|
-
require 'rom/dynamo/
|
|
8
|
+
require 'rom/dynamo/gateway'
|
|
7
9
|
|
|
10
|
+
# jRuby HACK: https://github.com/jruby/jruby/issues/3645#issuecomment-181660161
|
|
11
|
+
if RUBY_ENGINE == 'jruby'
|
|
12
|
+
module Aws; const_set(:DynamoDB, Aws::DynamoDB) end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Register adapter with ROM-rb
|
|
8
16
|
ROM.register_adapter(:dynamo, Rom::Dynamo)
|
data/lib/rom-dynamo.rb
CHANGED
data/rom-dynamo.gemspec
CHANGED
|
@@ -23,11 +23,16 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
24
24
|
spec.require_paths = ["lib"]
|
|
25
25
|
|
|
26
|
+
# Ruby 2.0 and above
|
|
27
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
|
28
|
+
|
|
26
29
|
# Runtime
|
|
27
|
-
spec.add_runtime_dependency "
|
|
28
|
-
spec.add_runtime_dependency "
|
|
30
|
+
spec.add_runtime_dependency "addressable", "~> 2.3"
|
|
31
|
+
spec.add_runtime_dependency "rom", ">= 1.0", "< 6.0"
|
|
32
|
+
spec.add_runtime_dependency "aws-sdk-dynamodb", "~> 1.0"
|
|
29
33
|
|
|
30
34
|
# Development
|
|
31
|
-
spec.add_development_dependency "
|
|
32
|
-
spec.add_development_dependency "
|
|
35
|
+
spec.add_development_dependency "activesupport", ">= 4.0", "< 9.0"
|
|
36
|
+
spec.add_development_dependency "bundler", ">= 1.7"
|
|
37
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
33
38
|
end
|
metadata
CHANGED
|
@@ -1,71 +1,111 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rom-dynamo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Rykov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-04-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: addressable
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.3'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.3'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: rom
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
16
30
|
requirements:
|
|
17
|
-
- -
|
|
31
|
+
- - ">="
|
|
18
32
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0
|
|
33
|
+
version: '1.0'
|
|
34
|
+
- - "<"
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '6.0'
|
|
20
37
|
type: :runtime
|
|
21
38
|
prerelease: false
|
|
22
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
40
|
requirements:
|
|
24
|
-
- -
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '1.0'
|
|
44
|
+
- - "<"
|
|
25
45
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0
|
|
46
|
+
version: '6.0'
|
|
27
47
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: aws-sdk-
|
|
48
|
+
name: aws-sdk-dynamodb
|
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
|
30
50
|
requirements:
|
|
31
|
-
- -
|
|
51
|
+
- - "~>"
|
|
32
52
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
53
|
+
version: '1.0'
|
|
34
54
|
type: :runtime
|
|
35
55
|
prerelease: false
|
|
36
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
57
|
requirements:
|
|
38
|
-
- -
|
|
58
|
+
- - "~>"
|
|
39
59
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
60
|
+
version: '1.0'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: activesupport
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '4.0'
|
|
68
|
+
- - "<"
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '9.0'
|
|
71
|
+
type: :development
|
|
72
|
+
prerelease: false
|
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '4.0'
|
|
78
|
+
- - "<"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '9.0'
|
|
41
81
|
- !ruby/object:Gem::Dependency
|
|
42
82
|
name: bundler
|
|
43
83
|
requirement: !ruby/object:Gem::Requirement
|
|
44
84
|
requirements:
|
|
45
|
-
- -
|
|
85
|
+
- - ">="
|
|
46
86
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '1.
|
|
87
|
+
version: '1.7'
|
|
48
88
|
type: :development
|
|
49
89
|
prerelease: false
|
|
50
90
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
91
|
requirements:
|
|
52
|
-
- -
|
|
92
|
+
- - ">="
|
|
53
93
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '1.
|
|
94
|
+
version: '1.7'
|
|
55
95
|
- !ruby/object:Gem::Dependency
|
|
56
96
|
name: rake
|
|
57
97
|
requirement: !ruby/object:Gem::Requirement
|
|
58
98
|
requirements:
|
|
59
|
-
- - ~>
|
|
99
|
+
- - "~>"
|
|
60
100
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
101
|
+
version: '13.0'
|
|
62
102
|
type: :development
|
|
63
103
|
prerelease: false
|
|
64
104
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
105
|
requirements:
|
|
66
|
-
- - ~>
|
|
106
|
+
- - "~>"
|
|
67
107
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
108
|
+
version: '13.0'
|
|
69
109
|
description: DynamoDB adapter for Ruby Object Mapper
|
|
70
110
|
email:
|
|
71
111
|
- mrykov@gmail.com
|
|
@@ -73,9 +113,10 @@ executables: []
|
|
|
73
113
|
extensions: []
|
|
74
114
|
extra_rdoc_files: []
|
|
75
115
|
files:
|
|
76
|
-
- .
|
|
77
|
-
- .
|
|
78
|
-
- .
|
|
116
|
+
- ".github/workflows/specs.yml"
|
|
117
|
+
- ".gitignore"
|
|
118
|
+
- ".rspec"
|
|
119
|
+
- ".travis.yml"
|
|
79
120
|
- Gemfile
|
|
80
121
|
- LICENSE.txt
|
|
81
122
|
- README.md
|
|
@@ -85,8 +126,9 @@ files:
|
|
|
85
126
|
- lib/rom-dynamo.rb
|
|
86
127
|
- lib/rom/dynamo.rb
|
|
87
128
|
- lib/rom/dynamo/commands.rb
|
|
129
|
+
- lib/rom/dynamo/dataset.rb
|
|
130
|
+
- lib/rom/dynamo/gateway.rb
|
|
88
131
|
- lib/rom/dynamo/relation.rb
|
|
89
|
-
- lib/rom/dynamo/repository.rb
|
|
90
132
|
- lib/rom/dynamo/version.rb
|
|
91
133
|
- rom-dynamo.gemspec
|
|
92
134
|
homepage: https://github.com/rykov/rom-dynamo
|
|
@@ -99,17 +141,17 @@ require_paths:
|
|
|
99
141
|
- lib
|
|
100
142
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
143
|
requirements:
|
|
102
|
-
- -
|
|
144
|
+
- - ">="
|
|
103
145
|
- !ruby/object:Gem::Version
|
|
104
|
-
version:
|
|
146
|
+
version: 2.4.0
|
|
105
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
148
|
requirements:
|
|
107
|
-
- -
|
|
149
|
+
- - ">="
|
|
108
150
|
- !ruby/object:Gem::Version
|
|
109
151
|
version: '0'
|
|
110
152
|
requirements: []
|
|
111
153
|
rubyforge_project:
|
|
112
|
-
rubygems_version: 2.
|
|
154
|
+
rubygems_version: 2.7.8
|
|
113
155
|
signing_key:
|
|
114
156
|
specification_version: 4
|
|
115
157
|
summary: DynamoDB adapter for Ruby Object Mapper
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
require 'uri'
|
|
2
|
-
require 'rom/repository'
|
|
3
|
-
|
|
4
|
-
module Rom
|
|
5
|
-
module Dynamo
|
|
6
|
-
class Repository < ROM::Repository
|
|
7
|
-
def initialize(uri)
|
|
8
|
-
uri = URI.parse(uri)
|
|
9
|
-
@connection = Aws::DynamoDB::Client.new(region: uri.host)
|
|
10
|
-
@prefix = uri.path.gsub('/', '')
|
|
11
|
-
@datasets = {}
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def use_logger(logger)
|
|
15
|
-
@logger = logger
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def dataset(name)
|
|
19
|
-
name = "#{@prefix}#{name}"
|
|
20
|
-
@datasets[name] ||= Dataset.new(name, @connection)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def dataset?(name)
|
|
24
|
-
name = "#{@prefix}#{name}"
|
|
25
|
-
list = connection.list_tables
|
|
26
|
-
list[:table_names].include?(name)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def [](name)
|
|
30
|
-
@datasets[name]
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|