u-case 5.5.0 → 5.6.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.
data/lib/micro/case.rb CHANGED
@@ -63,8 +63,11 @@ module Micro
63
63
  Proc.new { |arg| call(arg) }
64
64
  end
65
65
 
66
- def self.flow(*args)
67
- @__flow_use_cases = Cases::Utils.map_use_cases(args)
66
+ def self.flow(*args, transaction: nil, steps: nil)
67
+ ::Micro::Case.check.flow_steps_kwarg!(args.empty? ? nil : args, steps, "#{self.name}.flow")
68
+
69
+ @__flow_use_cases = Cases::Utils.map_use_cases(steps || args)
70
+ @__flow_transaction = transaction
68
71
  end
69
72
 
70
73
  def self.results(&block)
@@ -81,6 +84,19 @@ module Micro
81
84
  parent.respond_to?(:__results_contract__) ? parent.__results_contract__ : nil
82
85
  end
83
86
 
87
+ def self.transaction(with:)
88
+ ::Micro::Case.check.transaction_owner!(with)
89
+
90
+ @__transaction_class = with
91
+ end
92
+
93
+ def self.__transaction_class__
94
+ return @__transaction_class if defined?(@__transaction_class)
95
+
96
+ parent = superclass
97
+ parent.respond_to?(:__transaction_class__) ? parent.__transaction_class__ : nil
98
+ end
99
+
84
100
  class << self
85
101
  alias __call__ call
86
102
 
@@ -129,7 +145,17 @@ module Micro
129
145
 
130
146
  self.class_eval('def use_cases; self.class.use_cases; end')
131
147
 
132
- @__flow = __flow_builder__.build(args)
148
+ @__flow = __flow_builder__.build(args, transaction: __resolved_flow_transaction)
149
+ end
150
+
151
+ private_class_method def self.__flow_transaction
152
+ return @__flow_transaction if defined?(@__flow_transaction)
153
+ end
154
+
155
+ private_class_method def self.__resolved_flow_transaction
156
+ return __transaction_class__ || true if __flow_transaction == true
157
+
158
+ __flow_transaction
133
159
  end
134
160
 
135
161
  FLOW_STEP = 'Self'.freeze
@@ -301,15 +327,31 @@ module Micro
301
327
  @__result.__set__(is_success, value, type, self)
302
328
  end
303
329
 
304
- def transaction(adapter = :activerecord)
305
- raise NotImplementedError unless adapter == :activerecord
330
+ def transaction(adapter = nil, with: nil)
331
+ # Backward-compat shim for the pre-5.6.0 positional form:
332
+ # transaction(:activerecord) { ... }
333
+ # The `:activerecord` value was the only positional value the
334
+ # helper ever accepted on prior versions. Anything else raises.
335
+ if adapter
336
+ raise ArgumentError,
337
+ "transaction(#{adapter.inspect}) is not supported; use transaction(with: SomeARClass) or transaction without arguments" unless adapter == :activerecord
338
+ end
339
+
340
+ ::Micro::Case.check.transaction_owner!(with) if with
341
+
342
+ owner = with || self.class.__transaction_class__
343
+
344
+ if owner.nil?
345
+ ::Micro::Case.check.activerecord_loaded!
346
+ owner = Config.instance.default_transaction_class.call
347
+ end
306
348
 
307
349
  result = nil
308
350
 
309
- ActiveRecord::Base.transaction do
351
+ owner.transaction do
310
352
  result = yield
311
353
 
312
- raise ActiveRecord::Rollback if result.failure?
354
+ raise ::ActiveRecord::Rollback if result.failure?
313
355
  end
314
356
 
315
357
  result
@@ -7,6 +7,15 @@ module Micro
7
7
  class InvalidUseCases < ArgumentError
8
8
  def initialize; super('argument must be a collection of `Micro::Case` classes'.freeze); end
9
9
  end
10
+
11
+ class TransactionAdapterMissing < RuntimeError
12
+ def initialize
13
+ super(
14
+ 'transaction: true requires ActiveRecord to be loaded. '\
15
+ "Add `require 'active_record'` (or `gem 'activerecord'` to your Gemfile) before invoking the flow.".freeze
16
+ )
17
+ end
18
+ end
10
19
  end
11
20
 
12
21
  end
@@ -8,30 +8,31 @@ module Micro
8
8
 
9
9
  attr_reader :use_cases
10
10
 
11
- def self.build(args)
11
+ def self.build(args, transaction: nil)
12
12
  use_cases = Utils.map_use_cases(args)
13
13
 
14
14
  ::Micro::Case.check.flow_use_cases!(use_cases)
15
15
 
16
- new(use_cases)
16
+ new(use_cases, transaction: transaction)
17
17
  end
18
18
 
19
- def initialize(use_cases)
19
+ def initialize(use_cases, transaction: nil)
20
20
  @use_cases = use_cases.dup.freeze
21
21
  @next_ones = use_cases.dup
22
22
  @first = @next_ones.shift
23
+ @transaction = ::Micro::Case.check.transaction_kwarg!(transaction)
23
24
  end
24
25
 
25
26
  def inspect
26
- '#<(%s) use_cases=%s>' % [self.class, @use_cases]
27
+ return '#<(%s) use_cases=%s>' % [self.class, @use_cases] unless @transaction
28
+
29
+ '#<(%s) transaction=%p use_cases=%s>' % [self.class, @transaction, @use_cases]
27
30
  end
28
31
 
29
32
  def call!(input:, result:)
30
- first_result = __call_use_case(@first, result, input)
31
-
32
- return first_result if @next_ones.empty?
33
+ return __call_steps(input, result) unless @transaction
33
34
 
34
- __call_next_use_cases(first_result)
35
+ __wrap_in_transaction { __call_steps(input, result) }
35
36
  end
36
37
 
37
38
  def call(input = Kind::Empty::HASH)
@@ -75,6 +76,43 @@ module Micro
75
76
  raise Case::Error::InvalidInvocationOfTheThenMethod.new("#{self.class.name}#")
76
77
  end
77
78
 
79
+ def __call_steps(input, result)
80
+ first_result = __call_use_case(@first, result, input)
81
+
82
+ return first_result if @next_ones.empty?
83
+
84
+ __call_next_use_cases(first_result)
85
+ end
86
+
87
+ def __wrap_in_transaction
88
+ owner = __transaction_owner
89
+
90
+ result = nil
91
+
92
+ owner.transaction do
93
+ result = yield
94
+
95
+ raise ::ActiveRecord::Rollback if result.failure?
96
+ end
97
+
98
+ result
99
+ end
100
+
101
+ def __transaction_owner
102
+ return @transaction if @transaction.is_a?(Class)
103
+
104
+ callback = ::Micro::Case::Config.instance.default_transaction_class
105
+
106
+ # Only the gem's default callback (`-> { ActiveRecord::Base }`)
107
+ # needs the AR-loaded guard. A user-supplied callback can
108
+ # return whatever class they want — we trust it.
109
+ if callback.equal?(::Micro::Case::Config::DEFAULT_TRANSACTION_CLASS_CALLBACK)
110
+ ::Micro::Case.check.activerecord_loaded!
111
+ end
112
+
113
+ callback.call
114
+ end
115
+
78
116
  def __call_use_case(use_case, result, input)
79
117
  __build_use_case(use_case, result, input).__call__
80
118
  end
data/lib/micro/cases.rb CHANGED
@@ -8,16 +8,24 @@ require 'micro/cases/map'
8
8
 
9
9
  module Micro
10
10
  module Cases
11
- def self.flow(args)
12
- Flow.build(args)
11
+ def self.flow(args = nil, transaction: nil, steps: nil)
12
+ args = nil if args.is_a?(Array) && args.empty?
13
+
14
+ ::Micro::Case.check.flow_steps_kwarg!(args, steps, 'Micro::Cases.flow')
15
+
16
+ Flow.build(steps || args, transaction: transaction)
13
17
  end
14
18
 
15
- def self.safe_flow(args)
19
+ def self.safe_flow(args = nil, transaction: nil, steps: nil)
16
20
  if Case::Config.instance.disable_safe_features
17
21
  raise Case::Error::SafeFeaturesDisabled.new('Micro::Cases.safe_flow')
18
22
  end
19
23
 
20
- Safe::Flow.build(args)
24
+ args = nil if args.is_a?(Array) && args.empty?
25
+
26
+ ::Micro::Case.check.flow_steps_kwarg!(args, steps, 'Micro::Cases.safe_flow')
27
+
28
+ Safe::Flow.build(steps || args, transaction: transaction)
21
29
  end
22
30
 
23
31
  def self.map(args)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-case
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura