Skip to content

Commit e5fad38

Browse files
authored
Define transaction methods in extension modules only (#33)
Rather than the extension modules defining `#transaction` on the operation class itself, define it within the modules themselves, which still makes it available on the operation class. Doing this allows the user to be able to define their _own_ `#transaction` on their operation classes, and then call `super` to get to the method provided by the module, which is the standard behaviour for modules and Ruby method lookups.
1 parent 63b431c commit e5fad38

File tree

3 files changed

+60
-59
lines changed

3 files changed

+60
-59
lines changed

lib/dry/operation/extensions/active_record.rb

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -96,33 +96,34 @@ def initialize(connection, **options)
9696
@options = options
9797
end
9898

99-
def included(klass)
100-
class_exec(@connection, @options) do |default_connection, options|
101-
# @!method transaction(connection = ActiveRecord::Base, **options, &steps)
102-
# Wrap the given steps in an ActiveRecord transaction.
103-
#
104-
# If any of the steps returns a `Dry::Monads::Result::Failure`, the
105-
# transaction will be rolled back and `:halt` will be thrown with the
106-
# failure as its value.
107-
#
108-
# @param connection [#transaction] The class/object to use
109-
# @param options [Hash] Additional options for the ActiveRecord transaction
110-
# @yieldreturn [Object] the result of the block
111-
# @see Dry::Operation#steps
112-
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction
113-
klass.define_method(:transaction) do |connection = default_connection, **opts, &steps|
114-
intercepting_failure do
115-
result = nil
116-
connection.transaction(**options.merge(opts)) do
117-
intercepting_failure(->(failure) {
118-
result = failure
119-
raise ::ActiveRecord::Rollback
120-
}) do
121-
result = steps.()
122-
end
99+
def included(_klass)
100+
default_connection = @connection
101+
default_options = @options
102+
103+
# @!method transaction(connection = ActiveRecord::Base, **options, &steps)
104+
# Wrap the given steps in an ActiveRecord transaction.
105+
#
106+
# If any of the steps returns a `Dry::Monads::Result::Failure`, the
107+
# transaction will be rolled back and `:halt` will be thrown with the
108+
# failure as its value.
109+
#
110+
# @param connection [#transaction] The class/object to use
111+
# @param options [Hash] Additional options for the ActiveRecord transaction
112+
# @yieldreturn [Object] the result of the block
113+
# @see Dry::Operation#steps
114+
# @see https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction
115+
define_method(:transaction) do |connection = default_connection, **opts, &steps|
116+
intercepting_failure do
117+
result = nil
118+
connection.transaction(**default_options.merge(opts)) do
119+
intercepting_failure(->(failure) {
120+
result = failure
121+
raise ::ActiveRecord::Rollback
122+
}) do
123+
result = steps.()
123124
end
124-
result
125125
end
126+
result
126127
end
127128
end
128129
end

lib/dry/operation/extensions/rom.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,26 @@ def initialize(gateway:)
9595
@gateway = gateway
9696
end
9797

98-
def included(klass)
99-
class_exec(@gateway) do |default_gateway|
100-
klass.define_method(:transaction) do |gateway: default_gateway, &steps|
101-
raise Dry::Operation::ExtensionError, <<~MSG unless respond_to?(:rom)
102-
When using the ROM extension, you need to define a #rom method \
103-
that returns the ROM container
104-
MSG
98+
def included(_klass)
99+
default_gateway = @gateway
105100

106-
intercepting_failure do
107-
result = nil
108-
rom.gateways[gateway].transaction do |t|
109-
intercepting_failure(->(failure) {
110-
result = failure
111-
t.rollback!
112-
}) do
113-
result = steps.()
114-
end
101+
define_method(:transaction) do |gateway: default_gateway, &steps|
102+
raise Dry::Operation::ExtensionError, <<~MSG unless respond_to?(:rom)
103+
When using the ROM extension, you need to define a #rom method \
104+
that returns the ROM container
105+
MSG
106+
107+
intercepting_failure do
108+
result = nil
109+
rom.gateways[gateway].transaction do |t|
110+
intercepting_failure(->(failure) {
111+
result = failure
112+
t.rollback!
113+
}) do
114+
result = steps.()
115115
end
116-
result
117116
end
117+
result
118118
end
119119
end
120120
end

lib/dry/operation/extensions/sequel.rb

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,26 @@ def initialize(**options)
8181
@options = options
8282
end
8383

84-
def included(klass)
85-
class_exec(@options) do |default_options|
86-
klass.define_method(:transaction) do |**opts, &steps|
87-
raise Dry::Operation::ExtensionError, <<~MSG unless respond_to?(:db)
88-
When using the Sequel extension, you need to define a #db method \
89-
that returns the Sequel database object
90-
MSG
84+
def included(_klass)
85+
default_options = @options
9186

92-
intercepting_failure do
93-
result = nil
94-
db.transaction(**default_options.merge(opts)) do
95-
intercepting_failure(->(failure) {
96-
result = failure
97-
raise ::Sequel::Rollback
98-
}) do
99-
result = steps.()
100-
end
87+
define_method(:transaction) do |**opts, &steps|
88+
raise Dry::Operation::ExtensionError, <<~MSG unless respond_to?(:db)
89+
When using the Sequel extension, you need to define a #db method \
90+
that returns the Sequel database object
91+
MSG
92+
93+
intercepting_failure do
94+
result = nil
95+
db.transaction(**default_options.merge(opts)) do
96+
intercepting_failure(->(failure) {
97+
result = failure
98+
raise ::Sequel::Rollback
99+
}) do
100+
result = steps.()
101101
end
102-
result
103102
end
103+
result
104104
end
105105
end
106106
end

0 commit comments

Comments
 (0)