dynamoid 3.9.0 → 3.10.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -7
  3. data/README.md +20 -23
  4. data/dynamoid.gemspec +1 -2
  5. data/lib/dynamoid/adapter.rb +18 -12
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +78 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +19 -1
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +38 -0
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +33 -27
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +87 -62
  13. data/lib/dynamoid/associations/belongs_to.rb +6 -6
  14. data/lib/dynamoid/associations.rb +1 -1
  15. data/lib/dynamoid/config/options.rb +12 -12
  16. data/lib/dynamoid/config.rb +1 -0
  17. data/lib/dynamoid/criteria/chain.rb +95 -133
  18. data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
  19. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
  20. data/lib/dynamoid/criteria/where_conditions.rb +29 -0
  21. data/lib/dynamoid/dirty.rb +1 -1
  22. data/lib/dynamoid/document.rb +1 -1
  23. data/lib/dynamoid/dumping.rb +2 -2
  24. data/lib/dynamoid/fields/declare.rb +6 -6
  25. data/lib/dynamoid/fields.rb +6 -8
  26. data/lib/dynamoid/finders.rb +17 -26
  27. data/lib/dynamoid/indexes.rb +6 -7
  28. data/lib/dynamoid/loadable.rb +2 -2
  29. data/lib/dynamoid/persistence/save.rb +12 -16
  30. data/lib/dynamoid/persistence/update_fields.rb +2 -2
  31. data/lib/dynamoid/persistence/update_validations.rb +1 -1
  32. data/lib/dynamoid/persistence.rb +39 -4
  33. data/lib/dynamoid/type_casting.rb +15 -14
  34. data/lib/dynamoid/undumping.rb +1 -1
  35. data/lib/dynamoid/version.rb +1 -1
  36. metadata +17 -16
  37. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
  38. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -40
@@ -1,29 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'key_fields_detector'
4
- require_relative 'ignored_conditions_detector'
5
- require_relative 'overwritten_conditions_detector'
6
4
  require_relative 'nonexistent_fields_detector'
5
+ require_relative 'where_conditions'
7
6
 
8
7
  module Dynamoid
9
8
  module Criteria
10
9
  # The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
11
10
  # chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
12
11
  class Chain
13
- attr_reader :query, :source, :consistent_read, :key_fields_detector
12
+ attr_reader :source, :consistent_read, :key_fields_detector
14
13
 
15
14
  include Enumerable
15
+
16
+ ALLOWED_FIELD_OPERATORS = Set.new(
17
+ %w[
18
+ eq ne gt lt gte lte between begins_with in contains not_contains null not_null
19
+ ]
20
+ ).freeze
21
+
16
22
  # Create a new criteria chain.
17
23
  #
18
24
  # @param [Class] source the class upon which the ultimate query will be performed.
19
25
  def initialize(source)
20
- @query = {}
26
+ @where_conditions = WhereConditions.new
21
27
  @source = source
22
28
  @consistent_read = false
23
29
  @scan_index_forward = true
24
30
 
25
- # we should re-initialize keys detector every time we change query
26
- @key_fields_detector = KeyFieldsDetector.new(@query, @source)
31
+ # we should re-initialize keys detector every time we change @where_conditions
32
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source)
27
33
  end
28
34
 
29
35
  # Returns a chain which is a result of filtering current chain with the specified conditions.
@@ -92,25 +98,15 @@ module Dynamoid
92
98
  # @return [Dynamoid::Criteria::Chain]
93
99
  # @since 0.2.0
94
100
  def where(args)
95
- detector = IgnoredConditionsDetector.new(args)
96
- if detector.found?
97
- Dynamoid.logger.warn(detector.warning_message)
98
- end
99
-
100
- detector = OverwrittenConditionsDetector.new(@query, args)
101
- if detector.found?
102
- Dynamoid.logger.warn(detector.warning_message)
103
- end
104
-
105
101
  detector = NonexistentFieldsDetector.new(args, @source)
106
102
  if detector.found?
107
103
  Dynamoid.logger.warn(detector.warning_message)
108
104
  end
109
105
 
110
- query.update(args.symbolize_keys)
106
+ @where_conditions.update(args.symbolize_keys)
111
107
 
112
- # we should re-initialize keys detector every time we change query
113
- @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: @forced_index_name)
108
+ # we should re-initialize keys detector every time we change @where_conditions
109
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source, forced_index_name: @forced_index_name)
114
110
 
115
111
  self
116
112
  end
@@ -187,7 +183,7 @@ module Dynamoid
187
183
  def first(*args)
188
184
  n = args.first || 1
189
185
 
190
- return dup.scan_limit(n).to_a.first(*args) if @query.blank?
186
+ return dup.scan_limit(n).to_a.first(*args) if @where_conditions.empty?
191
187
  return super if @key_fields_detector.non_key_present?
192
188
 
193
189
  dup.record_limit(n).to_a.first(*args)
@@ -230,12 +226,12 @@ module Dynamoid
230
226
  ranges = []
231
227
 
232
228
  if @key_fields_detector.key_present?
233
- Dynamoid.adapter.query(source.table_name, range_query).flat_map { |i| i }.collect do |hash|
229
+ Dynamoid.adapter.query(source.table_name, query_key_conditions, query_non_key_conditions, query_options).flat_map { |i| i }.collect do |hash|
234
230
  ids << hash[source.hash_key.to_sym]
235
231
  ranges << hash[source.range_key.to_sym] if source.range_key
236
232
  end
237
233
  else
238
- Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).flat_map { |i| i }.collect do |hash|
234
+ Dynamoid.adapter.scan(source.table_name, scan_conditions, scan_options).flat_map { |i| i }.collect do |hash|
239
235
  ids << hash[source.hash_key.to_sym]
240
236
  ranges << hash[source.range_key.to_sym] if source.range_key
241
237
  end
@@ -384,7 +380,7 @@ module Dynamoid
384
380
  raise Dynamoid::Errors::InvalidIndex, "Unknown index #{index_name}" unless @source.find_index_by_name(index_name)
385
381
 
386
382
  @forced_index_name = index_name
387
- @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: index_name)
383
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source, forced_index_name: index_name)
388
384
  self
389
385
  end
390
386
 
@@ -451,9 +447,9 @@ module Dynamoid
451
447
  # It takes one or more field names and returns a collection of models with only
452
448
  # these fields set.
453
449
  #
454
- # Post.where('views_count.gt' => 1000).select(:title)
455
- # Post.where('views_count.gt' => 1000).select(:title, :created_at)
456
- # Post.select(:id)
450
+ # Post.where('views_count.gt' => 1000).project(:title)
451
+ # Post.where('views_count.gt' => 1000).project(:title, :created_at)
452
+ # Post.project(:id)
457
453
  #
458
454
  # It can be used to avoid loading large field values and to decrease a
459
455
  # memory footprint.
@@ -487,6 +483,8 @@ module Dynamoid
487
483
  def pluck(*args)
488
484
  fields = args.map(&:to_sym)
489
485
 
486
+ # `project` has a side effect - it sets `@project` instance variable.
487
+ # So use a duplicate to not pollute original chain.
490
488
  scope = dup
491
489
  scope.project(*fields)
492
490
 
@@ -535,7 +533,7 @@ module Dynamoid
535
533
  if @key_fields_detector.key_present?
536
534
  raw_pages_via_query
537
535
  else
538
- issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
536
+ issue_scan_warning if Dynamoid::Config.warn_on_scan && !@where_conditions.empty?
539
537
  raw_pages_via_scan
540
538
  end
541
539
  end
@@ -547,7 +545,7 @@ module Dynamoid
547
545
  # @since 3.1.0
548
546
  def raw_pages_via_query
549
547
  Enumerator.new do |y|
550
- Dynamoid.adapter.query(source.table_name, range_query).each do |items, metadata|
548
+ Dynamoid.adapter.query(source.table_name, query_key_conditions, query_non_key_conditions, query_options).each do |items, metadata|
551
549
  options = metadata.slice(:last_evaluated_key)
552
550
 
553
551
  y.yield items, options
@@ -562,7 +560,7 @@ module Dynamoid
562
560
  # @since 3.1.0
563
561
  def raw_pages_via_scan
564
562
  Enumerator.new do |y|
565
- Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items, metadata|
563
+ Dynamoid.adapter.scan(source.table_name, scan_conditions, scan_options).each do |items, metadata|
566
564
  options = metadata.slice(:last_evaluated_key)
567
565
 
568
566
  y.yield items, options
@@ -575,121 +573,88 @@ module Dynamoid
575
573
  Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.underscore}.rb:"
576
574
  Dynamoid.logger.warn "* global_secondary_index hash_key: 'some-name', range_key: 'some-another-name'"
577
575
  Dynamoid.logger.warn "* local_secondary_index range_key: 'some-name'"
578
- Dynamoid.logger.warn "Not indexed attributes: #{query.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
576
+ Dynamoid.logger.warn "Not indexed attributes: #{@where_conditions.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
579
577
  end
580
578
 
581
579
  def count_via_query
582
- Dynamoid.adapter.query_count(source.table_name, range_query)
580
+ Dynamoid.adapter.query_count(source.table_name, query_key_conditions, query_non_key_conditions, query_options)
583
581
  end
584
582
 
585
583
  def count_via_scan
586
- Dynamoid.adapter.scan_count(source.table_name, scan_query, scan_opts)
587
- end
588
-
589
- def range_hash(key)
590
- name, operation = key.to_s.split('.')
591
- val = type_cast_condition_parameter(name, query[key])
592
-
593
- case operation
594
- when 'gt'
595
- { range_greater_than: val }
596
- when 'lt'
597
- { range_less_than: val }
598
- when 'gte'
599
- { range_gte: val }
600
- when 'lte'
601
- { range_lte: val }
602
- when 'between'
603
- { range_between: val }
604
- when 'begins_with'
605
- { range_begins_with: val }
606
- end
584
+ Dynamoid.adapter.scan_count(source.table_name, scan_conditions, scan_options)
607
585
  end
608
586
 
609
- def field_hash(key)
610
- name, operation = key.to_s.split('.')
611
- val = type_cast_condition_parameter(name, query[key])
612
-
613
- hash = case operation
614
- when 'ne'
615
- { ne: val }
616
- when 'gt'
617
- { gt: val }
618
- when 'lt'
619
- { lt: val }
620
- when 'gte'
621
- { gte: val }
622
- when 'lte'
623
- { lte: val }
624
- when 'between'
625
- { between: val }
626
- when 'begins_with'
627
- { begins_with: val }
628
- when 'in'
629
- { in: val }
630
- when 'contains'
631
- { contains: val }
632
- when 'not_contains'
633
- { not_contains: val }
634
- # NULL/NOT_NULL operators don't have parameters
635
- # So { null: true } means NULL check and { null: false } means NOT_NULL one
636
- # The same logic is used for { not_null: BOOL }
637
- when 'null'
638
- val ? { null: nil } : { not_null: nil }
639
- when 'not_null'
640
- val ? { not_null: nil } : { null: nil }
641
- end
642
-
643
- { name.to_sym => hash }
644
- end
645
-
646
- def consistent_opts
647
- { consistent_read: consistent_read }
648
- end
649
-
650
- def range_query
651
- opts = {}
652
- query = self.query
587
+ def field_condition(key, value_before_type_casting)
588
+ name, operator = key.to_s.split('.')
589
+ value = type_cast_condition_parameter(name, value_before_type_casting)
590
+ operator ||= 'eq'
653
591
 
654
- # Honor STI and :type field if it presents
655
- if @source.attributes.key?(@source.inheritance_field) &&
656
- @key_fields_detector.hash_key.to_sym != @source.inheritance_field.to_sym
657
- query.update(sti_condition)
592
+ unless operator.in? ALLOWED_FIELD_OPERATORS
593
+ raise Dynamoid::Errors::Error, "Unsupported operator #{operator} in #{key}"
658
594
  end
659
595
 
596
+ condition =
597
+ case operator
598
+ # NULL/NOT_NULL operators don't have parameters
599
+ # So { null: true } means NULL check and { null: false } means NOT_NULL one
600
+ # The same logic is used for { not_null: BOOL }
601
+ when 'null'
602
+ value ? [:null, nil] : [:not_null, nil]
603
+ when 'not_null'
604
+ value ? [:not_null, nil] : [:null, nil]
605
+ else
606
+ [operator.to_sym, value]
607
+ end
608
+
609
+ [name.to_sym, condition]
610
+ end
611
+
612
+ def query_key_conditions
613
+ opts = {}
614
+
660
615
  # Add hash key
661
- opts[:hash_key] = @key_fields_detector.hash_key
662
- opts[:hash_value] = type_cast_condition_parameter(@key_fields_detector.hash_key, query[@key_fields_detector.hash_key])
616
+ # TODO: always have hash key in @where_conditions?
617
+ _, condition = field_condition(@key_fields_detector.hash_key, @where_conditions[@key_fields_detector.hash_key])
618
+ opts[@key_fields_detector.hash_key] = [condition]
663
619
 
664
620
  # Add range key
665
621
  if @key_fields_detector.range_key
666
- add_range_key_to_range_query(query, opts)
667
- end
622
+ if @where_conditions[@key_fields_detector.range_key].present?
623
+ _, condition = field_condition(@key_fields_detector.range_key, @where_conditions[@key_fields_detector.range_key])
624
+ opts[@key_fields_detector.range_key] = [condition]
625
+ end
668
626
 
669
- (query.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
670
- .reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
671
- .each do |key|
672
- if key.to_s.include?('.')
673
- opts.update(field_hash(key))
674
- else
675
- value = type_cast_condition_parameter(key, query[key])
676
- opts[key] = { eq: value }
627
+ @where_conditions.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
628
+ name, condition = field_condition(key, @where_conditions[key])
629
+ opts[name] ||= []
630
+ opts[name] << condition
677
631
  end
678
632
  end
679
633
 
680
- opts.merge(query_opts).merge(consistent_opts)
634
+ opts
681
635
  end
682
636
 
683
- def add_range_key_to_range_query(query, opts)
684
- opts[:range_key] = @key_fields_detector.range_key
685
- if query[@key_fields_detector.range_key].present?
686
- value = type_cast_condition_parameter(@key_fields_detector.range_key, query[@key_fields_detector.range_key])
687
- opts.update(range_eq: value)
637
+ def query_non_key_conditions
638
+ opts = {}
639
+
640
+ # Honor STI and :type field if it presents
641
+ if @source.attributes.key?(@source.inheritance_field) &&
642
+ @key_fields_detector.hash_key.to_sym != @source.inheritance_field.to_sym
643
+ @where_conditions.update(sti_condition)
688
644
  end
689
645
 
690
- query.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
691
- opts.merge!(range_hash(key))
646
+ # TODO: Separate key conditions and non-key conditions properly:
647
+ # only =, >, >=, <, <=, between and begins_with
648
+ # could be used for sort key in KeyConditionExpression
649
+ keys = (@where_conditions.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
650
+ .reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
651
+ keys.each do |key|
652
+ name, condition = field_condition(key, @where_conditions[key])
653
+ opts[name] ||= []
654
+ opts[name] << condition
692
655
  end
656
+
657
+ opts
693
658
  end
694
659
 
695
660
  # TODO: casting should be operator aware
@@ -737,7 +702,7 @@ module Dynamoid
737
702
  key
738
703
  end
739
704
 
740
- def query_opts
705
+ def query_options
741
706
  opts = {}
742
707
  # Don't specify select = ALL_ATTRIBUTES option explicitly because it's
743
708
  # already a default value of Select statement. Explicite Select value
@@ -749,30 +714,26 @@ module Dynamoid
749
714
  opts[:exclusive_start_key] = start_key if @start
750
715
  opts[:scan_index_forward] = @scan_index_forward
751
716
  opts[:project] = @project
717
+ opts[:consistent_read] = true if @consistent_read
752
718
  opts
753
719
  end
754
720
 
755
- def scan_query
756
- query = self.query
757
-
721
+ def scan_conditions
758
722
  # Honor STI and :type field if it presents
759
723
  if sti_condition
760
- query.update(sti_condition)
724
+ @where_conditions.update(sti_condition)
761
725
  end
762
726
 
763
727
  {}.tap do |opts|
764
- query.keys.map(&:to_sym).each do |key|
765
- if key.to_s.include?('.')
766
- opts.update(field_hash(key))
767
- else
768
- value = type_cast_condition_parameter(key, query[key])
769
- opts[key] = { eq: value }
770
- end
728
+ @where_conditions.keys.map(&:to_sym).each do |key|
729
+ name, condition = field_condition(key, @where_conditions[key])
730
+ opts[name] ||= []
731
+ opts[name] << condition
771
732
  end
772
733
  end
773
734
  end
774
735
 
775
- def scan_opts
736
+ def scan_options
776
737
  opts = {}
777
738
  opts[:index_name] = @key_fields_detector.index_name if @key_fields_detector.index_name
778
739
  opts[:record_limit] = @record_limit if @record_limit
@@ -784,6 +745,7 @@ module Dynamoid
784
745
  opts
785
746
  end
786
747
 
748
+ # TODO: return Array, not String
787
749
  def sti_condition
788
750
  condition = {}
789
751
  type = @source.inheritance_field
@@ -5,10 +5,10 @@ module Dynamoid
5
5
  # @private
6
6
  class KeyFieldsDetector
7
7
  class Query
8
- def initialize(query_hash)
9
- @query_hash = query_hash
10
- @fields_with_operator = query_hash.keys.map(&:to_s)
11
- @fields = query_hash.keys.map(&:to_s).map { |s| s.split('.').first }
8
+ def initialize(where_conditions)
9
+ @where_conditions = where_conditions
10
+ @fields_with_operator = where_conditions.keys.map(&:to_s)
11
+ @fields = where_conditions.keys.map(&:to_s).map { |s| s.split('.').first }
12
12
  end
13
13
 
14
14
  def contain_only?(field_names)
@@ -24,10 +24,9 @@ module Dynamoid
24
24
  end
25
25
  end
26
26
 
27
- def initialize(query, source, forced_index_name: nil)
28
- @query = query
27
+ def initialize(where_conditions, source, forced_index_name: nil)
29
28
  @source = source
30
- @query = Query.new(query)
29
+ @query = Query.new(where_conditions)
31
30
  @forced_index_name = forced_index_name
32
31
  @result = find_keys_in_query
33
32
  end
@@ -20,8 +20,8 @@ module Dynamoid
20
20
  fields_list = @nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
21
21
  count = @nonexistent_fields.size
22
22
 
23
- 'where conditions contain nonexistent' \
24
- " field #{'name'.pluralize(count)} #{fields_list}"
23
+ 'where conditions contain nonexistent ' \
24
+ "field #{'name'.pluralize(count)} #{fields_list}"
25
25
  end
26
26
 
27
27
  private
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dynamoid
4
+ module Criteria
5
+ # @private
6
+ class WhereConditions
7
+ def initialize
8
+ @conditions = []
9
+ end
10
+
11
+ def update(hash)
12
+ @conditions << hash.symbolize_keys
13
+ end
14
+
15
+ def keys
16
+ @conditions.flat_map(&:keys)
17
+ end
18
+
19
+ def empty?
20
+ @conditions.empty?
21
+ end
22
+
23
+ def [](key)
24
+ hash = @conditions.find { |h| h.key?(key) }
25
+ hash[key] if hash
26
+ end
27
+ end
28
+ end
29
+ end
@@ -238,7 +238,7 @@ module Dynamoid
238
238
 
239
239
  begin
240
240
  value = read_attribute(name)
241
- value = value.duplicable? ? value.clone : value
241
+ value = value.clone if value.duplicable?
242
242
  rescue TypeError, NoMethodError
243
243
  end
244
244
 
@@ -260,7 +260,7 @@ module Dynamoid
260
260
  #
261
261
  # @return [Integer]
262
262
  def hash
263
- hash_key.hash ^ range_value.hash
263
+ [hash_key, range_value].hash
264
264
  end
265
265
 
266
266
  # Return a model's hash key value.
@@ -210,7 +210,7 @@ module Dynamoid
210
210
  # datetime -> integer/string
211
211
  class DateTimeDumper < Base
212
212
  def process(value)
213
- !value.nil? ? format_datetime(value, @options) : nil
213
+ value.nil? ? nil : format_datetime(value, @options)
214
214
  end
215
215
 
216
216
  private
@@ -237,7 +237,7 @@ module Dynamoid
237
237
  # date -> integer/string
238
238
  class DateDumper < Base
239
239
  def process(value)
240
- !value.nil? ? format_date(value, @options) : nil
240
+ value.nil? ? nil : format_date(value, @options)
241
241
  end
242
242
 
243
243
  private
@@ -48,7 +48,7 @@ module Dynamoid
48
48
 
49
49
  @source.generated_methods.module_eval do
50
50
  define_method(name) { read_attribute(name) }
51
- define_method("#{name}?") do
51
+ define_method(:"#{name}?") do
52
52
  value = read_attribute(name)
53
53
  case value
54
54
  when true then true
@@ -57,8 +57,8 @@ module Dynamoid
57
57
  !value.nil?
58
58
  end
59
59
  end
60
- define_method("#{name}=") { |value| write_attribute(name, value) }
61
- define_method("#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
60
+ define_method(:"#{name}=") { |value| write_attribute(name, value) }
61
+ define_method(:"#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
62
62
  end
63
63
  end
64
64
 
@@ -70,9 +70,9 @@ module Dynamoid
70
70
 
71
71
  @source.generated_methods.module_eval do
72
72
  alias_method alias_name, name
73
- alias_method "#{alias_name}=", "#{name}="
74
- alias_method "#{alias_name}?", "#{name}?"
75
- alias_method "#{alias_name}_before_type_cast", "#{name}_before_type_cast"
73
+ alias_method :"#{alias_name}=", :"#{name}="
74
+ alias_method :"#{alias_name}?", :"#{name}?"
75
+ alias_method :"#{alias_name}_before_type_cast", :"#{name}_before_type_cast"
76
76
  end
77
77
  end
78
78
 
@@ -164,7 +164,7 @@ module Dynamoid
164
164
  #
165
165
  # @param name [Symbol] a range key attribute name
166
166
  # @param type [Symbol] a range key type (optional)
167
- # @param options [Symbol] type options (optional)
167
+ # @param options [Hash] type options (optional)
168
168
  def range(name, type = :string, options = {})
169
169
  field(name, type, options)
170
170
  self.range_key = name
@@ -263,10 +263,8 @@ module Dynamoid
263
263
 
264
264
  # @private
265
265
  def generated_methods
266
- @generated_methods ||= begin
267
- Module.new.tap do |mod|
268
- include(mod)
269
- end
266
+ @generated_methods ||= Module.new.tap do |mod|
267
+ include(mod)
270
268
  end
271
269
  end
272
270
  end
@@ -362,7 +360,7 @@ module Dynamoid
362
360
  seconds = options[:after]
363
361
 
364
362
  if self[name].blank?
365
- send("#{name}=", Time.now.to_i + seconds)
363
+ send(:"#{name}=", Time.now.to_i + seconds)
366
364
  end
367
365
  end
368
366
  end
@@ -374,12 +372,12 @@ module Dynamoid
374
372
 
375
373
  type = self.class.inheritance_field
376
374
  if self.class.attributes[type] && send(type).nil?
377
- send("#{type}=", self.class.sti_name)
375
+ send(:"#{type}=", self.class.sti_name)
378
376
  end
379
377
  end
380
378
 
381
379
  def attribute_is_present_on_model?(attribute_name)
382
- setter = "#{attribute_name}=".to_sym
380
+ setter = :"#{attribute_name}="
383
381
  respond_to?(setter)
384
382
  end
385
383
  end
@@ -6,17 +6,6 @@ module Dynamoid
6
6
  module Finders
7
7
  extend ActiveSupport::Concern
8
8
 
9
- # @private
10
- RANGE_MAP = {
11
- 'gt' => :range_greater_than,
12
- 'lt' => :range_less_than,
13
- 'gte' => :range_gte,
14
- 'lte' => :range_lte,
15
- 'begins_with' => :range_begins_with,
16
- 'between' => :range_between,
17
- 'eq' => :range_eq
18
- }.freeze
19
-
20
9
  module ClassMethods
21
10
  # Find one or many objects, specified by one id or an array of ids.
22
11
  #
@@ -253,7 +242,6 @@ module Dynamoid
253
242
  range = options[:range] || {}
254
243
  hash_key_field, hash_key_value = hash.first
255
244
  range_key_field, range_key_value = range.first
256
- range_op_mapped = nil
257
245
 
258
246
  if range_key_field
259
247
  range_key_field = range_key_field.to_s
@@ -261,27 +249,30 @@ module Dynamoid
261
249
  if range_key_field.include?('.')
262
250
  range_key_field, range_key_op = range_key_field.split('.', 2)
263
251
  end
264
- range_op_mapped = RANGE_MAP.fetch(range_key_op)
265
252
  end
266
253
 
267
254
  # Find the index
268
255
  index = find_index(hash_key_field, range_key_field)
269
256
  raise Dynamoid::Errors::MissingIndex, "attempted to find #{[hash_key_field, range_key_field]}" if index.nil?
270
257
 
271
- # query
272
- opts = {
273
- hash_key: hash_key_field.to_s,
274
- hash_value: hash_key_value,
275
- index_name: index.name
276
- }
258
+ # Query
259
+ query_key_conditions = {}
260
+ query_key_conditions[hash_key_field.to_sym] = [[:eq, hash_key_value]]
277
261
  if range_key_field
278
- opts[:range_key] = range_key_field
279
- opts[range_op_mapped] = range_key_value
280
- end
281
- dynamo_options = opts.merge(options.reject { |key, _| key == :range })
282
- Dynamoid.adapter.query(table_name, dynamo_options).flat_map { |i| i }.map do |item|
283
- from_database(item)
262
+ query_key_conditions[range_key_field.to_sym] = [[range_key_op.to_sym, range_key_value]]
284
263
  end
264
+
265
+ query_non_key_conditions = options
266
+ .except(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
267
+ .except(:range)
268
+ .symbolize_keys
269
+
270
+ query_options = options.slice(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
271
+ query_options[:index_name] = index.name
272
+
273
+ Dynamoid.adapter.query(table_name, query_key_conditions, query_non_key_conditions, query_options)
274
+ .flat_map { |i| i }
275
+ .map { |item| from_database(item) }
285
276
  end
286
277
 
287
278
  # Find using exciting method_missing finders attributes. Uses criteria
@@ -308,7 +299,7 @@ module Dynamoid
308
299
  chain = Dynamoid::Criteria::Chain.new(self)
309
300
  chain = chain.where({}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } })
310
301
 
311
- if finder =~ /all/
302
+ if finder.include?('all')
312
303
  chain.all
313
304
  else
314
305
  chain.first