dynamoid 1.3.4 → 2.0.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 +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +3 -0
- data/.travis.yml +32 -7
- data/Appraisals +7 -0
- data/CHANGELOG.md +69 -2
- data/Gemfile +2 -0
- data/README.md +108 -28
- data/Rakefile +0 -24
- data/docker-compose.yml +7 -0
- data/dynamoid.gemspec +2 -3
- data/gemfiles/rails_4_0.gemfile +2 -3
- data/gemfiles/rails_4_1.gemfile +2 -3
- data/gemfiles/rails_4_2.gemfile +2 -3
- data/gemfiles/rails_5_0.gemfile +1 -1
- data/gemfiles/rails_5_1.gemfile +7 -0
- data/lib/dynamoid.rb +31 -31
- data/lib/dynamoid/adapter.rb +5 -5
- data/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb +84 -57
- data/lib/dynamoid/associations.rb +21 -12
- data/lib/dynamoid/associations/association.rb +19 -3
- data/lib/dynamoid/associations/belongs_to.rb +26 -16
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -16
- data/lib/dynamoid/associations/has_many.rb +2 -17
- data/lib/dynamoid/associations/has_one.rb +0 -14
- data/lib/dynamoid/associations/many_association.rb +19 -6
- data/lib/dynamoid/associations/single_association.rb +25 -7
- data/lib/dynamoid/config.rb +18 -18
- data/lib/dynamoid/config/options.rb +1 -1
- data/lib/dynamoid/criteria/chain.rb +29 -21
- data/lib/dynamoid/dirty.rb +2 -2
- data/lib/dynamoid/document.rb +17 -5
- data/lib/dynamoid/errors.rb +4 -1
- data/lib/dynamoid/fields.rb +6 -6
- data/lib/dynamoid/finders.rb +19 -9
- data/lib/dynamoid/identity_map.rb +0 -1
- data/lib/dynamoid/indexes.rb +41 -54
- data/lib/dynamoid/persistence.rb +54 -24
- data/lib/dynamoid/railtie.rb +1 -1
- data/lib/dynamoid/validations.rb +4 -3
- data/lib/dynamoid/version.rb +1 -1
- metadata +14 -29
- data/gemfiles/rails_4_0.gemfile.lock +0 -150
- data/gemfiles/rails_4_1.gemfile.lock +0 -154
- data/gemfiles/rails_4_2.gemfile.lock +0 -175
- data/gemfiles/rails_5_0.gemfile.lock +0 -180
data/Rakefile
CHANGED
@@ -18,30 +18,6 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
18
18
|
spec.pattern = FileList["spec/**/*_spec.rb"]
|
19
19
|
end
|
20
20
|
|
21
|
-
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
22
|
-
spec.pattern = "spec/**/*_spec.rb"
|
23
|
-
spec.rcov = true
|
24
|
-
end
|
25
|
-
|
26
|
-
desc "Start DynamoDBLocal, run tests, clean up"
|
27
|
-
task :unattended_spec do |t|
|
28
|
-
|
29
|
-
if system("bin/start_dynamodblocal")
|
30
|
-
puts "DynamoDBLocal started; proceeding with specs."
|
31
|
-
else
|
32
|
-
raise "Unable to start DynamoDBLocal. Cannot run unattended specs."
|
33
|
-
end
|
34
|
-
|
35
|
-
#Cleanup
|
36
|
-
at_exit do
|
37
|
-
unless system("bin/stop_dynamodblocal")
|
38
|
-
$stderr.puts "Unable to cleanly stop DynamoDBLocal."
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
Rake::Task["spec"].invoke
|
43
|
-
end
|
44
|
-
|
45
21
|
require "yard"
|
46
22
|
YARD::Rake::YardocTask.new do |t|
|
47
23
|
t.files = ["lib/**/*.rb", "README", "LICENSE"] # optional
|
data/docker-compose.yml
ADDED
data/dynamoid.gemspec
CHANGED
@@ -50,13 +50,12 @@ Gem::Specification.new do |spec|
|
|
50
50
|
spec.add_development_dependency(%q<activesupport>, [">= 4"])
|
51
51
|
spec.add_runtime_dependency(%q<aws-sdk-resources>, ["~> 2"])
|
52
52
|
spec.add_runtime_dependency(%q<concurrent-ruby>, [">= 1.0"])
|
53
|
-
spec.add_development_dependency "pry"
|
53
|
+
spec.add_development_dependency "pry"
|
54
54
|
spec.add_development_dependency "bundler", "~> 1.14"
|
55
55
|
spec.add_development_dependency "rake", "~> 12.0"
|
56
56
|
spec.add_development_dependency "rspec", "~> 3.0"
|
57
57
|
spec.add_development_dependency "appraisal", "~> 2.1"
|
58
58
|
spec.add_development_dependency "wwtd", "~> 1.3"
|
59
59
|
spec.add_development_dependency(%q<yard>, [">= 0"])
|
60
|
-
spec.add_development_dependency
|
61
|
-
spec.add_development_dependency(%q<rspec-retry>, [">= 0"])
|
60
|
+
spec.add_development_dependency "coveralls", "~> 0.8"
|
62
61
|
end
|
data/gemfiles/rails_4_0.gemfile
CHANGED
data/gemfiles/rails_4_1.gemfile
CHANGED
data/gemfiles/rails_4_2.gemfile
CHANGED
data/gemfiles/rails_5_0.gemfile
CHANGED
data/lib/dynamoid.rb
CHANGED
@@ -1,36 +1,36 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
27
|
-
|
28
|
-
require
|
29
|
-
|
30
|
-
require
|
1
|
+
require 'delegate'
|
2
|
+
require 'time'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
require 'active_support/json'
|
7
|
+
require 'active_support/inflector'
|
8
|
+
require 'active_support/lazy_load_hooks'
|
9
|
+
require 'active_support/time_with_zone'
|
10
|
+
require 'active_model'
|
11
|
+
|
12
|
+
require 'dynamoid/version'
|
13
|
+
require 'dynamoid/errors'
|
14
|
+
require 'dynamoid/fields'
|
15
|
+
require 'dynamoid/indexes'
|
16
|
+
require 'dynamoid/associations'
|
17
|
+
require 'dynamoid/persistence'
|
18
|
+
require 'dynamoid/dirty'
|
19
|
+
require 'dynamoid/validations'
|
20
|
+
require 'dynamoid/criteria'
|
21
|
+
require 'dynamoid/finders'
|
22
|
+
require 'dynamoid/identity_map'
|
23
|
+
require 'dynamoid/config'
|
24
|
+
require 'dynamoid/components'
|
25
|
+
require 'dynamoid/document'
|
26
|
+
require 'dynamoid/adapter'
|
27
|
+
|
28
|
+
require 'dynamoid/tasks/database'
|
29
|
+
|
30
|
+
require 'dynamoid/middleware/identity_map'
|
31
31
|
|
32
32
|
if defined?(Rails)
|
33
|
-
require
|
33
|
+
require 'dynamoid/railtie'
|
34
34
|
end
|
35
35
|
|
36
36
|
module Dynamoid
|
data/lib/dynamoid/adapter.rb
CHANGED
@@ -99,13 +99,13 @@ module Dynamoid
|
|
99
99
|
# @param [Array] range_key of the record to delete, can also be a string of just one range_key
|
100
100
|
#
|
101
101
|
def delete(table, ids, options = {})
|
102
|
-
range_key = options[:range_key] #array of range keys that matches the ids passed in
|
102
|
+
range_key = options[:range_key] # array of range keys that matches the ids passed in
|
103
103
|
if ids.respond_to?(:each)
|
104
104
|
if range_key.respond_to?(:each)
|
105
|
-
#turn ids into array of arrays each element being hash_key, range_key
|
106
|
-
ids = ids.each_with_index.map{|id,i| [id,range_key[i]]}
|
105
|
+
# turn ids into array of arrays each element being hash_key, range_key
|
106
|
+
ids = ids.each_with_index.map{|id, i| [id, range_key[i]]}
|
107
107
|
else
|
108
|
-
ids = range_key ? [
|
108
|
+
ids = range_key ? ids.map { |id| [id, range_key] } : ids
|
109
109
|
end
|
110
110
|
|
111
111
|
batch_delete_item(table => ids)
|
@@ -120,7 +120,7 @@ module Dynamoid
|
|
120
120
|
# @param [Hash] scan_hash a hash of attributes: matching records will be returned by the scan
|
121
121
|
#
|
122
122
|
# @since 0.2.0
|
123
|
-
def scan(table, query, opts = {})
|
123
|
+
def scan(table, query = {}, opts = {})
|
124
124
|
benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
|
125
125
|
end
|
126
126
|
|
@@ -3,7 +3,7 @@ module Dynamoid
|
|
3
3
|
|
4
4
|
# The AwsSdkV2 adapter provides support for the aws-sdk version 2 for ruby.
|
5
5
|
class AwsSdkV2
|
6
|
-
EQ =
|
6
|
+
EQ = 'EQ'.freeze
|
7
7
|
RANGE_MAP = {
|
8
8
|
range_greater_than: 'GT',
|
9
9
|
range_less_than: 'LT',
|
@@ -28,16 +28,16 @@ module Dynamoid
|
|
28
28
|
contains: 'CONTAINS',
|
29
29
|
not_contains: 'NOT_CONTAINS'
|
30
30
|
}
|
31
|
-
HASH_KEY =
|
32
|
-
RANGE_KEY =
|
33
|
-
STRING_TYPE =
|
34
|
-
NUM_TYPE =
|
35
|
-
BINARY_TYPE =
|
31
|
+
HASH_KEY = 'HASH'.freeze
|
32
|
+
RANGE_KEY = 'RANGE'.freeze
|
33
|
+
STRING_TYPE = 'S'.freeze
|
34
|
+
NUM_TYPE = 'N'.freeze
|
35
|
+
BINARY_TYPE = 'B'.freeze
|
36
36
|
TABLE_STATUSES = {
|
37
|
-
creating:
|
38
|
-
updating:
|
39
|
-
deleting:
|
40
|
-
active:
|
37
|
+
creating: 'CREATING',
|
38
|
+
updating: 'UPDATING',
|
39
|
+
deleting: 'DELETING',
|
40
|
+
active: 'ACTIVE'
|
41
41
|
}.freeze
|
42
42
|
PARSE_TABLE_STATUS = ->(resp, lookup = :table) {
|
43
43
|
# lookup is table for describe_table API
|
@@ -45,6 +45,8 @@ module Dynamoid
|
|
45
45
|
# because Amazon, damnit.
|
46
46
|
resp.send(lookup).table_status
|
47
47
|
}
|
48
|
+
BATCH_WRITE_ITEM_REQUESTS_LIMIT = 25
|
49
|
+
|
48
50
|
attr_reader :table_cache
|
49
51
|
|
50
52
|
# Establish the connection to DynamoDB.
|
@@ -87,24 +89,31 @@ module Dynamoid
|
|
87
89
|
# @param [Array] items to be processed
|
88
90
|
# @param [Hash] additional options
|
89
91
|
#
|
90
|
-
#See:
|
92
|
+
# See:
|
93
|
+
# * http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
|
94
|
+
# * http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method
|
95
|
+
#
|
96
|
+
# TODO handle rejections because of exceeding limit for the whole request - 16 MB,
|
97
|
+
# item size limit - 400 KB or because provisioned throughput is exceeded
|
91
98
|
def batch_write_item table_name, objects, options = {}
|
92
|
-
|
93
|
-
|
94
|
-
objects.
|
95
|
-
|
99
|
+
requests = []
|
100
|
+
|
101
|
+
objects.each_slice(BATCH_WRITE_ITEM_REQUESTS_LIMIT) do |os|
|
102
|
+
requests << os.map { |o| { put_request: { item: o } } }
|
96
103
|
end
|
97
104
|
|
98
105
|
begin
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
requests.each do |request_items|
|
107
|
+
client.batch_write_item(
|
108
|
+
{
|
109
|
+
request_items: {
|
110
|
+
table_name => request_items,
|
111
|
+
},
|
112
|
+
return_consumed_capacity: 'TOTAL',
|
113
|
+
return_item_collection_metrics: 'SIZE'
|
114
|
+
}.merge!(options)
|
115
|
+
)
|
116
|
+
end
|
108
117
|
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
109
118
|
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
110
119
|
end
|
@@ -139,7 +148,7 @@ module Dynamoid
|
|
139
148
|
request_items = Hash.new{|h, k| h[k] = []}
|
140
149
|
|
141
150
|
keys = if rng.present?
|
142
|
-
Array(ids).map do |h,r|
|
151
|
+
Array(ids).map do |h, r|
|
143
152
|
{ hk => h, rng => r }
|
144
153
|
end
|
145
154
|
else
|
@@ -169,22 +178,41 @@ module Dynamoid
|
|
169
178
|
#
|
170
179
|
# @example Delete IDs 1 and 2 from the table testtable
|
171
180
|
# Dynamoid::AdapterPlugin::AwsSdk.batch_delete_item('table1' => ['1', '2'])
|
172
|
-
#or
|
181
|
+
# or
|
173
182
|
# Dynamoid::AdapterPlugin::AwsSdkV2.batch_delete_item('table1' => [['hk1', 'rk2'], ['hk1', 'rk2']]]))
|
174
183
|
#
|
175
184
|
# @param [Hash] options the hash of tables and IDs to delete
|
176
185
|
#
|
177
|
-
#
|
186
|
+
# See:
|
187
|
+
# * http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
|
188
|
+
# * http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method
|
178
189
|
#
|
179
|
-
#
|
190
|
+
# TODO handle rejections because of internal processing failures
|
180
191
|
def batch_delete_item(options)
|
192
|
+
requests = []
|
193
|
+
|
181
194
|
options.each_pair do |table_name, ids|
|
182
195
|
table = describe_table(table_name)
|
183
|
-
|
184
|
-
|
196
|
+
|
197
|
+
ids.each_slice(BATCH_WRITE_ITEM_REQUESTS_LIMIT) do |sliced_ids|
|
198
|
+
delete_requests = sliced_ids.map { |id|
|
199
|
+
{delete_request: {key: key_stanza(table, *id)}}
|
200
|
+
}
|
201
|
+
|
202
|
+
requests << {table_name => delete_requests}
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
begin
|
207
|
+
requests.map do |request_items|
|
208
|
+
client.batch_write_item(
|
209
|
+
request_items: request_items,
|
210
|
+
return_consumed_capacity: 'TOTAL',
|
211
|
+
return_item_collection_metrics: 'SIZE')
|
185
212
|
end
|
213
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
214
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException, e
|
186
215
|
end
|
187
|
-
nil
|
188
216
|
end
|
189
217
|
|
190
218
|
# Create a table on DynamoDB. This usually takes a long time to complete.
|
@@ -210,8 +238,8 @@ module Dynamoid
|
|
210
238
|
gs_indexes = options[:global_secondary_indexes]
|
211
239
|
|
212
240
|
key_schema = {
|
213
|
-
:
|
214
|
-
:
|
241
|
+
hash_key_schema: { key => (options[:hash_key_type] || :string) },
|
242
|
+
range_key_schema: options[:range_key]
|
215
243
|
}
|
216
244
|
attribute_definitions = build_all_attribute_definitions(
|
217
245
|
key_schema,
|
@@ -367,7 +395,7 @@ module Dynamoid
|
|
367
395
|
key: key_stanza(table, key, range_key),
|
368
396
|
attribute_updates: iu.to_h,
|
369
397
|
expected: expected_stanza(conditions),
|
370
|
-
return_values:
|
398
|
+
return_values: 'ALL_NEW'
|
371
399
|
)
|
372
400
|
result_item_to_hash(result[:attributes])
|
373
401
|
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
@@ -489,7 +517,7 @@ module Dynamoid
|
|
489
517
|
end
|
490
518
|
|
491
519
|
query_filter = {}
|
492
|
-
opts.reject {|k,_| k.in? RANGE_MAP.keys}.each do |attr, hash|
|
520
|
+
opts.reject {|k, _| k.in? RANGE_MAP.keys}.each do |attr, hash|
|
493
521
|
query_filter[attr] = {
|
494
522
|
comparison_operator: FIELD_MAP[hash.keys[0]],
|
495
523
|
attribute_value_list: [
|
@@ -557,7 +585,7 @@ module Dynamoid
|
|
557
585
|
# @since 1.0.0
|
558
586
|
#
|
559
587
|
# @todo: Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#scan-instance_method
|
560
|
-
def scan(table_name, scan_hash, select_opts = {})
|
588
|
+
def scan(table_name, scan_hash = {}, select_opts = {})
|
561
589
|
request = { table_name: table_name }
|
562
590
|
request[:consistent_read] = true if select_opts.delete(:consistent_read)
|
563
591
|
|
@@ -661,7 +689,7 @@ module Dynamoid
|
|
661
689
|
check = {again: true}
|
662
690
|
while check[:again]
|
663
691
|
sleep Dynamoid::Config.sync_retry_wait_seconds
|
664
|
-
resp = client.describe_table(
|
692
|
+
resp = client.describe_table(table_name: table_name)
|
665
693
|
check = check_table_status?(counter, resp, status)
|
666
694
|
Dynamoid.logger.info "Checked table status for #{table_name} (check #{check.inspect})"
|
667
695
|
counter += 1
|
@@ -689,7 +717,7 @@ module Dynamoid
|
|
689
717
|
end
|
690
718
|
end
|
691
719
|
|
692
|
-
#Converts from symbol to the API string for the given data type
|
720
|
+
# Converts from symbol to the API string for the given data type
|
693
721
|
# E.g. :number -> 'N'
|
694
722
|
def api_type(type)
|
695
723
|
case(type)
|
@@ -714,13 +742,13 @@ module Dynamoid
|
|
714
742
|
# @return an Expected stanza for the given conditions hash
|
715
743
|
#
|
716
744
|
def expected_stanza(conditions = nil)
|
717
|
-
expected = Hash.new { |h,k| h[k] = {} }
|
745
|
+
expected = Hash.new { |h, k| h[k] = {} }
|
718
746
|
return expected unless conditions
|
719
747
|
|
720
748
|
conditions.delete(:unless_exists).try(:each) do |col|
|
721
749
|
expected[col.to_s][:exists] = false
|
722
750
|
end
|
723
|
-
conditions.delete(:if).try(:each) do |col,val|
|
751
|
+
conditions.delete(:if).try(:each) do |col, val|
|
724
752
|
expected[col.to_s][:value] = val
|
725
753
|
end
|
726
754
|
|
@@ -741,7 +769,7 @@ module Dynamoid
|
|
741
769
|
#
|
742
770
|
def result_item_to_hash(item)
|
743
771
|
{}.tap do |r|
|
744
|
-
item.each { |k,v| r[k.to_sym] = v }
|
772
|
+
item.each { |k, v| r[k.to_sym] = v }
|
745
773
|
end
|
746
774
|
end
|
747
775
|
|
@@ -770,10 +798,10 @@ module Dynamoid
|
|
770
798
|
key_schema = aws_key_schema(index.hash_key_schema, index.range_key_schema)
|
771
799
|
|
772
800
|
hash = {
|
773
|
-
:
|
774
|
-
:
|
775
|
-
:
|
776
|
-
:
|
801
|
+
index_name: index.name,
|
802
|
+
key_schema: key_schema,
|
803
|
+
projection: {
|
804
|
+
projection_type: index.projection_type.to_s.upcase
|
777
805
|
}
|
778
806
|
}
|
779
807
|
|
@@ -785,8 +813,8 @@ module Dynamoid
|
|
785
813
|
# Only global secondary indexes have a separate throughput.
|
786
814
|
if index.type == :global_secondary
|
787
815
|
hash[:provisioned_throughput] = {
|
788
|
-
:
|
789
|
-
:
|
816
|
+
read_capacity_units: index.read_capacity,
|
817
|
+
write_capacity_units: index.write_capacity
|
790
818
|
}
|
791
819
|
end
|
792
820
|
hash
|
@@ -856,7 +884,6 @@ module Dynamoid
|
|
856
884
|
attribute_definitions
|
857
885
|
end
|
858
886
|
|
859
|
-
|
860
887
|
# Builds an attribute definitions based on hash key and range key
|
861
888
|
# @params [Hash] hash_key_schema - eg: {:id => :string}
|
862
889
|
# @params [Hash] range_key_schema - eg: {:created_at => :datetime}
|
@@ -887,8 +914,8 @@ module Dynamoid
|
|
887
914
|
aws_type = api_type(dynamoid_type)
|
888
915
|
|
889
916
|
{
|
890
|
-
:
|
891
|
-
:
|
917
|
+
attribute_name: name.to_s,
|
918
|
+
attribute_type: aws_type
|
892
919
|
}
|
893
920
|
end
|
894
921
|
|
@@ -913,7 +940,7 @@ module Dynamoid
|
|
913
940
|
def range_type
|
914
941
|
range_type ||= schema[:attribute_definitions].find { |d|
|
915
942
|
d[:attribute_name] == range_key
|
916
|
-
}.try(:fetch
|
943
|
+
}.try(:fetch, :attribute_type, nil)
|
917
944
|
end
|
918
945
|
|
919
946
|
def hash_key
|
@@ -982,19 +1009,19 @@ module Dynamoid
|
|
982
1009
|
def to_h
|
983
1010
|
ret = {}
|
984
1011
|
|
985
|
-
@additions.each do |k,v|
|
1012
|
+
@additions.each do |k, v|
|
986
1013
|
ret[k.to_s] = {
|
987
1014
|
action: ADD,
|
988
1015
|
value: v
|
989
1016
|
}
|
990
1017
|
end
|
991
|
-
@deletions.each do |k,v|
|
1018
|
+
@deletions.each do |k, v|
|
992
1019
|
ret[k.to_s] = {
|
993
1020
|
action: DELETE,
|
994
1021
|
value: v
|
995
1022
|
}
|
996
1023
|
end
|
997
|
-
@updates.each do |k,v|
|
1024
|
+
@updates.each do |k, v|
|
998
1025
|
ret[k.to_s] = {
|
999
1026
|
action: PUT,
|
1000
1027
|
value: v
|
@@ -1004,9 +1031,9 @@ module Dynamoid
|
|
1004
1031
|
ret
|
1005
1032
|
end
|
1006
1033
|
|
1007
|
-
ADD =
|
1008
|
-
DELETE =
|
1009
|
-
PUT =
|
1034
|
+
ADD = 'ADD'.freeze
|
1035
|
+
DELETE = 'DELETE'.freeze
|
1036
|
+
PUT = 'PUT'.freeze
|
1010
1037
|
end
|
1011
1038
|
end
|
1012
1039
|
end
|