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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -7
- data/README.md +20 -23
- data/dynamoid.gemspec +1 -2
- data/lib/dynamoid/adapter.rb +18 -12
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +78 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +19 -1
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +38 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +33 -27
- data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +87 -62
- data/lib/dynamoid/associations/belongs_to.rb +6 -6
- data/lib/dynamoid/associations.rb +1 -1
- data/lib/dynamoid/config/options.rb +12 -12
- data/lib/dynamoid/config.rb +1 -0
- data/lib/dynamoid/criteria/chain.rb +95 -133
- data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
- data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
- data/lib/dynamoid/criteria/where_conditions.rb +29 -0
- data/lib/dynamoid/dirty.rb +1 -1
- data/lib/dynamoid/document.rb +1 -1
- data/lib/dynamoid/dumping.rb +2 -2
- data/lib/dynamoid/fields/declare.rb +6 -6
- data/lib/dynamoid/fields.rb +6 -8
- data/lib/dynamoid/finders.rb +17 -26
- data/lib/dynamoid/indexes.rb +6 -7
- data/lib/dynamoid/loadable.rb +2 -2
- data/lib/dynamoid/persistence/save.rb +12 -16
- data/lib/dynamoid/persistence/update_fields.rb +2 -2
- data/lib/dynamoid/persistence/update_validations.rb +1 -1
- data/lib/dynamoid/persistence.rb +39 -4
- data/lib/dynamoid/type_casting.rb +15 -14
- data/lib/dynamoid/undumping.rb +1 -1
- data/lib/dynamoid/version.rb +1 -1
- metadata +17 -16
- data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
- 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 :
|
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
|
-
@
|
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
|
26
|
-
@key_fields_detector = KeyFieldsDetector.new(@
|
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
|
-
|
106
|
+
@where_conditions.update(args.symbolize_keys)
|
111
107
|
|
112
|
-
# we should re-initialize keys detector every time we change
|
113
|
-
@key_fields_detector = KeyFieldsDetector.new(@
|
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 @
|
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,
|
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,
|
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(@
|
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).
|
455
|
-
# Post.where('views_count.gt' => 1000).
|
456
|
-
# Post.
|
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 &&
|
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,
|
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,
|
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: #{
|
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,
|
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,
|
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
|
610
|
-
name,
|
611
|
-
|
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
|
-
|
655
|
-
|
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
|
-
|
662
|
-
|
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
|
-
|
667
|
-
|
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
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
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
|
634
|
+
opts
|
681
635
|
end
|
682
636
|
|
683
|
-
def
|
684
|
-
opts
|
685
|
-
|
686
|
-
|
687
|
-
|
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
|
-
|
691
|
-
|
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
|
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
|
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
|
-
|
724
|
+
@where_conditions.update(sti_condition)
|
761
725
|
end
|
762
726
|
|
763
727
|
{}.tap do |opts|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
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
|
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(
|
9
|
-
@
|
10
|
-
@fields_with_operator =
|
11
|
-
@fields =
|
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(
|
28
|
-
@query = query
|
27
|
+
def initialize(where_conditions, source, forced_index_name: nil)
|
29
28
|
@source = source
|
30
|
-
@query = Query.new(
|
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
|
-
"
|
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
|
data/lib/dynamoid/dirty.rb
CHANGED
data/lib/dynamoid/document.rb
CHANGED
data/lib/dynamoid/dumping.rb
CHANGED
@@ -210,7 +210,7 @@ module Dynamoid
|
|
210
210
|
# datetime -> integer/string
|
211
211
|
class DateTimeDumper < Base
|
212
212
|
def process(value)
|
213
|
-
|
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
|
-
|
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
|
|
data/lib/dynamoid/fields.rb
CHANGED
@@ -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 [
|
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 ||=
|
267
|
-
|
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}="
|
380
|
+
setter = :"#{attribute_name}="
|
383
381
|
respond_to?(setter)
|
384
382
|
end
|
385
383
|
end
|
data/lib/dynamoid/finders.rb
CHANGED
@@ -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
|
-
#
|
272
|
-
|
273
|
-
|
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
|
-
|
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
|
302
|
+
if finder.include?('all')
|
312
303
|
chain.all
|
313
304
|
else
|
314
305
|
chain.first
|