u-case 5.5.0 → 5.7.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 +24 -0
- data/README.md +563 -2
- data/README.pt-BR.md +581 -2
- data/lib/micro/case/check.rb +71 -0
- data/lib/micro/case/config.rb +16 -0
- data/lib/micro/case/result.rb +26 -0
- data/lib/micro/case/version.rb +1 -1
- data/lib/micro/case.rb +49 -7
- data/lib/micro/cases/error.rb +9 -0
- data/lib/micro/cases/flow.rb +46 -8
- data/lib/micro/cases.rb +12 -4
- metadata +1 -1
data/lib/micro/case/check.rb
CHANGED
|
@@ -60,6 +60,67 @@ module Micro
|
|
|
60
60
|
Kind::Hash[arg]
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
def flow_steps_kwarg!(args, steps, label)
|
|
64
|
+
return unless args && steps
|
|
65
|
+
|
|
66
|
+
raise ArgumentError,
|
|
67
|
+
"#{label} accepts a positional collection OR `steps:`, not both"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def transaction_kwarg!(value)
|
|
71
|
+
return nil if value.nil? || value == false
|
|
72
|
+
return true if value == true
|
|
73
|
+
|
|
74
|
+
if value.is_a?(Class)
|
|
75
|
+
transaction_owner!(value)
|
|
76
|
+
return value
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if value.is_a?(Hash)
|
|
80
|
+
extra = value.keys - [:with]
|
|
81
|
+
|
|
82
|
+
raise ArgumentError,
|
|
83
|
+
"transaction: unsupported key(s) #{extra.inspect} (only `:with` is accepted)" unless extra.empty?
|
|
84
|
+
|
|
85
|
+
with = value[:with]
|
|
86
|
+
transaction_owner!(with)
|
|
87
|
+
|
|
88
|
+
return with
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
raise ArgumentError,
|
|
92
|
+
"transaction: #{value.inspect} is not supported (accepts `true`, `false`, `nil`, or `{ with: SomeARClass }`)"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def activerecord_loaded!
|
|
96
|
+
return if defined?(::ActiveRecord::Base)
|
|
97
|
+
|
|
98
|
+
raise ::Micro::Cases::Error::TransactionAdapterMissing
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Validates a transaction owner class. We accept Class instances
|
|
102
|
+
# only; the AR-subclass check is enforced if (and only if)
|
|
103
|
+
# ActiveRecord is already loaded — otherwise we defer to runtime
|
|
104
|
+
# so that load-order quirks (Rails initializers running before
|
|
105
|
+
# the AR autoload) don't break class-eval-time declarations.
|
|
106
|
+
def transaction_owner!(klass)
|
|
107
|
+
raise ArgumentError,
|
|
108
|
+
"transaction owner #{klass.inspect} must be a subclass of ActiveRecord::Base" unless klass.is_a?(Class)
|
|
109
|
+
|
|
110
|
+
return unless defined?(::ActiveRecord::Base)
|
|
111
|
+
return if klass <= ::ActiveRecord::Base
|
|
112
|
+
|
|
113
|
+
raise ArgumentError,
|
|
114
|
+
"transaction owner #{klass.inspect} must be a subclass of ActiveRecord::Base"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def transaction_class_callback!(callable)
|
|
118
|
+
return if callable.respond_to?(:call)
|
|
119
|
+
|
|
120
|
+
raise ArgumentError,
|
|
121
|
+
"Micro::Case.config.default_transaction_class= expects a callable (a block, lambda or proc), got #{callable.inspect}"
|
|
122
|
+
end
|
|
123
|
+
|
|
63
124
|
def results_contract!(use_case_class, kind, type, value)
|
|
64
125
|
contract = use_case_class.__results_contract__
|
|
65
126
|
return unless contract
|
|
@@ -108,6 +169,16 @@ module Micro
|
|
|
108
169
|
def flow_use_cases!(_use_cases); end
|
|
109
170
|
def map_args!(_args); end
|
|
110
171
|
def hash!(arg); arg; end
|
|
172
|
+
def flow_steps_kwarg!(_args, _steps, _label); end
|
|
173
|
+
def transaction_kwarg!(value)
|
|
174
|
+
return true if value == true
|
|
175
|
+
return value if value.is_a?(Class)
|
|
176
|
+
return value[:with] if value.is_a?(Hash) && value[:with].is_a?(Class)
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
def activerecord_loaded!; end
|
|
180
|
+
def transaction_owner!(_klass); end
|
|
181
|
+
def transaction_class_callback!(_callable); end
|
|
111
182
|
def results_contract!(_use_case_class, _kind, _type, _value); end
|
|
112
183
|
end
|
|
113
184
|
end
|
data/lib/micro/case/config.rb
CHANGED
|
@@ -52,6 +52,22 @@ module Micro
|
|
|
52
52
|
|
|
53
53
|
@activemodel_validation_errors_failure = :invalid_attributes
|
|
54
54
|
end
|
|
55
|
+
|
|
56
|
+
DEFAULT_TRANSACTION_CLASS_CALLBACK = -> { ::ActiveRecord::Base }.freeze
|
|
57
|
+
|
|
58
|
+
def default_transaction_class=(callable)
|
|
59
|
+
::Micro::Case.check.transaction_class_callback!(callable)
|
|
60
|
+
|
|
61
|
+
@default_transaction_class = callable
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def default_transaction_class(&block)
|
|
65
|
+
return self.default_transaction_class = block if block
|
|
66
|
+
|
|
67
|
+
return @default_transaction_class if defined?(@default_transaction_class)
|
|
68
|
+
|
|
69
|
+
DEFAULT_TRANSACTION_CLASS_CALLBACK
|
|
70
|
+
end
|
|
55
71
|
end
|
|
56
72
|
end
|
|
57
73
|
end
|
data/lib/micro/case/result.rb
CHANGED
|
@@ -50,6 +50,32 @@ module Micro
|
|
|
50
50
|
@__success ? :success : :failure
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def deconstruct
|
|
54
|
+
[@__success ? :success : :failure, type, data]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def deconstruct_keys(keys)
|
|
58
|
+
if keys.nil?
|
|
59
|
+
hash = { type: type, data: data, result: data, use_case: use_case, transitions: transitions }
|
|
60
|
+
hash[@__success ? :success : :failure] = type
|
|
61
|
+
return hash
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
hash = {}
|
|
65
|
+
keys.each do |key|
|
|
66
|
+
case key
|
|
67
|
+
when :type then hash[:type] = type
|
|
68
|
+
when :data then hash[:data] = data
|
|
69
|
+
when :result then hash[:result] = data
|
|
70
|
+
when :use_case then hash[:use_case] = use_case
|
|
71
|
+
when :transitions then hash[:transitions] = transitions
|
|
72
|
+
when :success then hash[:success] = type if @__success
|
|
73
|
+
when :failure then hash[:failure] = type if !@__success
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
hash
|
|
77
|
+
end
|
|
78
|
+
|
|
53
79
|
def [](key)
|
|
54
80
|
data[key]
|
|
55
81
|
end
|
data/lib/micro/case/version.rb
CHANGED
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
|
-
|
|
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 = :
|
|
305
|
-
|
|
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
|
-
|
|
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
|
data/lib/micro/cases/error.rb
CHANGED
|
@@ -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
|
data/lib/micro/cases/flow.rb
CHANGED
|
@@ -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
|
-
|
|
31
|
-
|
|
32
|
-
return first_result if @next_ones.empty?
|
|
33
|
+
return __call_steps(input, result) unless @transaction
|
|
33
34
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|