dynamodb_record 0.2.2 → 0.3.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +55 -0
  3. data/dynamodb_record.gemspec +1 -1
  4. data/lib/dynamodb_record/associations.rb +77 -27
  5. data/lib/dynamodb_record/collection.rb +18 -27
  6. data/lib/dynamodb_record/config.rb +2 -0
  7. data/lib/dynamodb_record/fields.rb +11 -12
  8. data/lib/dynamodb_record/finders.rb +1 -0
  9. data/lib/dynamodb_record/has_and_belongs_to_many_collection.rb +58 -0
  10. data/lib/dynamodb_record/has_many_collection.rb +50 -0
  11. data/lib/dynamodb_record/has_many_through_collection.rb +44 -0
  12. data/lib/dynamodb_record/pager.rb +15 -0
  13. data/lib/dynamodb_record/persistence.rb +19 -19
  14. data/lib/dynamodb_record/query.rb +12 -9
  15. data/lib/dynamodb_record/query_pager.rb +28 -0
  16. data/lib/dynamodb_record/scan_pager.rb +28 -0
  17. data/lib/dynamodb_record/version.rb +1 -1
  18. data/lib/dynamodb_record.rb +6 -0
  19. data/spec/app/models/authorization.rb +0 -1
  20. data/spec/app/models/car.rb +9 -0
  21. data/spec/app/models/insurance.rb +9 -0
  22. data/spec/app/models/service.rb +8 -0
  23. data/spec/app/models/user.rb +2 -0
  24. data/spec/dynamodb_record/association_spec.rb +44 -0
  25. data/spec/dynamodb_record/document_spec.rb +2 -2
  26. data/spec/dynamodb_record/fields_spec.rb +21 -15
  27. data/spec/dynamodb_record/persistence_spec.rb +3 -2
  28. data/spec/dynamodb_record/query_spec.rb +5 -3
  29. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/_belongs_to/get_object.yml +105 -0
  30. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/_has_many/create_item.yml +156 -0
  31. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/_has_many/get_collection.yml +105 -0
  32. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/has_and_belongs_to_many.yml +157 -0
  33. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/has_and_belongs_to_many_create.yml +207 -0
  34. data/spec/fixtures/vcr_cassettes/DynamodbRecord_Associations/has_many_through.yml +157 -0
  35. data/spec/spec_helper.rb +3 -2
  36. metadata +22 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0019712191d0f7ba069ba581bd04a59a5c9dd985d34bc5f7cb2865f08820ac4
4
- data.tar.gz: 02ebcbed904cfeca08deb41a315ea9a018068f1be9747a8e884b289e782a132e
3
+ metadata.gz: 101d9c7e2b70670eaf86a9d13ae561f402f11d18c3a2685f6b836d1baeebaecc
4
+ data.tar.gz: 3ecd123a7420781247260289b92446ac5fd357a20d7985a8a3a204eadaba27d0
5
5
  SHA512:
6
- metadata.gz: f6ea7a0acbdf7f5eaa2ebecd8aef15613fafe0337af8ab67d31b648572ed51cf0b08fd24b06dc137895ebd6f2d6e6789cf942b961e708d5e33c67f32a9bbb27e
7
- data.tar.gz: 8cbd27b3dc8a12546aadeeabff96d94a4b8e3a562dbef7f2d980acdf7dea7f73ef863a17c62e4b5e662e7661383f5da59d54dadaad556881d3fcdfcae744e8a5
6
+ metadata.gz: d918a3f79ddbd0a6aa31ce76c8c7063df46a768a2ef6f7689df4c1c59b4edf1d00ea94e4f6ea98922836c5c789d69d9e902b0e423df2050366d22be9ca2080a5
7
+ data.tar.gz: ca0cf61dc6dd22f24406e55903b8ad0f1a7d5fac55774ad0e9cc38a036d51029bc041d7ddc132773c7b8b53d14e41f6c8a9f66e2e0f91dc7ffba2186c86e4cfb
data/.rubocop.yml ADDED
@@ -0,0 +1,55 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.2
3
+ NewCops: enable
4
+ Exclude:
5
+ - 'spec/dummy/bin/*'
6
+ - vendor/**
7
+ - 'vendor/**/*'
8
+ - vendor/bundle/**/*
9
+ - bin/**
10
+ Layout/SpaceInsideHashLiteralBraces:
11
+ EnforcedStyle: no_space
12
+ Metrics/BlockLength:
13
+ Max: 36
14
+ Exclude:
15
+ - spec/**/*.rb
16
+ - fleteo.gemspec
17
+
18
+ Lint/ConstantDefinitionInBlock:
19
+ Exclude:
20
+ - spec/**/**/*.rb
21
+ Metrics/BlockNesting:
22
+ Max: 2
23
+ Metrics/MethodLength:
24
+ CountComments: false
25
+ Max: 15
26
+ Metrics/ModuleLength:
27
+ Max: 120
28
+ Metrics/ClassLength:
29
+ Max: 120
30
+ Metrics/ParameterLists:
31
+ Max: 5
32
+ CountKeywordArgs: true
33
+ Style/CollectionMethods:
34
+ Enabled: true
35
+ PreferredMethods:
36
+ collect: 'map'
37
+ collect!: 'map!'
38
+ inject: 'reduce'
39
+ find: 'detect'
40
+ find_all: 'select'
41
+ delete: 'gsub'
42
+ Layout/DotPosition:
43
+ EnforcedStyle: trailing
44
+
45
+ #Layout/LineLength:
46
+ # Max: 100
47
+
48
+ Style/TrailingCommaInArrayLiteral:
49
+ EnforcedStyleForMultiline: 'no_comma'
50
+
51
+ Style/TrailingCommaInHashLiteral:
52
+ EnforcedStyleForMultiline: 'no_comma'
53
+
54
+ Gemspec/DevelopmentDependencies:
55
+ EnforcedStyle: gemspec
@@ -16,7 +16,6 @@ Gem::Specification.new do |spec|
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0")
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
19
  spec.require_paths = ['lib']
21
20
 
22
21
  spec.add_dependency 'activesupport', '~> 7.1', '>= 7.1.3.2'
@@ -29,4 +28,5 @@ Gem::Specification.new do |spec|
29
28
  spec.add_development_dependency 'rubocop', '~> 1.62'
30
29
  spec.add_development_dependency 'vcr', '~> 6.2'
31
30
  spec.add_development_dependency 'webmock', '~> 3.23'
31
+ spec.metadata['rubygems_mfa_required'] = 'true'
32
32
  end
@@ -5,24 +5,66 @@ module DynamodbRecord
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  class_methods do
8
+ def belongs_to(association)
9
+ association = association.to_s
10
+
11
+ define_method(association) do
12
+ id = send("#{association}_id")
13
+ klass = Object.const_get(association.capitalize)
14
+ klass.find!(id)
15
+ end
16
+ end
17
+
18
+ # rubocop:disable Naming/PredicateName
8
19
  def has_many(associations)
9
- model = associations.to_s.upcase.chop
10
- define_method(associations) do
11
- # options = self.class.default_options
12
- field = self.class.to_s.downcase
13
- options = { table_name: associations }
14
- options.merge!(key_condition_expression: "##{field}_id = :#{field}_id")
15
- options.merge!(expression_attribute_names: { "##{field}_id": "#{field}_id" })
16
- options.merge!(expression_attribute_values: { ":#{field}_id" => id })
20
+ base_model = to_s.downcase
21
+ model = associations.to_s.chop
22
+ # field = self.class.to_s.downcase
23
+ table = Config.namespace ? "#{Config.namespace}-#{associations}" : associations
17
24
 
18
- options.merge!(index_name: "#{self.class.to_s.downcase}_id_index")
25
+ options = {table_name: table}
26
+ options.merge!(key_condition_expression: "##{base_model}_id = :#{base_model}_id")
27
+ options.merge!(expression_attribute_names: {"##{base_model}_id": "#{base_model}_id"})
28
+ options.merge!(index_name: "#{base_model}_id_index")
19
29
 
30
+ define_method(associations) do
31
+ options.merge!(expression_attribute_values: {":#{base_model}_id" => id})
20
32
  klass = Object.const_get(model.capitalize)
21
- # puts options
22
- response = self.class.client.query(options)
33
+ query = QueryPager.new(options, klass)
34
+ HasManyCollection.new(query, self)
35
+ end
36
+ end
23
37
 
24
- # pager = DynamodbRecord::Pager.new(self.class.client, options, clase)
25
- DynamodbRecord::Collection.new(response, klass, options)
38
+ def has_many_through(associations, table)
39
+ base_model = to_s.downcase
40
+ relation_model = associations.to_s.chop
41
+ list = []
42
+ list << base_model
43
+ list << relation_model
44
+ sorted_list = list.sort
45
+ options = {table_name: table.to_s}
46
+
47
+ field = if sorted_list.first == base_model
48
+ sorted_list.first
49
+ else
50
+ sorted_list.last
51
+ end
52
+ options.merge!(index_name: "#{field}_id_index")
53
+ options.merge!(key_condition_expression: "##{field}_id = :#{field}_id")
54
+ options.merge!(expression_attribute_names: {"##{field}_id": "#{field}_id"})
55
+
56
+ define_method(associations) do
57
+ options.merge!(expression_attribute_values: {":#{field}_id" => id})
58
+ # p options
59
+
60
+ klass = Object.const_get(relation_model.capitalize)
61
+
62
+ # options.merge!(limit: 1)
63
+ # p options
64
+
65
+ query = QueryPager.new(options, klass)
66
+
67
+ HasManyThroughCollection.new(query, self)
26
68
  end
27
69
  end
28
70
 
@@ -33,27 +75,35 @@ module DynamodbRecord
33
75
  list << base_model
34
76
  list << relation_model
35
77
  sorted_list = list.sort
36
- table = sorted_list.map(&:pluralize).join('_')
78
+ options = {}
79
+ pluralize_name = sorted_list.map(&:pluralize).join('-')
80
+
81
+ table = Config.namespace ? "#{Config.namespace}-#{pluralize_name}" : pluralize_name
82
+ options[:table_name] = table
83
+ if sorted_list.first == base_model
84
+ field = sorted_list.first
85
+ else
86
+ field = sorted_list.last
87
+ options.merge!(index_name: "#{table}-index")
88
+ end
89
+
90
+ options.merge!(key_condition_expression: "##{field}_id = :#{field}_id")
91
+ options.merge!(expression_attribute_names: {"##{field}_id": "#{field}_id"})
37
92
 
38
93
  define_method(associations) do
39
- options = { table_name: table }
40
-
41
- if sorted_list.first == base_model
42
- field = sorted_list.first
43
- else
44
- field = sorted_list.last
45
- options.merge!(index_name: "#{table}_index")
46
- end
47
- options.merge!(key_condition_expression: "##{field}_id = :#{field}_id")
48
- options.merge!(expression_attribute_names: { "##{field}_id": "#{field}_id" })
49
- options.merge!(expression_attribute_values: { ":#{field}_id" => id })
94
+ options.merge!(expression_attribute_values: {":#{field}_id" => id})
95
+ # p options
50
96
 
51
97
  klass = Object.const_get(relation_model.capitalize)
52
- response = self.class.client.query(options)
53
98
 
54
- DynamodbRecord::Collection.new(response, klass, options)
99
+ # options.merge!(limit: 1)
100
+ # p options
101
+
102
+ query = QueryPager.new(options, klass)
103
+ HasAndBelongsToManyCollection.new(query, self)
55
104
  end
56
105
  end
106
+ # rubocop:enable Naming/PredicateName
57
107
  end
58
108
  end
59
109
  end
@@ -6,42 +6,33 @@ module DynamodbRecord
6
6
 
7
7
  # attr_reader :last_evaluated_key
8
8
 
9
- def initialize(pager, klass, options = {})
10
- @klass = klass
11
- @table_name = options[:table_name]
12
- @foreign_key = options[:expression_attribute_values].transform_keys { |k| k.delete_prefix(':').to_sym } rescue nil
13
- @items = pager.items.map { |item| klass.send(:from_database, item) }
14
- @last_evaluated_key = pager.last_evaluated_key
15
- end
16
-
17
- def last_evaluated_key
18
- if @last_evaluated_key.nil?
19
- nil
20
- else
21
- json_string = JSON.dump(@last_evaluated_key)
22
- Base64.urlsafe_encode64(json_string)
23
- end
9
+ def initialize(pager, base_object)
10
+ @base_object = base_object
11
+ @pager = pager
12
+ @klass = @pager.klass
13
+ @options = @pager.options
14
+ items = @pager.items
15
+ @items = items.map { |item| @klass.send(:from_database, item) }
24
16
  end
25
17
 
26
18
  def each(&)
27
19
  @items.each(&)
28
20
  end
29
21
 
22
+ def last_evaluated_key
23
+ @pager.last_evaluated_key
24
+ end
25
+
30
26
  def next_page?
31
- last_evaluated_key ? true : false
27
+ @pager ? @pager.next_page? : false
28
+ end
29
+
30
+ def next_page
31
+ self.class.new(@pager.next_page, @base_object)
32
32
  end
33
33
 
34
- def create!(items = {})
35
- items.merge!(@foreign_key)
36
- object = @klass.create!(items)
37
- unless @klass.to_s.pluralize.downcase == @table_name.to_s
38
-
39
- client = @klass.client
40
- @foreign_key.merge!({ "#{@klass.to_s.downcase}_id": object.id, created_at: Time.now.to_i })
41
- client.put_item({ table_name: @table_name, item: @foreign_key })
42
- end
43
- @items << object
44
- object
34
+ def page(last_key)
35
+ self.class.new(@pager.next_page(last_key), @base_object) if last_key
45
36
  end
46
37
  end
47
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DynamodbRecord
2
4
  module Config
3
5
  extend self
@@ -19,7 +19,7 @@ module DynamodbRecord
19
19
  def field(name, type = :string, opts = {})
20
20
  named = name.to_s
21
21
  # Add attributes
22
- attributes.merge!(name => { type:, options: opts })
22
+ attributes.merge!(name => {type:, options: opts})
23
23
 
24
24
  # self.hash_key = name if opts[:hash_key]
25
25
  # self.range_key = name if opts[:range_key]
@@ -60,11 +60,10 @@ module DynamodbRecord
60
60
  DateTime.parse(value)
61
61
  end
62
62
  when :array
63
- if value.is_a?(Array)
64
- value
65
- else
66
- raise ArgumentError, 'Array column'
67
- end
63
+ raise ArgumentError, 'Array column' unless value.is_a?(Array)
64
+
65
+ value
66
+
68
67
  else
69
68
  raise ArgumentError, "Unknown type #{options[:type]}"
70
69
  end
@@ -84,21 +83,21 @@ module DynamodbRecord
84
83
  def unload(attrs)
85
84
  {}.tap do |hash|
86
85
  attrs.each do |key, value|
87
- # if attributes[key.to_sym][:options][:hash_key]
88
- # hash[:pk] = dump_field(value, attributes[key.to_sym])
89
- # end
90
-
91
86
  hash[key] = dump_field(value, attributes[key.to_sym])
92
87
  end
93
88
  end
94
89
  end
95
90
 
96
91
  def hash_key
97
- @hash_key = attributes.select { |_k,v| v[:options][:hash_key] }.keys.first || :id
92
+ @hash_key = attributes.select { |_k, v| v[:options][:hash_key] }.keys.first || :id
98
93
  end
99
94
 
100
95
  def range_key
101
- @range_key ||= attributes.select { |_k,v| v[:options][:range_key] }.keys.first rescue nil
96
+ @range_key ||= begin
97
+ attributes.select { |_k, v| v[:options][:range_key] }.keys.first
98
+ rescue StandardError
99
+ nil
100
+ end
102
101
  end
103
102
 
104
103
  def secondary_indexes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DynamodbRecord
4
+ # +Dynamodb::Finders+ is a class that represent a Finders
4
5
  module Finders
5
6
  extend ActiveSupport::Concern
6
7
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamodbRecord
4
+ # +Dynamodb::ManyToManyCollection+ is a class that represent a ManyToManyCollection
5
+ class HasAndBelongsToManyCollection
6
+ include Enumerable
7
+
8
+ def initialize(pager, base_object)
9
+ @base_object = base_object
10
+ @pager = pager
11
+ @klass = @pager.klass
12
+ @options = @pager.options
13
+ @items = []
14
+ @pager.items.each do |item|
15
+ # p item
16
+ object = @klass.client.get_item(
17
+ table_name: @klass.table_name,
18
+ key: {id: item["#{@klass.to_s.downcase}_id"]}
19
+ )
20
+ @items << @klass.send(:from_database, object.item)
21
+ end
22
+ end
23
+
24
+ def each(&)
25
+ @items.each(&)
26
+ end
27
+
28
+ def last_evaluated_key
29
+ @pager.last_evaluated_key
30
+ end
31
+
32
+ def next_page?
33
+ @pager ? @pager.next_page? : false
34
+ end
35
+
36
+ def next_page
37
+ self.class.new(@pager.next_page, @base_object)
38
+ end
39
+
40
+ def page(last_key)
41
+ self.class.new(@pager.next_page(last_key), @base_object) if last_key
42
+ end
43
+
44
+ def create!(params = {})
45
+ raise "#{@base_object.class} must be saved" if @base_object.new_record
46
+
47
+ object = @klass.send(:from_database, params)
48
+ object.save!
49
+ table_name = @options[:table_name]
50
+ item = @options[:expression_attribute_values].transform_keys { |k| k.delete_prefix(':').to_sym }
51
+
52
+ item[:"#{@klass.to_s.downcase}_id"] = object.id
53
+ key = {table_name:, item:}
54
+ @klass.client.put_item(key)
55
+ object
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamodbRecord
4
+ # +Dynamodb::ManyToManyCollection+ is a class that represent a ManyToManyCollection
5
+ class HasManyCollection
6
+ include Enumerable
7
+
8
+ def initialize(pager, base_object)
9
+ @base_object = base_object
10
+ @pager = pager
11
+ @klass = @pager.klass
12
+ @options = @pager.options
13
+ @foreign_key = @options[:expression_attribute_values].transform_keys { |k| k.delete_prefix(':').to_sym }
14
+ @items = []
15
+ @pager.items.each do |object|
16
+ @items << @klass.send(:from_database, object)
17
+ end
18
+ end
19
+
20
+ def each(&)
21
+ @items.each(&)
22
+ end
23
+
24
+ def last_evaluated_key
25
+ @pager.last_evaluated_key
26
+ end
27
+
28
+ def next_page?
29
+ @pager ? @pager.next_page? : false
30
+ end
31
+
32
+ def next_page
33
+ self.class.new(@pager.next_page, @base_object)
34
+ end
35
+
36
+ def page(last_key)
37
+ self.class.new(@pager.next_page(last_key), @base_object) if last_key
38
+ end
39
+
40
+ def create!(params = {})
41
+ raise "#{@base_object.class} must be saved" if @base_object.new_record
42
+
43
+ params.merge!(@foreign_key)
44
+ object = @klass.send(:from_database, params)
45
+ object.save!
46
+ @items << object
47
+ object
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamodbRecord
4
+ # +Dynamodb::HasManyThroughCollection+ is a class that represent a ManyToManyCollection
5
+ class HasManyThroughCollection
6
+ include Enumerable
7
+
8
+ def initialize(pager, base_object)
9
+ @base_object = base_object
10
+ @pager = pager
11
+ @klass = @pager.klass
12
+ @options = @pager.options
13
+ @items = []
14
+ @pager.items.each do |item|
15
+ # p item
16
+ object = @klass.client.get_item(
17
+ table_name: @klass.table_name,
18
+ key: {id: item["#{@klass.to_s.downcase}_id"]}
19
+ )
20
+ @items << @klass.send(:from_database, object.item)
21
+ end
22
+ end
23
+
24
+ def each(&)
25
+ @items.each(&)
26
+ end
27
+
28
+ def last_evaluated_key
29
+ @pager.last_evaluated_key
30
+ end
31
+
32
+ def next_page?
33
+ @pager ? @pager.next_page? : false
34
+ end
35
+
36
+ def next_page
37
+ self.class.new(@pager.next_page, @base_object)
38
+ end
39
+
40
+ def page(last_key)
41
+ self.class.new(@pager.next_page(last_key), @base_object) if last_key
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ module DynamodbRecord
2
+ # +Dynamodb::Pager+ is a abstract class to paginate
3
+ class Pager
4
+ attr_reader :klass
5
+
6
+ def initialize(options, klass)
7
+ @options = options
8
+ @klass = klass
9
+ end
10
+
11
+ def last_evaluated_key
12
+ raise NotImplementedError, 'Este método debe ser implementado por las subclases'
13
+ end
14
+ end
15
+ end
@@ -12,7 +12,7 @@ module DynamodbRecord
12
12
  end
13
13
 
14
14
  def client
15
- opts = { region: DynamodbRecord::Config.region }
15
+ opts = {region: DynamodbRecord::Config.region}
16
16
  opts[:endpoint] = DynamodbRecord::Config.endpoint if DynamodbRecord::Config.endpoint
17
17
  opts[:access_key_id] = DynamodbRecord::Config.access_key_id if DynamodbRecord::Config.access_key_id
18
18
  opts[:secret_access_key] = DynamodbRecord::Config.access_key_id if DynamodbRecord::Config.secret_access_key
@@ -20,7 +20,7 @@ module DynamodbRecord
20
20
  end
21
21
 
22
22
  def default_options
23
- { table_name: }
23
+ {table_name:}
24
24
  end
25
25
 
26
26
  def describe_table
@@ -36,16 +36,16 @@ module DynamodbRecord
36
36
  key_schema = []
37
37
 
38
38
  # Default id hash key
39
- attribute_definitions << { attribute_name: 'id',
40
- attribute_type: 'S' }
41
- key_schema << { attribute_name: 'id',
42
- key_type: 'HASH' }
39
+ attribute_definitions << {attribute_name: 'id',
40
+ attribute_type: 'S'}
41
+ key_schema << {attribute_name: 'id',
42
+ key_type: 'HASH'}
43
43
 
44
44
  if range_key
45
- attribute_definitions << { attribute_name: range_key.to_s,
46
- attribute_type: dynamodb_type(attributes[range_key][:type]) }
47
- key_schema << { attribute_name: range_key.to_s,
48
- key_type: 'RANGE' }
45
+ attribute_definitions << {attribute_name: range_key.to_s,
46
+ attribute_type: dynamodb_type(attributes[range_key][:type])}
47
+ key_schema << {attribute_name: range_key.to_s,
48
+ key_type: 'RANGE'}
49
49
  end
50
50
 
51
51
  # Global secondary indexes
@@ -54,20 +54,20 @@ module DynamodbRecord
54
54
  indexes << key if value[:options][:index]
55
55
  end
56
56
 
57
- global_secoglobal_secondary_indexesndary_indexes = []
57
+ # global_secoglobal_secondary_indexesndary_indexes = []
58
58
  indexes.each do |index|
59
59
  index_definition = {}
60
60
  index_definition[:index_name] = "#{index}_index"
61
- index_definition[:key_schema] = [{ attribute_name: index, key_type: 'HASH' },
62
- { attribute_name: 'id', key_type: 'RANGE' }]
63
- index_definition[:projection] = { projection_type: 'ALL' }
61
+ index_definition[:key_schema] = [{attribute_name: index, key_type: 'HASH'},
62
+ {attribute_name: 'id', key_type: 'RANGE'}]
63
+ index_definition[:projection] = {projection_type: 'ALL'}
64
64
  index_definition[:provisioned_throughput] = {
65
65
  read_capacity_units: 1,
66
66
  write_capacity_units: 1
67
67
  }
68
68
  global_secondary_indexes << index_definition
69
- attribute_definitions << { attribute_name: index.to_s,
70
- attribute_type: dynamodb_type(attributes[index][:type]) }
69
+ attribute_definitions << {attribute_name: index.to_s,
70
+ attribute_type: dynamodb_type(attributes[index][:type])}
71
71
  end
72
72
 
73
73
  options = {
@@ -113,7 +113,7 @@ module DynamodbRecord
113
113
  self.updated_at = time
114
114
 
115
115
  if @new_record # New item. Don't overwrite if id exists
116
- options.merge!(condition_expression: 'id <> :s', expression_attribute_values: { ':s' => id })
116
+ options.merge!(condition_expression: 'id <> :s', expression_attribute_values: {':s' => id})
117
117
  end
118
118
 
119
119
  options.merge!(item: self.class.unload(attributes))
@@ -127,10 +127,10 @@ module DynamodbRecord
127
127
  hash_key = self.class.hash_key
128
128
  range_key = self.class.range_key
129
129
  key = {}
130
- key[hash_key] = self.class.dump_field(self.read_attribute(hash_key), self.class.attributes[hash_key])
130
+ key[hash_key] = self.class.dump_field(read_attribute(hash_key), self.class.attributes[hash_key])
131
131
 
132
132
  if range_key.present?
133
- key[range_key] = self.class.dump_field(self.read_attribute(range_key), self.class.attributes[range_key])
133
+ key[range_key] = self.class.dump_field(read_attribute(range_key), self.class.attributes[range_key])
134
134
  end
135
135
 
136
136
  self.class.client.delete_item(options.merge(key:))
@@ -1,3 +1,4 @@
1
+ # rubocop:disable Style/FrozenStringLiteralComment
1
2
 
2
3
  module DynamodbRecord
3
4
  # Query Module
@@ -9,8 +10,8 @@ module DynamodbRecord
9
10
  def all(opts = {})
10
11
  options = default_options
11
12
  options.merge!(opts.slice(:limit))
12
- response = client.scan(options)
13
- DynamodbRecord::Collection.new(response, self)
13
+ scan_pager = ScanPager.new(options, self)
14
+ Collection.new(scan_pager, self)
14
15
  end
15
16
 
16
17
  # search table
@@ -23,7 +24,7 @@ module DynamodbRecord
23
24
  expression_attribute_values = {}
24
25
  filter_expression = ''
25
26
  opts.each do |key, value|
26
- expression_attribute_names["##{key}".to_sym] = key.to_s
27
+ expression_attribute_names[:"##{key}"] = key.to_s
27
28
  expression_attribute_values[":#{key}"] = value
28
29
  filter_expression << if filter_expression.empty?
29
30
  "##{key} = :#{key}"
@@ -37,13 +38,15 @@ module DynamodbRecord
37
38
  options.merge!(expression_attribute_names:) if expression_attribute_names.present?
38
39
  options.merge!(expression_attribute_values:) if expression_attribute_values.present?
39
40
  options.merge!(filter_expression:) if filter_expression.present?
40
- if exclusive_start_key.present?
41
- json_string_decode = Base64.urlsafe_decode64(exclusive_start_key)
42
- decode = JSON.parse(json_string_decode)
43
- options.merge!(exclusive_start_key: decode)
44
- end
45
- DynamodbRecord::Collection.new(client.scan(options), self)
41
+
42
+ options.merge!(exclusive_start_key:) if exclusive_start_key
43
+
44
+ scan_pager = ScanPager.new(options, self)
45
+ Collection.new(scan_pager, self)
46
+
47
+ # DynamodbRecord::Collection.new(client.scan(options), self)
46
48
  end
47
49
  end
48
50
  end
49
51
  end
52
+ # rubocop:enable Style/FrozenStringLiteralComment
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DynamodbRecord
4
+ # +Dynamodb::Page+ is a class that include logic business from paginate
5
+ class QueryPager < Pager
6
+ attr_reader :last_evaluated_key, :items, :options
7
+
8
+ def initialize(options, klass)
9
+ super(options, klass)
10
+ response = @klass.client.query(@options)
11
+ @items = response.items
12
+ @last_evaluated_key = response.last_evaluated_key
13
+ end
14
+
15
+ def next_page?
16
+ last_evaluated_key ? true : false
17
+ end
18
+
19
+ def next_page(last_key = nil)
20
+ return nil unless next_page?
21
+
22
+ @last_evaluated_key = last_key if last_key.present?
23
+
24
+ @options.merge!(exclusive_start_key: last_evaluated_key) if next_page?
25
+ self.class.new(@options, @klass)
26
+ end
27
+ end
28
+ end