dynamoid 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +53 -0
  3. data/.rubocop_todo.yml +55 -0
  4. data/.travis.yml +5 -27
  5. data/Appraisals +17 -15
  6. data/CHANGELOG.md +26 -3
  7. data/Gemfile +4 -2
  8. data/README.md +95 -77
  9. data/Rakefile +17 -17
  10. data/Vagrantfile +5 -3
  11. data/dynamoid.gemspec +39 -45
  12. data/gemfiles/rails_4_2.gemfile +7 -5
  13. data/gemfiles/rails_5_0.gemfile +6 -4
  14. data/gemfiles/rails_5_1.gemfile +6 -4
  15. data/gemfiles/rails_5_2.gemfile +6 -4
  16. data/lib/dynamoid.rb +11 -4
  17. data/lib/dynamoid/adapter.rb +21 -27
  18. data/lib/dynamoid/adapter_plugin/{aws_sdk_v2.rb → aws_sdk_v3.rb} +118 -113
  19. data/lib/dynamoid/application_time_zone.rb +27 -0
  20. data/lib/dynamoid/associations.rb +3 -6
  21. data/lib/dynamoid/associations/association.rb +3 -6
  22. data/lib/dynamoid/associations/belongs_to.rb +4 -5
  23. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -3
  24. data/lib/dynamoid/associations/has_many.rb +2 -3
  25. data/lib/dynamoid/associations/has_one.rb +2 -3
  26. data/lib/dynamoid/associations/many_association.rb +8 -9
  27. data/lib/dynamoid/associations/single_association.rb +3 -3
  28. data/lib/dynamoid/components.rb +2 -2
  29. data/lib/dynamoid/config.rb +9 -5
  30. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +4 -2
  31. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +3 -1
  32. data/lib/dynamoid/config/options.rb +4 -4
  33. data/lib/dynamoid/criteria.rb +3 -5
  34. data/lib/dynamoid/criteria/chain.rb +42 -49
  35. data/lib/dynamoid/dirty.rb +5 -4
  36. data/lib/dynamoid/document.rb +142 -36
  37. data/lib/dynamoid/dumping.rb +167 -0
  38. data/lib/dynamoid/dynamodb_time_zone.rb +16 -0
  39. data/lib/dynamoid/errors.rb +7 -6
  40. data/lib/dynamoid/fields.rb +24 -23
  41. data/lib/dynamoid/finders.rb +101 -59
  42. data/lib/dynamoid/identity_map.rb +5 -11
  43. data/lib/dynamoid/indexes.rb +45 -46
  44. data/lib/dynamoid/middleware/identity_map.rb +2 -0
  45. data/lib/dynamoid/persistence.rb +67 -307
  46. data/lib/dynamoid/primary_key_type_mapping.rb +34 -0
  47. data/lib/dynamoid/railtie.rb +3 -1
  48. data/lib/dynamoid/tasks/database.rake +11 -11
  49. data/lib/dynamoid/tasks/database.rb +4 -3
  50. data/lib/dynamoid/type_casting.rb +193 -0
  51. data/lib/dynamoid/undumping.rb +188 -0
  52. data/lib/dynamoid/validations.rb +4 -7
  53. data/lib/dynamoid/version.rb +3 -1
  54. metadata +59 -53
  55. data/gemfiles/rails_4_0.gemfile +0 -9
  56. data/gemfiles/rails_4_1.gemfile +0 -9
data/Rakefile CHANGED
@@ -1,32 +1,32 @@
1
- require "bundler/gem_tasks"
1
+ # frozen_string_literal: true
2
2
 
3
- require "bundler/setup"
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'bundler/setup'
4
6
  begin
5
7
  Bundler.setup(:default, :development)
6
8
  rescue Bundler::BundlerError => e
7
- $stderr.puts e.message
8
- $stderr.puts "Run `bundle install` to install missing gems"
9
+ warn e.message
10
+ warn 'Run `bundle install` to install missing gems'
9
11
  exit e.status_code
10
12
  end
11
- if defined?(Rails)
12
- load "./lib/dynamoid/tasks/database.rake"
13
- end
13
+ load './lib/dynamoid/tasks/database.rake' if defined?(Rails)
14
14
 
15
- require "rake"
16
- require "rspec/core/rake_task"
15
+ require 'rake'
16
+ require 'rspec/core/rake_task'
17
17
  RSpec::Core::RakeTask.new(:spec) do |spec|
18
- spec.pattern = FileList["spec/**/*_spec.rb"]
18
+ spec.pattern = FileList['spec/**/*_spec.rb']
19
19
  end
20
20
 
21
- require "yard"
21
+ require 'yard'
22
22
  YARD::Rake::YardocTask.new do |t|
23
- t.files = ["lib/**/*.rb", "README", "LICENSE"] # optional
24
- t.options = ["-m", "markdown"] # optional
23
+ t.files = ['lib/**/*.rb', 'README', 'LICENSE'] # optional
24
+ t.options = ['-m', 'markdown'] # optional
25
25
  end
26
26
 
27
- desc "Publish documentation to gh-pages"
27
+ desc 'Publish documentation to gh-pages'
28
28
  task :publish do
29
- Rake::Task["yard"].invoke
29
+ Rake::Task['yard'].invoke
30
30
  `git add .`
31
31
  `git commit -m 'Regenerated documentation'`
32
32
  `git checkout gh-pages`
@@ -41,6 +41,6 @@ task :publish do
41
41
  `git checkout master`
42
42
  end
43
43
 
44
- require "wwtd/tasks"
44
+ require 'wwtd/tasks'
45
45
 
46
- task :default => :spec
46
+ task default: :spec
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Vagrant.configure('2') do |config|
2
4
  # Choose base box
3
5
  config.vm.box = 'bento/ubuntu-16.04'
@@ -16,11 +18,11 @@ Vagrant.configure('2') do |config|
16
18
  salt.minion_config = '.dev/vagrant/minion'
17
19
 
18
20
  # Pillars
19
- salt.pillar({
21
+ salt.pillar(
20
22
  'ruby' => {
21
- 'version' => '2.4.1',
23
+ 'version' => '2.4.1'
22
24
  }
23
- })
25
+ )
24
26
 
25
27
  salt.run_highstate = true
26
28
  end
@@ -1,62 +1,56 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "dynamoid/version"
5
+ require 'dynamoid/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "dynamoid"
8
+ spec.name = 'dynamoid'
8
9
  spec.version = Dynamoid::VERSION
9
10
 
10
11
  # Keep in sync with README
11
12
  spec.authors = [
12
- "Josh Symonds",
13
- "Logan Bowers",
14
- "Craig Heneveld",
15
- "Anatha Kumaran",
16
- "Jason Dew",
17
- "Luis Arias",
18
- "Stefan Neculai",
19
- "Philip White",
20
- "Peeyush Kumar",
21
- "Sumanth Ravipati",
22
- "Pascal Corpet",
23
- "Brian Glusman",
24
- "Peter Boling",
25
- "Andrew Konchin"
13
+ 'Josh Symonds',
14
+ 'Logan Bowers',
15
+ 'Craig Heneveld',
16
+ 'Anatha Kumaran',
17
+ 'Jason Dew',
18
+ 'Luis Arias',
19
+ 'Stefan Neculai',
20
+ 'Philip White',
21
+ 'Peeyush Kumar',
22
+ 'Sumanth Ravipati',
23
+ 'Pascal Corpet',
24
+ 'Brian Glusman',
25
+ 'Peter Boling',
26
+ 'Andrew Konchin'
26
27
  ]
27
- spec.email = ["peter.boling@gmail.com", "brian@stellaservice.com"]
28
+ spec.email = ['peter.boling@gmail.com', 'brian@stellaservice.com']
28
29
 
29
30
  spec.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
30
31
  spec.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
31
32
  spec.extra_rdoc_files = [
32
- "LICENSE.txt",
33
- "README.md"
33
+ 'LICENSE.txt',
34
+ 'README.md'
34
35
  ]
35
36
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features|.dev|Vagrantfile)/}) }
36
- spec.homepage = "http://github.com/Dynamoid/Dynamoid"
37
- spec.licenses = ["MIT"]
38
- spec.bindir = "exe"
37
+ spec.homepage = 'http://github.com/Dynamoid/Dynamoid'
38
+ spec.licenses = ['MIT']
39
+ spec.bindir = 'exe'
39
40
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
40
- spec.require_paths = ["lib"]
41
+ spec.require_paths = ['lib']
42
+
43
+ spec.add_runtime_dependency 'activemodel', '>=4'
44
+ spec.add_runtime_dependency 'aws-sdk-dynamodb', '~> 1'
45
+ spec.add_runtime_dependency 'concurrent-ruby', '>= 1.0'
41
46
 
42
- # This form of switching the gem dependencies is not compatible with wwtd & appraisal
43
- # if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.2.2")
44
- # spec.add_runtime_dependency(%q<activemodel>, [">= 4", "< 5.1.0"])
45
- # spec.add_development_dependency(%q<activesupport>, [">= 4", "< 5.1.0"])
46
- # else
47
- # spec.add_runtime_dependency(%q<activemodel>, ["~> 4"])
48
- # spec.add_development_dependency(%q<activesupport>, ["~> 4"])
49
- # end
50
- spec.add_runtime_dependency(%q<activemodel>, [">= 4"])
51
- spec.add_development_dependency(%q<activesupport>, [">= 4"])
52
- spec.add_runtime_dependency(%q<aws-sdk-resources>, ["~> 2"])
53
- spec.add_runtime_dependency(%q<concurrent-ruby>, [">= 1.0"])
54
- spec.add_development_dependency "pry"
55
- spec.add_development_dependency "bundler", "~> 1.14"
56
- spec.add_development_dependency "rake", "~> 12.0"
57
- spec.add_development_dependency "rspec", "~> 3.0"
58
- spec.add_development_dependency "appraisal", "~> 2.1"
59
- spec.add_development_dependency "wwtd", "~> 1.3"
60
- spec.add_development_dependency(%q<yard>, [">= 0"])
61
- spec.add_development_dependency "coveralls", "~> 0.8"
47
+ spec.add_development_dependency 'appraisal'
48
+ spec.add_development_dependency 'bundler'
49
+ spec.add_development_dependency 'coveralls'
50
+ spec.add_development_dependency 'pry'
51
+ spec.add_development_dependency 'rake'
52
+ spec.add_development_dependency 'rspec'
53
+ spec.add_development_dependency 'rubocop'
54
+ spec.add_development_dependency 'wwtd'
55
+ spec.add_development_dependency 'yard'
62
56
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "pry-byebug", platforms: :ruby
6
- gem "rails", "~> 4.2.0"
7
- gem "nokogiri", "~> 1.6.8"
7
+ gem 'nokogiri', '~> 1.6.8'
8
+ gem 'pry-byebug', platforms: :ruby
9
+ gem 'rails', '~> 4.2.0'
8
10
 
9
- gemspec path: "../"
11
+ gemspec path: '../'
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "pry-byebug", platforms: :ruby
6
- gem "rails", "~> 5.0.0"
7
+ gem 'pry-byebug', platforms: :ruby
8
+ gem 'rails', '~> 5.0.0'
7
9
 
8
- gemspec path: "../"
10
+ gemspec path: '../'
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "pry-byebug", platforms: :ruby
6
- gem "rails", "~> 5.1.0"
7
+ gem 'pry-byebug', platforms: :ruby
8
+ gem 'rails', '~> 5.1.0'
7
9
 
8
- gemspec path: "../"
10
+ gemspec path: '../'
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "pry-byebug", platforms: :ruby
6
- gem "rails", "~> 5.2.0"
7
+ gem 'pry-byebug', platforms: :ruby
8
+ gem 'rails', '~> 5.2.0'
7
9
 
8
- gemspec path: "../"
10
+ gemspec path: '../'
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sdk-dynamodb'
1
4
  require 'delegate'
2
5
  require 'time'
3
6
  require 'securerandom'
@@ -11,10 +14,16 @@ require 'active_model'
11
14
 
12
15
  require 'dynamoid/version'
13
16
  require 'dynamoid/errors'
17
+ require 'dynamoid/application_time_zone'
18
+ require 'dynamoid/dynamodb_time_zone'
14
19
  require 'dynamoid/fields'
15
20
  require 'dynamoid/indexes'
16
21
  require 'dynamoid/associations'
17
22
  require 'dynamoid/persistence'
23
+ require 'dynamoid/dumping'
24
+ require 'dynamoid/undumping'
25
+ require 'dynamoid/type_casting'
26
+ require 'dynamoid/primary_key_type_mapping'
18
27
  require 'dynamoid/dirty'
19
28
  require 'dynamoid/validations'
20
29
  require 'dynamoid/criteria'
@@ -29,9 +38,7 @@ require 'dynamoid/tasks/database'
29
38
 
30
39
  require 'dynamoid/middleware/identity_map'
31
40
 
32
- if defined?(Rails)
33
- require 'dynamoid/railtie'
34
- end
41
+ require 'dynamoid/railtie' if defined?(Rails)
35
42
 
36
43
  module Dynamoid
37
44
  extend self
@@ -39,7 +46,7 @@ module Dynamoid
39
46
  def configure
40
47
  block_given? ? yield(Dynamoid::Config) : Dynamoid::Config
41
48
  end
42
- alias :config :configure
49
+ alias config configure
43
50
 
44
51
  def logger
45
52
  Dynamoid::Config.logger
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # require only 'concurrent/atom' once this issue is resolved:
2
4
  # https://github.com/ruby-concurrency/concurrent-ruby/pull/377
3
5
  require 'concurrent'
4
6
 
5
7
  # encoding: utf-8
6
8
  module Dynamoid
7
-
8
9
  # Adapter's value-add:
9
10
  # 1) For the rest of Dynamoid, the gateway to DynamoDB.
10
11
  # 2) Allows switching `config.adapter` to ease development of a new adapter.
@@ -16,8 +17,8 @@ module Dynamoid
16
17
  end
17
18
 
18
19
  def tables
19
- if !@tables_.value
20
- @tables_.swap{|value, args| benchmark('Cache Tables') { list_tables || [] } }
20
+ unless @tables_.value
21
+ @tables_.swap { |_value, _args| benchmark('Cache Tables') { list_tables || [] } }
21
22
  end
22
23
  @tables_.value
23
24
  end
@@ -26,7 +27,7 @@ module Dynamoid
26
27
  #
27
28
  # @since 0.2.0
28
29
  def adapter
29
- if !@adapter_.value
30
+ unless @adapter_.value
30
31
  adapter = self.class.adapter_plugin_class.new
31
32
  adapter.connect! if adapter.respond_to?(:connect!)
32
33
  @adapter_.compare_and_set(nil, adapter)
@@ -36,7 +37,7 @@ module Dynamoid
36
37
  end
37
38
 
38
39
  def clear_cache!
39
- @tables_.swap{|value, args| nil}
40
+ @tables_.swap { |_value, _args| nil }
40
41
  end
41
42
 
42
43
  # Shows how long it takes a method to run on the adapter. Useful for generating logged output.
@@ -51,8 +52,8 @@ module Dynamoid
51
52
  def benchmark(method, *args)
52
53
  start = Time.now
53
54
  result = yield
54
- 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? }"
55
- return result
55
+ 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?}"
56
+ result
56
57
  end
57
58
 
58
59
  # Write an object to the adapter.
@@ -81,13 +82,9 @@ module Dynamoid
81
82
  #
82
83
  # @since 0.2.0
83
84
  def read(table, ids, options = {}, &blk)
84
- range_key = options.delete(:range_key)
85
-
86
85
  if ids.respond_to?(:each)
87
- ids = ids.collect{|id| range_key ? [id, range_key] : id}
88
- batch_get_item({table => ids}, options, &blk)
86
+ batch_get_item({ table => ids }, options, &blk)
89
87
  else
90
- options[:range_key] = range_key if range_key
91
88
  get_item(table, ids, options)
92
89
  end
93
90
  end
@@ -101,12 +98,12 @@ module Dynamoid
101
98
  def delete(table, ids, options = {})
102
99
  range_key = options[:range_key] # array of range keys that matches the ids passed in
103
100
  if ids.respond_to?(:each)
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]]}
107
- else
108
- ids = range_key ? ids.map { |id| [id, range_key] } : ids
109
- end
101
+ ids = if range_key.respond_to?(:each)
102
+ # turn ids into array of arrays each element being hash_key, range_key
103
+ ids.each_with_index.map { |id, i| [id, range_key[i]] }
104
+ else
105
+ range_key ? ids.map { |id| [id, range_key] } : ids
106
+ end
110
107
 
111
108
  batch_delete_item(table => ids)
112
109
  else
@@ -121,11 +118,11 @@ module Dynamoid
121
118
  #
122
119
  # @since 0.2.0
123
120
  def scan(table, query = {}, opts = {})
124
- benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
121
+ benchmark('Scan', table, query) { adapter.scan(table, query, opts) }
125
122
  end
126
123
 
127
124
  def create_table(table_name, key, options = {})
128
- if !tables.include?(table_name)
125
+ unless tables.include?(table_name)
129
126
  benchmark('Create Table') { adapter.create_table(table_name, key, options) }
130
127
  tables << table_name
131
128
  end
@@ -140,15 +137,15 @@ module Dynamoid
140
137
  end
141
138
  end
142
139
 
143
- [:batch_get_item, :delete_item, :get_item, :list_tables, :put_item, :truncate, :batch_write_item, :batch_delete_item].each do |m|
140
+ %i[batch_get_item delete_item get_item list_tables put_item truncate batch_write_item batch_delete_item].each do |m|
144
141
  # Method delegation with benchmark to the underlying adapter. Faster than relying on method_missing.
145
142
  #
146
143
  # @since 0.2.0
147
144
  define_method(m) do |*args, &blk|
148
145
  if blk.present?
149
- benchmark("#{m.to_s}", *args) { adapter.send(m, *args, &blk) }
146
+ benchmark(m.to_s, *args) { adapter.send(m, *args, &blk) }
150
147
  else
151
- benchmark("#{m.to_s}", *args) { adapter.send(m, *args) }
148
+ benchmark(m.to_s, *args) { adapter.send(m, *args) }
152
149
  end
153
150
  end
154
151
  end
@@ -157,7 +154,7 @@ module Dynamoid
157
154
  #
158
155
  # @since 0.2.0
159
156
  def method_missing(method, *args, &block)
160
- return benchmark(method, *args) {adapter.send(method, *args, &block)} if adapter.respond_to?(method)
157
+ return benchmark(method, *args) { adapter.send(method, *args, &block) } if adapter.respond_to?(method)
161
158
  super
162
159
  end
163
160
 
@@ -180,8 +177,6 @@ module Dynamoid
180
177
  adapter.query(table_name, opts)
181
178
  end
182
179
 
183
- private
184
-
185
180
  def self.adapter_plugin_class
186
181
  unless Dynamoid.const_defined?(:AdapterPlugin) && Dynamoid::AdapterPlugin.const_defined?(Dynamoid::Config.adapter.camelcase)
187
182
  require "dynamoid/adapter_plugin/#{Dynamoid::Config.adapter}"
@@ -189,6 +184,5 @@ module Dynamoid
189
184
 
190
185
  Dynamoid::AdapterPlugin.const_get(Dynamoid::Config.adapter.camelcase)
191
186
  end
192
-
193
187
  end
194
188
  end
@@ -1,46 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dynamoid
2
4
  module AdapterPlugin
3
-
4
- # The AwsSdkV2 adapter provides support for the aws-sdk version 2 for ruby.
5
- class AwsSdkV2
6
- EQ = 'EQ'.freeze
5
+ # The AwsSdkV3 adapter provides support for the aws-sdk version 2 for ruby.
6
+ class AwsSdkV3
7
+ EQ = 'EQ'
7
8
  RANGE_MAP = {
8
- range_greater_than: 'GT',
9
- range_less_than: 'LT',
10
- range_gte: 'GE',
11
- range_lte: 'LE',
12
- range_begins_with: 'BEGINS_WITH',
13
- range_between: 'BETWEEN',
14
- range_eq: 'EQ'
15
- }
9
+ range_greater_than: 'GT',
10
+ range_less_than: 'LT',
11
+ range_gte: 'GE',
12
+ range_lte: 'LE',
13
+ range_begins_with: 'BEGINS_WITH',
14
+ range_between: 'BETWEEN',
15
+ range_eq: 'EQ'
16
+ }.freeze
16
17
 
17
18
  # Don't implement NULL and NOT_NULL because it doesn't make seanse -
18
19
  # we declare schema in models
19
20
  FIELD_MAP = {
20
- eq: 'EQ',
21
- ne: 'NE',
22
- gt: 'GT',
23
- lt: 'LT',
24
- gte: 'GE',
25
- lte: 'LE',
26
- begins_with: 'BEGINS_WITH',
27
- between: 'BETWEEN',
28
- in: 'IN',
29
- contains: 'CONTAINS',
30
- not_contains: 'NOT_CONTAINS'
31
- }
32
- HASH_KEY = 'HASH'.freeze
33
- RANGE_KEY = 'RANGE'.freeze
34
- STRING_TYPE = 'S'.freeze
35
- NUM_TYPE = 'N'.freeze
36
- BINARY_TYPE = 'B'.freeze
21
+ eq: 'EQ',
22
+ ne: 'NE',
23
+ gt: 'GT',
24
+ lt: 'LT',
25
+ gte: 'GE',
26
+ lte: 'LE',
27
+ begins_with: 'BEGINS_WITH',
28
+ between: 'BETWEEN',
29
+ in: 'IN',
30
+ contains: 'CONTAINS',
31
+ not_contains: 'NOT_CONTAINS'
32
+ }.freeze
33
+ HASH_KEY = 'HASH'
34
+ RANGE_KEY = 'RANGE'
35
+ STRING_TYPE = 'S'
36
+ NUM_TYPE = 'N'
37
+ BINARY_TYPE = 'B'
37
38
  TABLE_STATUSES = {
38
- creating: 'CREATING',
39
- updating: 'UPDATING',
40
- deleting: 'DELETING',
41
- active: 'ACTIVE'
39
+ creating: 'CREATING',
40
+ updating: 'UPDATING',
41
+ deleting: 'DELETING',
42
+ active: 'ACTIVE'
42
43
  }.freeze
43
- PARSE_TABLE_STATUS = ->(resp, lookup = :table) {
44
+ PARSE_TABLE_STATUS = lambda { |resp, lookup = :table|
44
45
  # lookup is table for describe_table API
45
46
  # lookup is table_description for create_table API
46
47
  # because Amazon, damnit.
@@ -74,6 +75,13 @@ module Dynamoid
74
75
  @connection_hash[:region] = Dynamoid::Config.region
75
76
  end
76
77
 
78
+ # https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-core/lib/aws-sdk-core/plugins/logging.rb
79
+ # https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-core/lib/aws-sdk-core/log/formatter.rb
80
+ formatter = Aws::Log::Formatter.new(':operation | Request :http_request_body | Response :http_response_body')
81
+ @connection_hash[:logger] = Dynamoid::Config.logger
82
+ @connection_hash[:log_level] = :debug
83
+ @connection_hash[:log_formatter] = formatter
84
+
77
85
  @connection_hash
78
86
  end
79
87
 
@@ -90,10 +98,10 @@ module Dynamoid
90
98
  # Block receives boolean flag which is true if there are some unprocessed items, otherwise false.
91
99
  #
92
100
  # @example Saves several items to the table testtable
93
- # Dynamoid::AdapterPlugin::AwsSdkV2.batch_write_item('table1', [{ id: '1', name: 'a' }, { id: '2', name: 'b'}])
101
+ # Dynamoid::AdapterPlugin::AwsSdkV3.batch_write_item('table1', [{ id: '1', name: 'a' }, { id: '2', name: 'b'}])
94
102
  #
95
103
  # @example Pass block
96
- # Dynamoid::AdapterPlugin::AwsSdkV2.batch_write_item('table1', items) do |bool|
104
+ # Dynamoid::AdapterPlugin::AwsSdkV3.batch_write_item('table1', items) do |bool|
97
105
  # if bool
98
106
  # puts 'there are unprocessed items'
99
107
  # end
@@ -107,27 +115,25 @@ module Dynamoid
107
115
  # See:
108
116
  # * http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
109
117
  # * http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method
110
- def batch_write_item table_name, objects, options = {}
118
+ def batch_write_item(table_name, objects, options = {})
111
119
  items = objects.map { |o| sanitize_item(o) }
112
120
 
113
121
  begin
114
- while items.present? do
122
+ while items.present?
115
123
  batch = items.shift(BATCH_WRITE_ITEM_REQUESTS_LIMIT)
116
124
  requests = batch.map { |item| { put_request: { item: item } } }
117
125
 
118
126
  response = client.batch_write_item(
119
127
  {
120
128
  request_items: {
121
- table_name => requests,
129
+ table_name => requests
122
130
  },
123
131
  return_consumed_capacity: 'TOTAL',
124
132
  return_item_collection_metrics: 'SIZE'
125
133
  }.merge!(options)
126
134
  )
127
135
 
128
- if block_given?
129
- yield(response.unprocessed_items.present?)
130
- end
136
+ yield(response.unprocessed_items.present?) if block_given?
131
137
 
132
138
  if response.unprocessed_items.present?
133
139
  items += response.unprocessed_items[table_name].map { |r| r.put_request.item }
@@ -148,10 +154,10 @@ module Dynamoid
148
154
  # * and boolean flag is true if there are some unprocessed keys, otherwise false.
149
155
  #
150
156
  # @example Retrieve IDs 1 and 2 from the table testtable
151
- # Dynamoid::AdapterPlugin::AwsSdkV2.batch_get_item('table1' => ['1', '2'])
157
+ # Dynamoid::AdapterPlugin::AwsSdkV3.batch_get_item('table1' => ['1', '2'])
152
158
  #
153
159
  # @example Pass block to receive each batch
154
- # Dynamoid::AdapterPlugin::AwsSdkV2.batch_get_item('table1' => ids) do |hash, bool|
160
+ # Dynamoid::AdapterPlugin::AwsSdkV3.batch_get_item('table1' => ids) do |hash, bool|
155
161
  # puts hash['table1']
156
162
  #
157
163
  # if bool
@@ -171,9 +177,9 @@ module Dynamoid
171
177
  # @since 1.0.0
172
178
  #
173
179
  # @todo: Provide support for passing options to underlying batch_get_item
174
- def batch_get_item(table_ids, options = {})
175
- request_items = Hash.new{|h, k| h[k] = []}
176
- return request_items if table_ids.all?{|k, v| v.blank?}
180
+ def batch_get_item(table_ids, _options = {})
181
+ request_items = Hash.new { |h, k| h[k] = [] }
182
+ return request_items if table_ids.all? { |_k, v| v.blank? }
177
183
 
178
184
  ret = Hash.new([].freeze) # Default for tables where no rows are returned
179
185
 
@@ -184,20 +190,20 @@ module Dynamoid
184
190
  hk = tbl.hash_key.to_s
185
191
  rng = tbl.range_key.to_s
186
192
 
187
- while ids.present? do
193
+ while ids.present?
188
194
  batch = ids.shift(Dynamoid::Config.batch_size)
189
195
 
190
- request_items = Hash.new{|h, k| h[k] = []}
196
+ request_items = Hash.new { |h, k| h[k] = [] }
191
197
 
192
198
  keys = if rng.present?
193
- Array(batch).map do |h, r|
194
- { hk => h, rng => r }
195
- end
196
- else
197
- Array(batch).map do |id|
198
- { hk => id }
199
- end
200
- end
199
+ Array(batch).map do |h, r|
200
+ { hk => h, rng => r }
201
+ end
202
+ else
203
+ Array(batch).map do |id|
204
+ { hk => id }
205
+ end
206
+ end
201
207
 
202
208
  request_items[t] = {
203
209
  keys: keys
@@ -207,11 +213,7 @@ module Dynamoid
207
213
  request_items: request_items
208
214
  )
209
215
 
210
- unless block_given?
211
- results.data[:responses].each do |table, rows|
212
- ret[table] += rows.collect { |r| result_item_to_hash(r) }
213
- end
214
- else
216
+ if block_given?
215
217
  batch_results = Hash.new([].freeze)
216
218
 
217
219
  results.data[:responses].each do |table, rows|
@@ -219,6 +221,10 @@ module Dynamoid
219
221
  end
220
222
 
221
223
  yield(batch_results, results.unprocessed_keys.present?)
224
+ else
225
+ results.data[:responses].each do |table, rows|
226
+ ret[table] += rows.collect { |r| result_item_to_hash(r) }
227
+ end
222
228
  end
223
229
 
224
230
  if results.unprocessed_keys.present?
@@ -227,9 +233,7 @@ module Dynamoid
227
233
  end
228
234
  end
229
235
 
230
- unless block_given?
231
- ret
232
- end
236
+ ret unless block_given?
233
237
  end
234
238
 
235
239
  # Delete many items at once from DynamoDB. More efficient than delete each item individually.
@@ -237,7 +241,7 @@ module Dynamoid
237
241
  # @example Delete IDs 1 and 2 from the table testtable
238
242
  # Dynamoid::AdapterPlugin::AwsSdk.batch_delete_item('table1' => ['1', '2'])
239
243
  # or
240
- # Dynamoid::AdapterPlugin::AwsSdkV2.batch_delete_item('table1' => [['hk1', 'rk2'], ['hk1', 'rk2']]]))
244
+ # Dynamoid::AdapterPlugin::AwsSdkV3.batch_delete_item('table1' => [['hk1', 'rk2'], ['hk1', 'rk2']]]))
241
245
  #
242
246
  # @param [Hash] options the hash of tables and IDs to delete
243
247
  #
@@ -253,11 +257,11 @@ module Dynamoid
253
257
  table = describe_table(table_name)
254
258
 
255
259
  ids.each_slice(BATCH_WRITE_ITEM_REQUESTS_LIMIT) do |sliced_ids|
256
- delete_requests = sliced_ids.map { |id|
257
- {delete_request: {key: key_stanza(table, *id)}}
258
- }
260
+ delete_requests = sliced_ids.map do |id|
261
+ { delete_request: { key: key_stanza(table, *id) } }
262
+ end
259
263
 
260
- requests << {table_name => delete_requests}
264
+ requests << { table_name => delete_requests }
261
265
  end
262
266
  end
263
267
 
@@ -266,7 +270,8 @@ module Dynamoid
266
270
  client.batch_write_item(
267
271
  request_items: request_items,
268
272
  return_consumed_capacity: 'TOTAL',
269
- return_item_collection_metrics: 'SIZE')
273
+ return_item_collection_metrics: 'SIZE'
274
+ )
270
275
  end
271
276
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
272
277
  raise Dynamoid::Errors::ConditionalCheckFailedException, e
@@ -330,10 +335,10 @@ module Dynamoid
330
335
  end
331
336
  end
332
337
  resp = client.create_table(client_opts)
333
- options[:sync] = true if !options.has_key?(:sync) && ls_indexes.present? || gs_indexes.present?
338
+ options[:sync] = true if !options.key?(:sync) && ls_indexes.present? || gs_indexes.present?
334
339
  until_past_table_status(table_name, :creating) if options[:sync] &&
335
- (status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
336
- status == TABLE_STATUSES[:creating]
340
+ (status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
341
+ status == TABLE_STATUSES[:creating]
337
342
  # Response to original create_table, which, if options[:sync]
338
343
  # may have an outdated table_description.table_status of "CREATING"
339
344
  resp
@@ -395,8 +400,8 @@ module Dynamoid
395
400
  def delete_table(table_name, options = {})
396
401
  resp = client.delete_table(table_name: table_name)
397
402
  until_past_table_status(table_name, :deleting) if options[:sync] &&
398
- (status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
399
- status == TABLE_STATUSES[:deleting]
403
+ (status = PARSE_TABLE_STATUS.call(resp, :table_description)) &&
404
+ status == TABLE_STATUSES[:deleting]
400
405
  table_cache.delete(table_name)
401
406
  rescue Aws::DynamoDB::Errors::ResourceInUseException => e
402
407
  Dynamoid.logger.error "Table #{table_name} cannot be deleted as it is in use"
@@ -422,12 +427,11 @@ module Dynamoid
422
427
  # @todo Provide support for various options http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#get_item-instance_method
423
428
  def get_item(table_name, key, options = {})
424
429
  options ||= {}
425
- table = describe_table(table_name)
430
+ table = describe_table(table_name)
426
431
  range_key = options.delete(:range_key)
427
432
 
428
433
  item = client.get_item(table_name: table_name,
429
- key: key_stanza(table, key, range_key)
430
- )[:item]
434
+ key: key_stanza(table, key, range_key))[:item]
431
435
  item ? result_item_to_hash(item) : nil
432
436
  end
433
437
 
@@ -450,11 +454,10 @@ module Dynamoid
450
454
  raise "non-empty options: #{options}" unless options.empty?
451
455
  begin
452
456
  result = client.update_item(table_name: table_name,
453
- key: key_stanza(table, key, range_key),
454
- attribute_updates: iu.to_h,
455
- expected: expected_stanza(conditions),
456
- return_values: 'ALL_NEW'
457
- )
457
+ key: key_stanza(table, key, range_key),
458
+ attribute_updates: iu.to_h,
459
+ expected: expected_stanza(conditions),
460
+ return_values: 'ALL_NEW')
458
461
  result_item_to_hash(result[:attributes])
459
462
  rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
460
463
  raise Dynamoid::Errors::ConditionalCheckFailedException, e
@@ -518,11 +521,11 @@ module Dynamoid
518
521
  hk = (opts[:hash_key].present? ? opts.delete(:hash_key) : table.hash_key).to_s
519
522
  rng = (opts[:range_key].present? ? opts.delete(:range_key) : table.range_key).to_s
520
523
  q = opts.slice(
521
- :consistent_read,
522
- :scan_index_forward,
523
- :select,
524
- :index_name
525
- )
524
+ :consistent_read,
525
+ :scan_index_forward,
526
+ :select,
527
+ :index_name
528
+ )
526
529
 
527
530
  opts.delete(:consistent_read)
528
531
  opts.delete(:scan_index_forward)
@@ -534,7 +537,7 @@ module Dynamoid
534
537
  scan_limit = opts.delete(:scan_limit)
535
538
  batch_size = opts.delete(:batch_size)
536
539
  exclusive_start_key = opts.delete(:exclusive_start_key)
537
- limit = [record_limit, scan_limit, batch_size].compact.min
540
+ limit = [record_limit, scan_limit, batch_size].compact.min
538
541
 
539
542
  key_conditions = {
540
543
  hk => {
@@ -543,8 +546,8 @@ module Dynamoid
543
546
  }
544
547
  }
545
548
 
546
- opts.each_pair do |k, v|
547
- next unless(op = RANGE_MAP[k])
549
+ opts.each_pair do |k, _v|
550
+ next unless (op = RANGE_MAP[k])
548
551
  key_conditions[rng] = {
549
552
  comparison_operator: op,
550
553
  attribute_value_list: attribute_value_list(op, opts.delete(k).freeze)
@@ -552,7 +555,7 @@ module Dynamoid
552
555
  end
553
556
 
554
557
  query_filter = {}
555
- opts.reject {|k, _| k.in? RANGE_MAP.keys}.each do |attr, hash|
558
+ opts.reject { |k, _| k.in? RANGE_MAP.keys }.each do |attr, hash|
556
559
  query_filter[attr] = {
557
560
  comparison_operator: FIELD_MAP[hash.keys[0]],
558
561
  attribute_value_list: attribute_value_list(FIELD_MAP[hash.keys[0]], hash.values[0].freeze)
@@ -565,7 +568,7 @@ module Dynamoid
565
568
  q[:key_conditions] = key_conditions
566
569
  q[:query_filter] = query_filter
567
570
 
568
- Enumerator.new { |y|
571
+ Enumerator.new do |y|
569
572
  record_count = 0
570
573
  scan_count = 0
571
574
  loop do
@@ -600,13 +603,13 @@ module Dynamoid
600
603
  scan_count += results.scanned_count
601
604
  break if scan_limit && scan_count >= scan_limit
602
605
 
603
- if(lk = results.last_evaluated_key)
606
+ if (lk = results.last_evaluated_key)
604
607
  q[:exclusive_start_key] = lk
605
608
  else
606
609
  break
607
610
  end
608
611
  end
609
- }
612
+ end
610
613
  end
611
614
 
612
615
  # Scan the DynamoDB table. This is usually a very slow operation as it naively filters all data on
@@ -632,13 +635,13 @@ module Dynamoid
632
635
  request_limit = [record_limit, scan_limit, batch_size].compact.min
633
636
  request[:limit] = request_limit if request_limit
634
637
  request[:exclusive_start_key] = exclusive_start_key if exclusive_start_key
635
-
638
+
636
639
  if scan_hash.present?
637
640
  request[:scan_filter] = scan_hash.reduce({}) do |memo, (attr, cond)|
638
641
  memo.merge(attr.to_s => {
639
- comparison_operator: FIELD_MAP[cond.keys[0]],
640
- attribute_value_list: attribute_value_list(FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
641
- })
642
+ comparison_operator: FIELD_MAP[cond.keys[0]],
643
+ attribute_value_list: attribute_value_list(FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
644
+ })
642
645
  end
643
646
  end
644
647
 
@@ -678,7 +681,7 @@ module Dynamoid
678
681
  break if scan_limit && scan_count >= scan_limit
679
682
 
680
683
  # Keep pulling if we haven't finished paging in all data
681
- if(lk = results[:last_evaluated_key])
684
+ if (lk = results[:last_evaluated_key])
682
685
  request[:exclusive_start_key] = lk
683
686
  else
684
687
  break
@@ -715,14 +718,14 @@ module Dynamoid
715
718
  status = PARSE_TABLE_STATUS.call(resp)
716
719
  again = counter < Dynamoid::Config.sync_retry_max_times &&
717
720
  status == TABLE_STATUSES[expect_status]
718
- {again: again, status: status, counter: counter}
721
+ { again: again, status: status, counter: counter }
719
722
  end
720
723
 
721
724
  def until_past_table_status(table_name, status = :creating)
722
725
  counter = 0
723
726
  resp = nil
724
727
  begin
725
- check = {again: true}
728
+ check = { again: true }
726
729
  while check[:again]
727
730
  sleep Dynamoid::Config.sync_retry_wait_seconds
728
731
  resp = client.describe_table(table_name: table_name)
@@ -756,7 +759,7 @@ module Dynamoid
756
759
  # Converts from symbol to the API string for the given data type
757
760
  # E.g. :number -> 'N'
758
761
  def api_type(type)
759
- case(type)
762
+ case type
760
763
  when :string then STRING_TYPE
761
764
  when :number then NUM_TYPE
762
765
  when :binary then BINARY_TYPE
@@ -967,7 +970,7 @@ module Dynamoid
967
970
  def attribute_value_list(operator, value)
968
971
  # For BETWEEN and IN operators we should keep value as is (it should be already an array)
969
972
  # For all the other operators we wrap the value with array
970
- if ["BETWEEN", "IN"].include?(operator)
973
+ if %w[BETWEEN IN].include?(operator)
971
974
  [value].flatten
972
975
  else
973
976
  [value]
@@ -993,13 +996,13 @@ module Dynamoid
993
996
  end
994
997
 
995
998
  def range_type
996
- range_type ||= schema[:attribute_definitions].find { |d|
999
+ range_type ||= schema[:attribute_definitions].find do |d|
997
1000
  d[:attribute_name] == range_key
998
- }.try(:fetch, :attribute_type, nil)
1001
+ end.try(:fetch, :attribute_type, nil)
999
1002
  end
1000
1003
 
1001
1004
  def hash_key
1002
- @hash_key ||= schema[:key_schema].find { |d| d[:key_type] == HASH_KEY }.try(:attribute_name).to_sym
1005
+ @hash_key ||= schema[:key_schema].find { |d| d[:key_type] == HASH_KEY }.try(:attribute_name).to_sym
1003
1006
  end
1004
1007
 
1005
1008
  #
@@ -1024,7 +1027,9 @@ module Dynamoid
1024
1027
  attr_reader :table, :key, :range_key
1025
1028
 
1026
1029
  def initialize(table, key, range_key = nil)
1027
- @table = table; @key = key, @range_key = range_key
1030
+ @table = table
1031
+ @key = key
1032
+ @range_key = range_key
1028
1033
  @additions = {}
1029
1034
  @deletions = {}
1030
1035
  @updates = {}
@@ -1086,13 +1091,13 @@ module Dynamoid
1086
1091
  ret
1087
1092
  end
1088
1093
 
1089
- ADD = 'ADD'.freeze
1090
- DELETE = 'DELETE'.freeze
1091
- PUT = 'PUT'.freeze
1094
+ ADD = 'ADD'
1095
+ DELETE = 'DELETE'
1096
+ PUT = 'PUT'
1092
1097
  end
1093
1098
 
1094
1099
  def sanitize_item(attributes)
1095
- attributes.reject do |k, v|
1100
+ attributes.reject do |_k, v|
1096
1101
  v.nil? || ((v.is_a?(Set) || v.is_a?(String)) && v.empty?)
1097
1102
  end
1098
1103
  end