dynamoid 3.10.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +182 -2
  4. data/dynamoid.gemspec +4 -4
  5. data/lib/dynamoid/adapter.rb +1 -1
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +53 -18
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +5 -4
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +9 -7
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +1 -1
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +1 -1
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/transact.rb +31 -0
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +9 -5
  13. data/lib/dynamoid/components.rb +1 -0
  14. data/lib/dynamoid/config.rb +2 -0
  15. data/lib/dynamoid/criteria/chain.rb +63 -18
  16. data/lib/dynamoid/criteria/where_conditions.rb +13 -6
  17. data/lib/dynamoid/dirty.rb +86 -11
  18. data/lib/dynamoid/dumping.rb +36 -14
  19. data/lib/dynamoid/errors.rb +14 -2
  20. data/lib/dynamoid/finders.rb +6 -6
  21. data/lib/dynamoid/loadable.rb +1 -0
  22. data/lib/dynamoid/persistence/inc.rb +6 -7
  23. data/lib/dynamoid/persistence/item_updater_with_casting_and_dumping.rb +36 -0
  24. data/lib/dynamoid/persistence/item_updater_with_dumping.rb +33 -0
  25. data/lib/dynamoid/persistence/save.rb +5 -2
  26. data/lib/dynamoid/persistence/update_fields.rb +5 -3
  27. data/lib/dynamoid/persistence/upsert.rb +5 -4
  28. data/lib/dynamoid/persistence.rb +38 -17
  29. data/lib/dynamoid/transaction_write/base.rb +47 -0
  30. data/lib/dynamoid/transaction_write/create.rb +49 -0
  31. data/lib/dynamoid/transaction_write/delete_with_instance.rb +60 -0
  32. data/lib/dynamoid/transaction_write/delete_with_primary_key.rb +59 -0
  33. data/lib/dynamoid/transaction_write/destroy.rb +79 -0
  34. data/lib/dynamoid/transaction_write/save.rb +164 -0
  35. data/lib/dynamoid/transaction_write/update_attributes.rb +46 -0
  36. data/lib/dynamoid/transaction_write/update_fields.rb +102 -0
  37. data/lib/dynamoid/transaction_write/upsert.rb +96 -0
  38. data/lib/dynamoid/transaction_write.rb +464 -0
  39. data/lib/dynamoid/type_casting.rb +3 -1
  40. data/lib/dynamoid/undumping.rb +13 -2
  41. data/lib/dynamoid/validations.rb +1 -1
  42. data/lib/dynamoid/version.rb +1 -1
  43. data/lib/dynamoid.rb +7 -0
  44. metadata +18 -5
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'item_updater_with_casting_and_dumping'
4
+
3
5
  module Dynamoid
4
6
  module Persistence
5
7
  # @private
@@ -32,11 +34,10 @@ module Dynamoid
32
34
 
33
35
  def update_item
34
36
  Dynamoid.adapter.update_item(@model_class.table_name, @partition_key, options_to_update_item) do |t|
35
- @attributes.each do |k, v|
36
- value_casted = TypeCasting.cast_field(v, @model_class.attributes[k])
37
- value_dumped = Dumping.dump_field(value_casted, @model_class.attributes[k])
37
+ item_updater = ItemUpdaterWithCastingAndDumping.new(@model_class, t)
38
38
 
39
- t.set(k => value_dumped)
39
+ @attributes.each do |k, v|
40
+ item_updater.set(k => v)
40
41
  end
41
42
  end
42
43
  end
@@ -10,6 +10,7 @@ require 'dynamoid/persistence/upsert'
10
10
  require 'dynamoid/persistence/save'
11
11
  require 'dynamoid/persistence/inc'
12
12
  require 'dynamoid/persistence/update_validations'
13
+ require 'dynamoid/persistence/item_updater_with_dumping'
13
14
 
14
15
  # encoding: utf-8
15
16
  module Dynamoid
@@ -18,8 +19,9 @@ module Dynamoid
18
19
  module Persistence
19
20
  extend ActiveSupport::Concern
20
21
 
21
- attr_accessor :new_record
22
+ attr_accessor :new_record, :destroyed
22
23
  alias new_record? new_record
24
+ alias destroyed? destroyed
23
25
 
24
26
  # @private
25
27
  UNIX_EPOCH_DATE = Date.new(1970, 1, 1).freeze
@@ -166,7 +168,7 @@ module Dynamoid
166
168
 
167
169
  # Create a model.
168
170
  #
169
- # Initializes a new model and immediately saves it to DynamoDB.
171
+ # Initializes a new model and immediately saves it into DynamoDB.
170
172
  #
171
173
  # User.create(first_name: 'Mark', last_name: 'Tyler')
172
174
  #
@@ -174,7 +176,8 @@ module Dynamoid
174
176
  #
175
177
  # User.create([{ first_name: 'Alice' }, { first_name: 'Bob' }])
176
178
  #
177
- # Creates a model and pass it into a block to set other attributes.
179
+ # Instantiates a model and pass it into an optional block to set other
180
+ # attributes.
178
181
  #
179
182
  # User.create(first_name: 'Mark') do |u|
180
183
  # u.age = 21
@@ -182,7 +185,7 @@ module Dynamoid
182
185
  #
183
186
  # Validates model and runs callbacks.
184
187
  #
185
- # @param attrs [Hash|Array[Hash]] Attributes of the models
188
+ # @param attrs [Hash|Array<Hash>] Attributes of a model
186
189
  # @param block [Proc] Block to process a document after initialization
187
190
  # @return [Dynamoid::Document] The created document
188
191
  # @since 0.2.0
@@ -196,12 +199,28 @@ module Dynamoid
196
199
 
197
200
  # Create a model.
198
201
  #
199
- # Initializes a new object and immediately saves it to the Dynamoid.
202
+ # Initializes a new object and immediately saves it into DynamoDB.
203
+ #
204
+ # User.create!(first_name: 'Mark', last_name: 'Tyler')
205
+ #
200
206
  # Raises an exception +Dynamoid::Errors::DocumentNotValid+ if validation
201
- # failed. Accepts both Hash and Array of Hashes and can create several
207
+ # failed.
208
+ #
209
+ # Accepts both Hash and Array of Hashes and can create several
202
210
  # models.
203
211
  #
204
- # @param attrs [Hash|Array[Hash]] Attributes with which to create the object.
212
+ # User.create!([{ first_name: 'Alice' }, { first_name: 'Bob' }])
213
+ #
214
+ # Instantiates a model and pass it into an optional block to set other
215
+ # attributes.
216
+ #
217
+ # User.create!(first_name: 'Mark') do |u|
218
+ # u.age = 21
219
+ # end
220
+ #
221
+ # Validates model and runs callbacks.
222
+ #
223
+ # @param attrs [Hash|Array<Hash>] Attributes with which to create the object.
205
224
  # @param block [Proc] Block to process a document after initialization
206
225
  # @return [Dynamoid::Document] The created document
207
226
  # @since 0.2.0
@@ -348,7 +367,7 @@ module Dynamoid
348
367
  # an updated document back with one HTTP request.
349
368
  #
350
369
  # Raises a +Dynamoid::Errors::UnknownAttribute+ exception if any of the
351
- # attributes is not on the model
370
+ # attributes is not declared in the model class.
352
371
  #
353
372
  # @param hash_key_value [Scalar value] hash key
354
373
  # @param range_key_value [Scalar value] range key (optional)
@@ -404,7 +423,7 @@ module Dynamoid
404
423
  # @param hash_key_value [Scalar value] hash key
405
424
  # @param range_key_value [Scalar value] range key (optional)
406
425
  # @param counters [Hash] value to increase by
407
- # @option counters [true | Symbol | Array[Symbol]] :touch to update update_at attribute and optionally the specified ones
426
+ # @option counters [true | Symbol | Array<Symbol>] :touch to update update_at attribute and optionally the specified ones
408
427
  # @return [Model class] self
409
428
  def inc(hash_key_value, range_key_value = nil, counters)
410
429
  Inc.call(self, hash_key_value, range_key_value, counters)
@@ -490,7 +509,7 @@ module Dynamoid
490
509
  #
491
510
  # +save+ by default sets timestamps attributes - +created_at+ and
492
511
  # +updated_at+ when creates new model and updates +updated_at+ attribute
493
- # when update already existing one.
512
+ # when updates already existing one.
494
513
  #
495
514
  # Changing +updated_at+ attribute at updating a model can be skipped with
496
515
  # +touch: false+ option:
@@ -535,7 +554,9 @@ module Dynamoid
535
554
  end
536
555
 
537
556
  # Update multiple attributes at once, saving the object once the updates
538
- # are complete. Returns +true+ if saving is successful and +false+
557
+ # are complete.
558
+ #
559
+ # Returns +true+ if saving is successful and +false+
539
560
  # otherwise.
540
561
  #
541
562
  # user.update_attributes(age: 27, last_name: 'Tylor')
@@ -668,12 +689,12 @@ module Dynamoid
668
689
  update_item_options = options.merge(conditions: conditions)
669
690
 
670
691
  new_attrs = Dynamoid.adapter.update_item(table_name, hash_key, update_item_options) do |t|
671
- t.add(lock_version: 1) if self.class.attributes[:lock_version]
692
+ item_updater = ItemUpdaterWithDumping.new(self.class, t)
693
+
694
+ item_updater.add(lock_version: 1) if self.class.attributes[:lock_version]
672
695
 
673
696
  if self.class.timestamps_enabled?
674
- time_now = DateTime.now.in_time_zone(Time.zone)
675
- time_now_dumped = Dumping.dump_field(time_now, self.class.attributes[:updated_at])
676
- t.set(updated_at: time_now_dumped)
697
+ item_updater.set(updated_at: DateTime.now.in_time_zone(Time.zone))
677
698
  end
678
699
 
679
700
  yield t
@@ -804,7 +825,7 @@ module Dynamoid
804
825
  #
805
826
  # @param attribute [Symbol] attribute name
806
827
  # @param by [Numeric] value to add (optional)
807
- # @param touch [true | Symbol | Array[Symbol]] to update update_at attribute and optionally the specified ones
828
+ # @param touch [true | Symbol | Array<Symbol>] to update update_at attribute and optionally the specified ones
808
829
  # @return [Dynamoid::Document] self
809
830
  def increment!(attribute, by = 1, touch: nil)
810
831
  increment(attribute, by)
@@ -855,7 +876,7 @@ module Dynamoid
855
876
  #
856
877
  # @param attribute [Symbol] attribute name
857
878
  # @param by [Numeric] value to subtract (optional)
858
- # @param touch [true | Symbol | Array[Symbol]] to update update_at attribute and optionally the specified ones
879
+ # @param touch [true | Symbol | Array<Symbol>] to update update_at attribute and optionally the specified ones
859
880
  # @return [Dynamoid::Document] self
860
881
  def decrement!(attribute, by = 1, touch: nil)
861
882
  increment!(attribute, -by, touch: touch)
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dynamoid
4
+ class TransactionWrite
5
+ class Base
6
+ # Callback called at "initialization" or "registration" an action
7
+ # before changes are persisted. It's a proper place to validate
8
+ # a model or run callbacks
9
+ def on_registration
10
+ raise 'Not implemented'
11
+ end
12
+
13
+ # Callback called after changes are persisted.
14
+ # It's a proper place to mark changes in a model as applied.
15
+ def on_commit
16
+ raise 'Not implemented'
17
+ end
18
+
19
+ # Callback called when a transaction is rolled back.
20
+ # It's a proper place to undo changes made in after_... callbacks.
21
+ def on_rollback
22
+ raise 'Not implemented'
23
+ end
24
+
25
+ # Whether some callback aborted or canceled an action
26
+ def aborted?
27
+ raise 'Not implemented'
28
+ end
29
+
30
+ # Whether there are changes to persist, e.g. updating a model with no
31
+ # attribute changed is skipped.
32
+ def skipped?
33
+ raise 'Not implemented'
34
+ end
35
+
36
+ # Value returned to a user as an action result
37
+ def observable_by_user_result
38
+ raise 'Not implemented'
39
+ end
40
+
41
+ # Coresponding part of a final request body
42
+ def action_request
43
+ raise 'Not implemented'
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Dynamoid
6
+ class TransactionWrite
7
+ class Create < Base
8
+ def initialize(model_class, attributes = {}, **options, &block)
9
+ super()
10
+
11
+ @model = model_class.new(attributes)
12
+
13
+ if block
14
+ yield(@model)
15
+ end
16
+
17
+ @save_action = Save.new(@model, **options)
18
+ end
19
+
20
+ def on_registration
21
+ @save_action.on_registration
22
+ end
23
+
24
+ def on_commit
25
+ @save_action.on_commit
26
+ end
27
+
28
+ def on_rollback
29
+ @save_action.on_rollback
30
+ end
31
+
32
+ def aborted?
33
+ @save_action.aborted?
34
+ end
35
+
36
+ def skipped?
37
+ false
38
+ end
39
+
40
+ def observable_by_user_result
41
+ @model
42
+ end
43
+
44
+ def action_request
45
+ @save_action.action_request
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Dynamoid
6
+ class TransactionWrite
7
+ class DeleteWithInstance < Base
8
+ def initialize(model)
9
+ super()
10
+
11
+ @model = model
12
+ @model_class = model.class
13
+ end
14
+
15
+ def on_registration
16
+ validate_model!
17
+ end
18
+
19
+ def on_commit
20
+ @model.destroyed = true
21
+ end
22
+
23
+ def on_rollback; end
24
+
25
+ def aborted?
26
+ false
27
+ end
28
+
29
+ def skipped?
30
+ false
31
+ end
32
+
33
+ def observable_by_user_result
34
+ @model
35
+ end
36
+
37
+ def action_request
38
+ key = { @model_class.hash_key => @model.hash_key }
39
+
40
+ if @model_class.range_key?
41
+ key[@model_class.range_key] = @model.range_value
42
+ end
43
+
44
+ {
45
+ delete: {
46
+ key: key,
47
+ table_name: @model_class.table_name
48
+ }
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ def validate_model!
55
+ raise Dynamoid::Errors::MissingHashKey if @model.hash_key.nil?
56
+ raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Dynamoid
6
+ class TransactionWrite
7
+ class DeleteWithPrimaryKey < Base
8
+ def initialize(model_class, hash_key, range_key)
9
+ super()
10
+
11
+ @model_class = model_class
12
+ @hash_key = hash_key
13
+ @range_key = range_key
14
+ end
15
+
16
+ def on_registration
17
+ validate_primary_key!
18
+ end
19
+
20
+ def on_commit; end
21
+
22
+ def on_rollback; end
23
+
24
+ def aborted?
25
+ false
26
+ end
27
+
28
+ def skipped?
29
+ false
30
+ end
31
+
32
+ def observable_by_user_result
33
+ nil
34
+ end
35
+
36
+ def action_request
37
+ key = { @model_class.hash_key => @hash_key }
38
+
39
+ if @model_class.range_key?
40
+ key[@model_class.range_key] = @range_key
41
+ end
42
+
43
+ {
44
+ delete: {
45
+ key: key,
46
+ table_name: @model_class.table_name
47
+ }
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ def validate_primary_key!
54
+ raise Dynamoid::Errors::MissingHashKey if @hash_key.nil?
55
+ raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @range_key.nil?
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Dynamoid
6
+ class TransactionWrite
7
+ class Destroy < Base
8
+ def initialize(model, **options)
9
+ super()
10
+
11
+ @model = model
12
+ @options = options
13
+ @model_class = model.class
14
+ @aborted = false
15
+ end
16
+
17
+ def on_registration
18
+ validate_model!
19
+
20
+ @aborted = true
21
+ @model.run_callbacks(:destroy) do
22
+ @aborted = false
23
+ true
24
+ end
25
+
26
+ if @aborted && @options[:raise_error]
27
+ raise Dynamoid::Errors::RecordNotDestroyed, @model
28
+ end
29
+ end
30
+
31
+ def on_commit
32
+ return if @aborted
33
+
34
+ @model.destroyed = true
35
+ @model.run_callbacks(:commit)
36
+ end
37
+
38
+ def on_rollback
39
+ @model.run_callbacks(:rollback)
40
+ end
41
+
42
+ def aborted?
43
+ @aborted
44
+ end
45
+
46
+ def skipped?
47
+ false
48
+ end
49
+
50
+ def observable_by_user_result
51
+ return false if @aborted
52
+
53
+ @model
54
+ end
55
+
56
+ def action_request
57
+ key = { @model_class.hash_key => @model.hash_key }
58
+
59
+ if @model_class.range_key?
60
+ key[@model_class.range_key] = @model.range_value
61
+ end
62
+
63
+ {
64
+ delete: {
65
+ key: key,
66
+ table_name: @model_class.table_name
67
+ }
68
+ }
69
+ end
70
+
71
+ private
72
+
73
+ def validate_model!
74
+ raise Dynamoid::Errors::MissingHashKey if @model.hash_key.nil?
75
+ raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Dynamoid
6
+ class TransactionWrite
7
+ class Save < Base
8
+ def initialize(model, **options)
9
+ super()
10
+
11
+ @model = model
12
+ @model_class = model.class
13
+ @options = options
14
+
15
+ @aborted = false
16
+ @was_new_record = model.new_record?
17
+ @valid = nil
18
+ end
19
+
20
+ def on_registration
21
+ validate_model!
22
+
23
+ if @options[:validate] != false && !(@valid = @model.valid?)
24
+ if @options[:raise_error]
25
+ raise Dynamoid::Errors::DocumentNotValid, @model
26
+ else
27
+ @aborted = true
28
+ return
29
+ end
30
+ end
31
+
32
+ @aborted = true
33
+ callback_name = @was_new_record ? :create : :update
34
+
35
+ @model.run_callbacks(:save) do
36
+ @model.run_callbacks(callback_name) do
37
+ @model.run_callbacks(:validate) do
38
+ @aborted = false
39
+ true
40
+ end
41
+ end
42
+ end
43
+
44
+ if @aborted && @options[:raise_error]
45
+ raise Dynamoid::Errors::RecordNotSaved, @model
46
+ end
47
+
48
+ if @was_new_record && @model.hash_key.nil?
49
+ @model.hash_key = SecureRandom.uuid
50
+ end
51
+ end
52
+
53
+ def on_commit
54
+ return if @aborted
55
+
56
+ @model.changes_applied
57
+
58
+ if @was_new_record
59
+ @model.new_record = false
60
+ end
61
+
62
+ @model.run_callbacks(:commit)
63
+ end
64
+
65
+ def on_rollback
66
+ @model.run_callbacks(:rollback)
67
+ end
68
+
69
+ def aborted?
70
+ @aborted
71
+ end
72
+
73
+ def skipped?
74
+ @model.persisted? && !@model.changed?
75
+ end
76
+
77
+ def observable_by_user_result
78
+ !@aborted
79
+ end
80
+
81
+ def action_request
82
+ if @was_new_record
83
+ action_request_to_create
84
+ else
85
+ action_request_to_update
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def validate_model!
92
+ raise Dynamoid::Errors::MissingHashKey if !@was_new_record && @model.hash_key.nil?
93
+ raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key? && @model.range_value.nil?
94
+ end
95
+
96
+ def action_request_to_create
97
+ touch_model_timestamps(skip_created_at: false)
98
+
99
+ attributes_dumped = Dynamoid::Dumping.dump_attributes(@model.attributes, @model_class.attributes)
100
+
101
+ # require primary key not to exist yet
102
+ condition = "attribute_not_exists(#{@model_class.hash_key})"
103
+ if @model_class.range_key?
104
+ condition += " AND attribute_not_exists(#{@model_class.range_key})"
105
+ end
106
+
107
+ {
108
+ put: {
109
+ item: attributes_dumped,
110
+ table_name: @model_class.table_name,
111
+ condition_expression: condition
112
+ }
113
+ }
114
+ end
115
+
116
+ def action_request_to_update
117
+ touch_model_timestamps(skip_created_at: true)
118
+
119
+ # changed attributes to persist
120
+ changes = @model.attributes.slice(*@model.changed.map(&:to_sym))
121
+ changes_dumped = Dynamoid::Dumping.dump_attributes(changes, @model_class.attributes)
122
+
123
+ # primary key to look up an item to update
124
+ key = { @model_class.hash_key => @model.hash_key }
125
+ key[@model_class.range_key] = @model.range_value if @model_class.range_key?
126
+
127
+ # Build UpdateExpression and keep names and values placeholders mapping
128
+ # in ExpressionAttributeNames and ExpressionAttributeValues.
129
+ update_expression_statements = []
130
+ expression_attribute_names = {}
131
+ expression_attribute_values = {}
132
+
133
+ changes_dumped.each_with_index do |(name, value), i|
134
+ name_placeholder = "#_n#{i}"
135
+ value_placeholder = ":_s#{i}"
136
+
137
+ update_expression_statements << "#{name_placeholder} = #{value_placeholder}"
138
+ expression_attribute_names[name_placeholder] = name
139
+ expression_attribute_values[value_placeholder] = value
140
+ end
141
+
142
+ update_expression = "SET #{update_expression_statements.join(', ')}"
143
+
144
+ {
145
+ update: {
146
+ key: key,
147
+ table_name: @model_class.table_name,
148
+ update_expression: update_expression,
149
+ expression_attribute_names: expression_attribute_names,
150
+ expression_attribute_values: expression_attribute_values
151
+ }
152
+ }
153
+ end
154
+
155
+ def touch_model_timestamps(skip_created_at:)
156
+ return unless @model_class.timestamps_enabled?
157
+
158
+ timestamp = DateTime.now.in_time_zone(Time.zone)
159
+ @model.updated_at = timestamp unless @options[:touch] == false && !@was_new_record
160
+ @model.created_at ||= timestamp unless skip_created_at
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'dynamoid/persistence/update_validations'
5
+
6
+ module Dynamoid
7
+ class TransactionWrite
8
+ class UpdateAttributes < Base
9
+ def initialize(model, attributes, **options)
10
+ super()
11
+
12
+ @model = model
13
+ @model.assign_attributes(attributes)
14
+ @save_action = Save.new(model, **options)
15
+ end
16
+
17
+ def on_registration
18
+ @save_action.on_registration
19
+ end
20
+
21
+ def on_commit
22
+ @save_action.on_commit
23
+ end
24
+
25
+ def on_rollback
26
+ @save_action.on_rollback
27
+ end
28
+
29
+ def aborted?
30
+ @save_action.aborted?
31
+ end
32
+
33
+ def skipped?
34
+ @save_action.skipped?
35
+ end
36
+
37
+ def observable_by_user_result
38
+ @save_action.observable_by_user_result
39
+ end
40
+
41
+ def action_request
42
+ @save_action.action_request
43
+ end
44
+ end
45
+ end
46
+ end