commutator 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Changelog.md +15 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +55 -0
- data/README.md +86 -0
- data/Rakefile +6 -0
- data/bin/console +26 -0
- data/bin/setup +8 -0
- data/commutator.gemspec +29 -0
- data/lib/commutator/collection.rb +78 -0
- data/lib/commutator/expressions/attribute_names.rb +24 -0
- data/lib/commutator/expressions/attribute_values.rb +21 -0
- data/lib/commutator/expressions/condition_expression.rb +101 -0
- data/lib/commutator/expressions/projection_expression.rb +50 -0
- data/lib/commutator/expressions/statement.rb +38 -0
- data/lib/commutator/expressions/update_expression.rb +61 -0
- data/lib/commutator/item_modifiers.rb +24 -0
- data/lib/commutator/model/attributes.rb +72 -0
- data/lib/commutator/model/hooks.rb +41 -0
- data/lib/commutator/model/table_configuration.rb +41 -0
- data/lib/commutator/model.rb +239 -0
- data/lib/commutator/options/delete_item.rb +48 -0
- data/lib/commutator/options/get_item.rb +41 -0
- data/lib/commutator/options/proxy.rb +90 -0
- data/lib/commutator/options/put_item.rb +46 -0
- data/lib/commutator/options/query.rb +73 -0
- data/lib/commutator/options/scan.rb +60 -0
- data/lib/commutator/options/update_item.rb +55 -0
- data/lib/commutator/simple_client.rb +27 -0
- data/lib/commutator/util/fluent.rb +67 -0
- data/lib/commutator/util/placeholders.rb +607 -0
- data/lib/commutator/version.rb +3 -0
- data/lib/commutator.rb +64 -0
- metadata +178 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Commutator
|
2
|
+
class ItemModifiers
|
3
|
+
def initialize(modifiers, factory: false)
|
4
|
+
@modifiers = modifiers
|
5
|
+
@factory = factory
|
6
|
+
end
|
7
|
+
|
8
|
+
def factory?
|
9
|
+
@factory
|
10
|
+
end
|
11
|
+
|
12
|
+
# This is a mess, but I wasn't sure how else to guarantee to
|
13
|
+
# call Procs at the time of collection creation
|
14
|
+
def expand_proc_modifiers
|
15
|
+
return self unless factory?
|
16
|
+
|
17
|
+
self.class.new(@modifiers.map(&:call))
|
18
|
+
end
|
19
|
+
|
20
|
+
def modify(item)
|
21
|
+
@modifiers.each { |modifier| modifier.call(item) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Model
|
3
|
+
# This module provides methods related to registering attributes
|
4
|
+
# and generating attr_accessors, ultimately for the purpose of
|
5
|
+
# enabling Commutator::Model to know which attributes to send to the db.
|
6
|
+
module Attributes
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
def assign_attributes(attrs = {})
|
10
|
+
attrs.slice(*attribute_names).each do |attr_name, value|
|
11
|
+
send("#{attr_name}=", value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def attributes
|
16
|
+
attribute_names.each_with_object({}) do |attr_name, hash|
|
17
|
+
hash[attr_name] = send(attr_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def attribute_names
|
22
|
+
self.class.attribute_names
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def convert_type(value, options)
|
28
|
+
return if options.fetch(:allow_nil, true) && value.nil?
|
29
|
+
|
30
|
+
case options[:type]
|
31
|
+
when :array then value.to_a
|
32
|
+
when :boolean then !!value
|
33
|
+
when :float then value.to_f
|
34
|
+
when :hash then value.to_h
|
35
|
+
when :integer then value.to_i
|
36
|
+
when :set then Set.new(value)
|
37
|
+
when :string then value.to_s
|
38
|
+
else value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# :nodoc:
|
43
|
+
module ClassMethods
|
44
|
+
def attribute(*attr_names)
|
45
|
+
options = attr_names.extract_options!
|
46
|
+
|
47
|
+
attr_names.each do |attr_name|
|
48
|
+
attribute_names << attr_name
|
49
|
+
|
50
|
+
# Skip reader and writer methods entirely
|
51
|
+
next if options[:accessor] == false
|
52
|
+
|
53
|
+
define_writer(attr_name, options) unless options[:writer] == false
|
54
|
+
attr_reader attr_name unless options[:reader] == false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def attribute_names
|
59
|
+
@attribute_names ||= Set.new
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def define_writer(attr_name, options)
|
65
|
+
define_method "#{attr_name}=" do |value|
|
66
|
+
instance_variable_set("@#{attr_name}", convert_type(value, options))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Model
|
3
|
+
module Hooks
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def run_before_hooks(operation, options)
|
7
|
+
self.class.before_hooks[operation].each do |method_name|
|
8
|
+
send(method_name, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
options
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
API_ITEM_OPERATIONS.each do |operation|
|
16
|
+
define_method "before_#{operation}" do |*args|
|
17
|
+
add_before_hook(operation, *args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_before_hook(operation, *method_names)
|
22
|
+
method_names.each do |method_name|
|
23
|
+
before_hooks[operation] << method_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_hooks
|
28
|
+
@before_hooks ||= Hash.new { |h, k| h[k] = [] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_before_hooks(operation, options)
|
32
|
+
before_hooks[operation].each do |method_name|
|
33
|
+
send(method_name, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
options
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Model
|
3
|
+
# Some basic configuration related to a Dynamo table
|
4
|
+
module TableConfiguration
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
delegate :primary_key_hash_name,
|
8
|
+
:primary_key_range_name,
|
9
|
+
to: 'self.class'
|
10
|
+
|
11
|
+
def primary_key_hash
|
12
|
+
send(primary_key_hash_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def primary_key_range
|
16
|
+
send(primary_key_range_name) if primary_key_range_name.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_name
|
20
|
+
self.class.table_name
|
21
|
+
end
|
22
|
+
|
23
|
+
# :nodoc:
|
24
|
+
module ClassMethods
|
25
|
+
attr_reader :primary_key_hash_name,
|
26
|
+
:primary_key_range_name
|
27
|
+
|
28
|
+
def primary_key(options = {})
|
29
|
+
@primary_key_hash_name = options[:hash]
|
30
|
+
@primary_key_range_name = options[:range]
|
31
|
+
end
|
32
|
+
|
33
|
+
def table_name(table_name = nil)
|
34
|
+
return @table_name unless table_name.present?
|
35
|
+
|
36
|
+
@table_name = table_name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require "commutator/model/attributes"
|
2
|
+
require "commutator/model/table_configuration"
|
3
|
+
require "commutator/model/hooks"
|
4
|
+
|
5
|
+
module Commutator
|
6
|
+
# Basic CRUD wrapper for items in a dynamodb table.
|
7
|
+
#
|
8
|
+
# This module is focused on collections of homogenous items within a
|
9
|
+
# single table.
|
10
|
+
#
|
11
|
+
# TODO: support multiple tables
|
12
|
+
#
|
13
|
+
# class Person
|
14
|
+
# include Commutator::Model
|
15
|
+
#
|
16
|
+
# attribute :first_name,
|
17
|
+
# :last_name,
|
18
|
+
# :email
|
19
|
+
#
|
20
|
+
# attribute :age, type: :integer
|
21
|
+
# attribute :favorite_color, writer: false
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# primary_key hash: :email, range: :age
|
25
|
+
#
|
26
|
+
# def favorite_color=(color)
|
27
|
+
# @color = color.downcase
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
module Model
|
31
|
+
extend ActiveSupport::Concern
|
32
|
+
|
33
|
+
include ActiveModel::Model
|
34
|
+
include Commutator::Model::Attributes
|
35
|
+
include Commutator::Model::TableConfiguration
|
36
|
+
include Commutator::Model::Hooks
|
37
|
+
|
38
|
+
# These build up some basic Dynamo request options related to a particular
|
39
|
+
# api action so that we don't have to specify them every time. They don't
|
40
|
+
# override any values if they are present.
|
41
|
+
included do
|
42
|
+
before_put_item :configure_default_put_item
|
43
|
+
before_update_item :configure_default_update_item
|
44
|
+
before_delete_item :configure_default_delete_item
|
45
|
+
|
46
|
+
before_query :configure_default_query
|
47
|
+
before_scan :configure_default_scan
|
48
|
+
before_get_item :configure_default_get_item
|
49
|
+
|
50
|
+
class_attribute :collection_item_modifiers, instance_accessor: false
|
51
|
+
end
|
52
|
+
|
53
|
+
delegate :client, :options_class, to: 'self.class'
|
54
|
+
|
55
|
+
def initialize(attrs = {})
|
56
|
+
assign_attributes(attrs.symbolize_keys)
|
57
|
+
end
|
58
|
+
|
59
|
+
def put_item_options
|
60
|
+
self.class.build_options_proxy(:put_item, self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def update_item_options
|
64
|
+
self.class.build_options_proxy(:update_item, self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete_item_options
|
68
|
+
self.class.build_options_proxy(:delete_item, self)
|
69
|
+
end
|
70
|
+
|
71
|
+
def put_item(options = nil)
|
72
|
+
dynamo_request(:put_item, options) unless invalid?
|
73
|
+
errors.empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
def update_item(options = nil)
|
77
|
+
dynamo_request(:update_item, options) unless invalid?
|
78
|
+
errors.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_item(options = nil)
|
82
|
+
dynamo_request(:delete_item, options)
|
83
|
+
return false if errors.present?
|
84
|
+
|
85
|
+
@deleted = true
|
86
|
+
freeze
|
87
|
+
end
|
88
|
+
|
89
|
+
def deleted?
|
90
|
+
@deleted == true
|
91
|
+
end
|
92
|
+
|
93
|
+
def ==(other)
|
94
|
+
self.class == other.class &&
|
95
|
+
primary_key_hash == other.primary_key_hash &&
|
96
|
+
primary_key_range == other.primary_key_range &&
|
97
|
+
attributes == other.attributes
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def configure_default_put_item(options)
|
103
|
+
options
|
104
|
+
.table_name(table_name)
|
105
|
+
.item(attributes.stringify_keys)
|
106
|
+
end
|
107
|
+
|
108
|
+
def configure_default_update_item(options)
|
109
|
+
options.table_name(table_name)
|
110
|
+
end
|
111
|
+
|
112
|
+
def configure_default_delete_item(options)
|
113
|
+
options
|
114
|
+
.table_name(table_name)
|
115
|
+
.with_key { |key| key[primary_key_hash_name] = primary_key_hash }
|
116
|
+
|
117
|
+
return unless primary_key_range.present?
|
118
|
+
|
119
|
+
options.with_key { |key| key[primary_key_range_name] = primary_key_range }
|
120
|
+
end
|
121
|
+
|
122
|
+
def dynamo_request(operation, options)
|
123
|
+
options ||= self.class.options_class(operation).new
|
124
|
+
run_before_hooks(operation, options)
|
125
|
+
client.send(operation, options)
|
126
|
+
rescue Aws::DynamoDB::Errors::ValidationException,
|
127
|
+
Aws::DynamoDB::Errors::ServiceError => e
|
128
|
+
errors.add(:base, e.message)
|
129
|
+
end
|
130
|
+
|
131
|
+
# :nodoc:
|
132
|
+
module ClassMethods
|
133
|
+
attr_writer :client
|
134
|
+
|
135
|
+
def inherited(subclass)
|
136
|
+
subclass.attribute_names.merge(attribute_names)
|
137
|
+
before_hooks.each { |k, v| subclass.before_hooks[k] = v.dup }
|
138
|
+
|
139
|
+
subclass.table_name(table_name)
|
140
|
+
subclass.primary_key(hash: primary_key_hash_name,
|
141
|
+
range: primary_key_range_name)
|
142
|
+
|
143
|
+
scopes = const_defined?("Scopes", false) ? const_get("Scopes") : nil
|
144
|
+
subclass.const_set("Scopes", Module.new { include scopes }) if scopes
|
145
|
+
end
|
146
|
+
|
147
|
+
def client
|
148
|
+
@client ||= ::Commutator::SimpleClient.new
|
149
|
+
end
|
150
|
+
|
151
|
+
def create(attrs)
|
152
|
+
new(attrs).tap { |dp| dp.put_item_options.execute }
|
153
|
+
end
|
154
|
+
|
155
|
+
def modify_collection_items_with(*modifiers, factory: false)
|
156
|
+
self.collection_item_modifiers = [ItemModifiers.new(modifiers, factory: factory)].unshift(*collection_item_modifiers)
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_item_options
|
160
|
+
build_options_proxy(:get_item)
|
161
|
+
end
|
162
|
+
|
163
|
+
def query_options
|
164
|
+
build_options_proxy(:query)
|
165
|
+
end
|
166
|
+
|
167
|
+
def scan_options
|
168
|
+
build_options_proxy(:scan)
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_item(options = build_options_proxy(:get_item))
|
172
|
+
item = client.get_item(options).item
|
173
|
+
new(item) unless item.nil?
|
174
|
+
end
|
175
|
+
|
176
|
+
def query(options = build_options_proxy(:query))
|
177
|
+
collection_for(:query, options)
|
178
|
+
end
|
179
|
+
|
180
|
+
def scan(options = build_options_proxy(:scan))
|
181
|
+
collection_for(:scan, options)
|
182
|
+
end
|
183
|
+
|
184
|
+
def build_options_proxy(operation, context = self)
|
185
|
+
Options::Proxy.new(context, operation)
|
186
|
+
end
|
187
|
+
|
188
|
+
def options_class(operation)
|
189
|
+
@scoped_options ||= Hash.new do |h, k|
|
190
|
+
scopes = self.const_defined?("Scopes", false) ? self.const_get("Scopes") : nil
|
191
|
+
const_name = k.to_s.camelize
|
192
|
+
h[k] = enhance_options(const_name, scopes)
|
193
|
+
end
|
194
|
+
@scoped_options[operation]
|
195
|
+
end
|
196
|
+
|
197
|
+
def method_missing(method, *args)
|
198
|
+
super unless respond_to?(method)
|
199
|
+
query_options.send(method, *args)
|
200
|
+
end
|
201
|
+
|
202
|
+
def respond_to?(method)
|
203
|
+
super || (const_defined?(:Scopes, false) && const_get(:Scopes).method_defined?(method))
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def enhance_options(const_name, scopes = nil)
|
209
|
+
Class.new(Options.const_get(const_name)) do
|
210
|
+
include ::Commutator::Util::Fluent
|
211
|
+
include scopes if scopes && %w[Query Scan].include?(const_name)
|
212
|
+
|
213
|
+
fluent_accessor :_proxy
|
214
|
+
delegate :context, to: :_proxy
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def collection_for(operation, options)
|
219
|
+
Collection.new(
|
220
|
+
client.send(operation, options),
|
221
|
+
self,
|
222
|
+
modifiers: Array(collection_item_modifiers)
|
223
|
+
)
|
224
|
+
end
|
225
|
+
|
226
|
+
def configure_default_query(options)
|
227
|
+
options.table_name(table_name)
|
228
|
+
end
|
229
|
+
|
230
|
+
def configure_default_scan(options)
|
231
|
+
options.table_name(table_name)
|
232
|
+
end
|
233
|
+
|
234
|
+
def configure_default_get_item(options)
|
235
|
+
options.table_name(table_name)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Options
|
3
|
+
class DeleteItem
|
4
|
+
include Util::Fluent
|
5
|
+
|
6
|
+
fluent_accessor :key,
|
7
|
+
:condition_expression,
|
8
|
+
:expression_attribute_names,
|
9
|
+
:expression_attribute_values,
|
10
|
+
:table_name,
|
11
|
+
:return_values,
|
12
|
+
:return_consumed_capacity,
|
13
|
+
:return_item_collection_metrics
|
14
|
+
|
15
|
+
fluent_wrapper :key,
|
16
|
+
:condition_expression,
|
17
|
+
:expression_attribute_names,
|
18
|
+
:expression_attribute_values
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@key = {}
|
22
|
+
|
23
|
+
@expression_attribute_names = Expressions::AttributeNames.new
|
24
|
+
@expression_attribute_values = Expressions::AttributeValues.new
|
25
|
+
|
26
|
+
@condition_expression = Expressions::ConditionExpression.new(
|
27
|
+
attribute_names: @expression_attribute_names,
|
28
|
+
attribute_values: @expression_attribute_values)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
hash = {
|
33
|
+
key: key,
|
34
|
+
table_name: table_name,
|
35
|
+
return_values: return_values,
|
36
|
+
return_consumed_capacity: return_consumed_capacity,
|
37
|
+
return_item_collection_metrics: return_item_collection_metrics,
|
38
|
+
expression_attribute_names: expression_attribute_names.to_h,
|
39
|
+
expression_attribute_values: expression_attribute_values.to_h,
|
40
|
+
condition_expression: condition_expression.to_s(wrap: false)
|
41
|
+
}
|
42
|
+
|
43
|
+
hash.keep_if { |_key, value| value.present? || value == false }
|
44
|
+
end
|
45
|
+
alias :to_hash :to_h
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Options
|
3
|
+
class GetItem
|
4
|
+
include Util::Fluent
|
5
|
+
|
6
|
+
fluent_accessor :key,
|
7
|
+
:projection_expression,
|
8
|
+
:expression_attribute_names,
|
9
|
+
:consistent_read,
|
10
|
+
:table_name,
|
11
|
+
:return_consumed_capacity
|
12
|
+
|
13
|
+
fluent_wrapper :key,
|
14
|
+
:projection_expression,
|
15
|
+
:expression_attribute_names
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@key = {}
|
19
|
+
|
20
|
+
@expression_attribute_names = Expressions::AttributeNames.new
|
21
|
+
|
22
|
+
@projection_expression = Expressions::ProjectionExpression.new(
|
23
|
+
attribute_names: @expression_attribute_names)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_h
|
27
|
+
hash = {
|
28
|
+
table_name: table_name,
|
29
|
+
expression_attribute_names: expression_attribute_names.to_h,
|
30
|
+
consistent_read: consistent_read,
|
31
|
+
return_consumed_capacity: return_consumed_capacity,
|
32
|
+
projection_expression: projection_expression.to_s,
|
33
|
+
key: key
|
34
|
+
}
|
35
|
+
|
36
|
+
hash.keep_if { |_key, value| value.present? || value == false }
|
37
|
+
end
|
38
|
+
alias :to_hash :to_h
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Options
|
3
|
+
class Proxy
|
4
|
+
attr_reader :proxied_history, :options
|
5
|
+
|
6
|
+
def initialize(context, callback_name)
|
7
|
+
@context = context
|
8
|
+
@callback_name = callback_name
|
9
|
+
@proxied_history = []
|
10
|
+
|
11
|
+
@options = instantiate_options
|
12
|
+
end
|
13
|
+
|
14
|
+
class Operation
|
15
|
+
attr_reader :method, :args, :block
|
16
|
+
|
17
|
+
def initialize(method, args, block)
|
18
|
+
@method = method
|
19
|
+
@args = args
|
20
|
+
@block = block
|
21
|
+
end
|
22
|
+
|
23
|
+
def apply(options, chainable_history = nil)
|
24
|
+
options.send(method, *args, &block).tap do |result|
|
25
|
+
# if result == self then that was a call to #with_context
|
26
|
+
chainable_history << self if chainable_history && result == options
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method_name, *args, &block)
|
32
|
+
super unless options.respond_to?(method_name)
|
33
|
+
|
34
|
+
operation = Operation.new(method_name, args, block)
|
35
|
+
result = operation.apply(@options, @proxied_history)
|
36
|
+
result != options ? result : self
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to?(*args)
|
40
|
+
super || options.respond_to?(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def context(context)
|
44
|
+
@context = context
|
45
|
+
@options = rehydrate_options
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def options
|
50
|
+
@options ||= rehydrate_options
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute
|
54
|
+
@context.send(callback_name, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def first
|
58
|
+
# TODO: asc / desc only work on Query (not Scan)
|
59
|
+
limit(1).asc.execute.items.first
|
60
|
+
end
|
61
|
+
|
62
|
+
def last
|
63
|
+
# TODO: asc / desc only work on Query (not Scan)
|
64
|
+
limit(1).desc.execute.items.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def count
|
68
|
+
response = @context.client.send(callback_name, options.dup.select("COUNT"))
|
69
|
+
response.inject(0) { |sum, page| sum + page.count }
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def instantiate_options
|
75
|
+
@context.run_before_hooks(
|
76
|
+
@callback_name,
|
77
|
+
@context.options_class(@callback_name).new._proxy(self)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def rehydrate_options
|
82
|
+
instantiate_options.tap do |options|
|
83
|
+
proxied_history.each { |operation| operation.apply(options) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_reader :callback_name
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Commutator
|
2
|
+
module Options
|
3
|
+
class PutItem
|
4
|
+
include Util::Fluent
|
5
|
+
|
6
|
+
fluent_accessor :item,
|
7
|
+
:condition_expression,
|
8
|
+
:expression_attribute_names,
|
9
|
+
:expression_attribute_values,
|
10
|
+
:return_consumed_capacity,
|
11
|
+
:return_item_collection_metrics,
|
12
|
+
:return_values,
|
13
|
+
:table_name
|
14
|
+
|
15
|
+
fluent_wrapper :item,
|
16
|
+
:condition_expression,
|
17
|
+
:expression_attribute_names,
|
18
|
+
:expression_attribute_values
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@expression_attribute_names ||= Expressions::AttributeNames.new
|
22
|
+
@expression_attribute_values ||= Expressions::AttributeValues.new
|
23
|
+
|
24
|
+
@condition_expression ||= Expressions::ConditionExpression.new(
|
25
|
+
attribute_names: @expression_attribute_names,
|
26
|
+
attribute_values: @expression_attribute_values)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
hash = {
|
31
|
+
condition_expression: condition_expression.to_s(wrap: false),
|
32
|
+
expression_attribute_names: expression_attribute_names.to_h,
|
33
|
+
expression_attribute_values: expression_attribute_values.to_h,
|
34
|
+
item: item.to_h,
|
35
|
+
return_consumed_capacity: return_consumed_capacity,
|
36
|
+
return_item_collection_metrics: return_item_collection_metrics,
|
37
|
+
return_values: return_values,
|
38
|
+
table_name: table_name
|
39
|
+
}
|
40
|
+
|
41
|
+
hash.keep_if { |_key, value| value.present? || value == false }
|
42
|
+
end
|
43
|
+
alias :to_hash :to_h
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|