|
3 | 3 | module Dynamoid |
4 | 4 | class TransactionWrite |
5 | 5 | class ItemUpdater |
6 | | - def initialize |
7 | | - @additions = {} |
8 | | - @deletions = {} |
9 | | - @removals = [] |
10 | | - end |
| 6 | + attr_reader :attributes_to_set, :attributes_to_add, :attributes_to_delete, :attributes_to_remove |
11 | 7 |
|
12 | | - def empty? |
13 | | - @additions.empty? && @deletions.empty? && @removals.empty? |
14 | | - end |
| 8 | + def initialize(model_class) |
| 9 | + @model_class = model_class |
15 | 10 |
|
16 | | - # adds to array of fields for use in REMOVE update expression |
17 | | - def remove(field) |
18 | | - @removals << field |
19 | | - end |
20 | | - |
21 | | - # increments a number or adds to a set, starts at 0 or [] if it doesn't yet exist |
22 | | - def add(values) |
23 | | - @additions.merge!(values) |
| 11 | + @attributes_to_set = {} |
| 12 | + @attributes_to_add = {} |
| 13 | + @attributes_to_delete = {} |
| 14 | + @attributes_to_remove = [] |
24 | 15 | end |
25 | 16 |
|
26 | | - # deletes a value or values from a set type or simply sets a field to nil |
27 | | - def delete(field_or_values) |
28 | | - if field_or_values.is_a?(Hash) |
29 | | - @deletions.merge!(field_or_values) |
30 | | - else |
31 | | - remove(field_or_values) |
32 | | - end |
33 | | - end |
34 | | - |
35 | | - def merge_update_expression(expression_attribute_names, expression_attribute_values, update_expression) |
36 | | - update_expression = set_additions(expression_attribute_values, update_expression) |
37 | | - update_expression = set_deletions(expression_attribute_values, update_expression) |
38 | | - expression_attribute_names, update_expression = set_removals(expression_attribute_names, update_expression) |
39 | | - [expression_attribute_names, update_expression] |
40 | | - end |
41 | | - |
42 | | - # adds all of the ADD statements to the update_expression and returns it |
43 | | - def set_additions(expression_attribute_values, update_expression) |
44 | | - return update_expression unless @additions.present? |
45 | | - |
46 | | - # ADD statements can be used to increment a counter: |
47 | | - # txn.update!(UserCount, "UserCount#Red", {}, options: {add: {record_count: 1}}) |
48 | | - add_keys = @additions.keys |
49 | | - add_values = transform_add_values(@additions) |
50 | | - |
51 | | - update_expression += build_add_expression(add_keys) |
52 | | - add_keys.each_with_index { |k, i| expression_attribute_values[":_a#{i}"] = add_values[k] } |
53 | | - update_expression |
| 17 | + def empty? |
| 18 | + [@attributes_to_set, @attributes_to_add, @attributes_to_delete, @attributes_to_remove].all?(&:empty?) |
54 | 19 | end |
55 | 20 |
|
56 | | - def transform_add_values(additions) |
57 | | - additions.transform_values do |v| |
58 | | - !v.is_a?(Set) && v.is_a?(Enumerable) ? Set.new(v) : v |
59 | | - end |
| 21 | + def set(attributes) |
| 22 | + validate_attribute_names!(attributes.keys) |
| 23 | + @attributes_to_set.merge!(attributes) |
60 | 24 | end |
61 | 25 |
|
62 | | - def build_add_expression(add_keys) |
63 | | - " ADD #{add_keys.each_with_index.map { |k, i| "#{k} :_a#{i}" }.join(', ')}" |
| 26 | + # adds to array of fields for use in REMOVE update expression |
| 27 | + def remove(*names) |
| 28 | + validate_attribute_names!(names) |
| 29 | + @attributes_to_remove += names |
64 | 30 | end |
65 | 31 |
|
66 | | - # adds all of the DELETE statements to the update_expression and returns it |
67 | | - def set_deletions(expression_attribute_values, update_expression) |
68 | | - return update_expression unless @deletions.present? |
69 | | - |
70 | | - update_expression += build_delete_expression(@deletions.keys) |
71 | | - delete_values = prepare_delete_values(@deletions) |
72 | | - |
73 | | - @deletions.keys.each_with_index do |key, index| |
74 | | - expression_attribute_values[":_d#{index}"] = delete_values[key] |
75 | | - end |
76 | | - |
77 | | - update_expression |
| 32 | + # increments a number or adds to a set, starts at 0 or [] if it doesn't yet exist |
| 33 | + def add(attributes) |
| 34 | + validate_attribute_names!(attributes.keys) |
| 35 | + @attributes_to_add.merge!(attributes) |
78 | 36 | end |
79 | 37 |
|
80 | | - def build_delete_expression(delete_keys) |
81 | | - " DELETE #{delete_keys.each_with_index.map { |key, index| "#{key} :_d#{index}" }.join(', ')}" |
| 38 | + # deletes a value or values from a set |
| 39 | + def delete(attributes) |
| 40 | + validate_attribute_names!(attributes.keys) |
| 41 | + @attributes_to_delete.merge!(attributes) |
82 | 42 | end |
83 | 43 |
|
84 | | - def prepare_delete_values(deletions) |
85 | | - deletions.transform_values(&method(:prepare_delete_values_transform)) |
86 | | - end |
| 44 | + private |
87 | 45 |
|
88 | | - def prepare_delete_values_transform(value) |
89 | | - if value.is_a?(Set) |
90 | | - value |
91 | | - else |
92 | | - Set.new(value.is_a?(Enumerable) ? value : [value]) |
| 46 | + def validate_attribute_names!(names) |
| 47 | + names.each do |name| |
| 48 | + unless @model_class.attributes[name] |
| 49 | + raise Dynamoid::Errors::UnknownAttribute.new(@model_class, name) |
| 50 | + end |
93 | 51 | end |
94 | 52 | end |
95 | | - |
96 | | - # adds all of the removals as a REMOVE clause |
97 | | - def set_removals(expression_attribute_names, update_expression) |
98 | | - return expression_attribute_names, update_expression unless @removals.present? |
99 | | - |
100 | | - update_expression += " REMOVE #{@removals.each_with_index.map { |_k, i| "#_r#{i}" }.join(', ')}" |
101 | | - expression_attribute_names = expression_attribute_names.merge( |
102 | | - @removals.each_with_index.map { |k, i| ["#_r#{i}", k.to_s] }.to_h |
103 | | - ) |
104 | | - [expression_attribute_names, update_expression] |
105 | | - end |
106 | 53 | end |
107 | 54 | end |
108 | 55 | end |
0 commit comments