hubbado-sequence 0.3.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 +7 -0
- data/CHANGELOG.md +166 -0
- data/LICENSE +21 -0
- data/README.md +562 -0
- data/config/locales/en.yml +8 -0
- data/hubbado-sequence.gemspec +41 -0
- data/lib/hubbado/sequence/controls/contract.rb +45 -0
- data/lib/hubbado/sequence/controls/model.rb +33 -0
- data/lib/hubbado/sequence/controls/policy.rb +48 -0
- data/lib/hubbado/sequence/controls.rb +3 -0
- data/lib/hubbado/sequence/ctx.rb +19 -0
- data/lib/hubbado/sequence/errors.rb +16 -0
- data/lib/hubbado/sequence/macros/contract/build.rb +48 -0
- data/lib/hubbado/sequence/macros/contract/deserialize.rb +43 -0
- data/lib/hubbado/sequence/macros/contract/persist.rb +50 -0
- data/lib/hubbado/sequence/macros/contract/validate.rb +53 -0
- data/lib/hubbado/sequence/macros/model/build.rb +57 -0
- data/lib/hubbado/sequence/macros/model/find.rb +59 -0
- data/lib/hubbado/sequence/macros/policy/check.rb +65 -0
- data/lib/hubbado/sequence/path.rb +35 -0
- data/lib/hubbado/sequence/pipeline.rb +141 -0
- data/lib/hubbado/sequence/result.rb +83 -0
- data/lib/hubbado/sequence/run_sequence.rb +26 -0
- data/lib/hubbado/sequence/runner.rb +184 -0
- data/lib/hubbado/sequence/sequencer.rb +109 -0
- data/lib/hubbado/sequence.rb +30 -0
- metadata +227 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Sequence
|
|
3
|
+
class Result
|
|
4
|
+
FRAMEWORK_I18N_SCOPE = "sequence.errors".freeze
|
|
5
|
+
|
|
6
|
+
attr_reader :ctx
|
|
7
|
+
attr_reader :error
|
|
8
|
+
attr_reader :trail
|
|
9
|
+
attr_reader :i18n_scope
|
|
10
|
+
|
|
11
|
+
def self.ok(ctx, trail: [], i18n_scope: nil)
|
|
12
|
+
new(:ok, ctx, error: nil, trail: trail, i18n_scope: i18n_scope)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.fail(ctx, error:, trail: [], i18n_scope: nil)
|
|
16
|
+
unless error.is_a?(Hash) && error[:code]
|
|
17
|
+
raise ArgumentError, "Result.fail requires error: { code: ... }"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
new(:fail, ctx, error: error, trail: trail, i18n_scope: i18n_scope)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(status, ctx, error:, trail:, i18n_scope:)
|
|
24
|
+
@status = status
|
|
25
|
+
@ctx = ctx
|
|
26
|
+
@error = error
|
|
27
|
+
@trail = trail
|
|
28
|
+
@i18n_scope = i18n_scope
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def ok?
|
|
32
|
+
@status == :ok
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def failure?
|
|
36
|
+
@status == :fail
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def with_trail(trail)
|
|
40
|
+
self.class.new(@status, @ctx, error: @error, trail: trail, i18n_scope: @i18n_scope)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def with_i18n_scope(scope)
|
|
44
|
+
return self unless @i18n_scope.nil?
|
|
45
|
+
|
|
46
|
+
self.class.new(@status, @ctx, error: @error, trail: @trail, i18n_scope: scope)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def message
|
|
50
|
+
return nil if ok?
|
|
51
|
+
|
|
52
|
+
translation = translate_with_chain
|
|
53
|
+
return translation if translation
|
|
54
|
+
|
|
55
|
+
@error[:message] || humanize_code
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def translate_with_chain
|
|
61
|
+
scopes = []
|
|
62
|
+
scopes << @error[:i18n_scope] if @error[:i18n_scope]
|
|
63
|
+
scopes << @i18n_scope if @i18n_scope
|
|
64
|
+
scopes << FRAMEWORK_I18N_SCOPE
|
|
65
|
+
scopes.uniq!
|
|
66
|
+
|
|
67
|
+
key = @error[:i18n_key] || @error[:code]
|
|
68
|
+
args = @error[:i18n_args] || {}
|
|
69
|
+
|
|
70
|
+
scopes.each do |scope|
|
|
71
|
+
translated = ::I18n.t("#{scope}.#{key}", default: nil, **args)
|
|
72
|
+
return translated unless translated.nil?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def humanize_code
|
|
79
|
+
@error[:code].to_s.tr("_", " ").capitalize
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Sequence
|
|
3
|
+
# Mixed into controllers (or other top-level boundaries that cannot take
|
|
4
|
+
# injected dependencies because their lifecycle is owned by the framework).
|
|
5
|
+
# Wraps Runner so the calling site reads `run_sequence SomeSeq do |r| ... end`
|
|
6
|
+
# while a single `sequencer_arguments` override handles per-host kwargs
|
|
7
|
+
# injection (current_user, params, etc.).
|
|
8
|
+
module RunSequence
|
|
9
|
+
def run_sequence(sequencer_class, **kwargs, &block)
|
|
10
|
+
sequence_runner.(sequencer_class, **sequencer_arguments(kwargs), &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Override in the host (controller, job) to inject defaults like
|
|
14
|
+
# current_user without forcing every call site to pass them.
|
|
15
|
+
def sequencer_arguments(kwargs)
|
|
16
|
+
kwargs
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def sequence_runner
|
|
22
|
+
@sequence_runner ||= Runner.new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Sequence
|
|
3
|
+
# Invokes a sequencer and dispatches its Result to outcome blocks. Forgetting
|
|
4
|
+
# to handle a serious failure raises rather than silently swallowing it.
|
|
5
|
+
# Usable as a configurable dependency wherever a sequencer Result needs
|
|
6
|
+
# branch-style handling.
|
|
7
|
+
class Runner
|
|
8
|
+
configure :run_sequence
|
|
9
|
+
|
|
10
|
+
def self.build
|
|
11
|
+
new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(sequencer_class, **kwargs, &block)
|
|
15
|
+
result = sequencer_class.(**kwargs)
|
|
16
|
+
|
|
17
|
+
dispatch = Dispatch.new(sequencer_class, result)
|
|
18
|
+
block.call(dispatch) if block_given?
|
|
19
|
+
|
|
20
|
+
dispatch.enforce_safety_nets!
|
|
21
|
+
|
|
22
|
+
dispatch.returned
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Dispatch
|
|
26
|
+
include Hubbado::Log::Dependency
|
|
27
|
+
|
|
28
|
+
attr_reader :returned, :result, :sequencer_class
|
|
29
|
+
|
|
30
|
+
def initialize(sequencer_class, result)
|
|
31
|
+
@sequencer_class = sequencer_class
|
|
32
|
+
@result = result
|
|
33
|
+
@handled = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def success
|
|
37
|
+
return unless @result.ok?
|
|
38
|
+
execute { yield(@result.ctx) }
|
|
39
|
+
logger.info("Sequencer #{@sequencer_class.name} succeeded: #{trail_summary}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def policy_failed
|
|
43
|
+
return unless code == :forbidden
|
|
44
|
+
execute { yield(@result.ctx) }
|
|
45
|
+
logger.info("Sequencer #{@sequencer_class.name} policy failed at #{step_label} (#{code}): #{trail_summary}")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def not_found
|
|
49
|
+
return unless code == :not_found
|
|
50
|
+
execute { yield(@result.ctx) }
|
|
51
|
+
logger.info("Sequencer #{@sequencer_class.name} not found at #{step_label}: #{trail_summary}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validation_failed
|
|
55
|
+
return unless code == :validation_failed
|
|
56
|
+
execute { yield(@result.ctx) }
|
|
57
|
+
logger.info("Sequencer #{@sequencer_class.name} validation failed at #{step_label}: #{trail_summary}")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# otherwise deliberately does not catch policy denials or not_found —
|
|
61
|
+
# those have their own required handlers.
|
|
62
|
+
def otherwise
|
|
63
|
+
return if @result.ok?
|
|
64
|
+
return if code == :forbidden
|
|
65
|
+
return if code == :not_found
|
|
66
|
+
return if @handled
|
|
67
|
+
|
|
68
|
+
execute { yield(@result.ctx) }
|
|
69
|
+
logger.info("Sequencer #{@sequencer_class.name} failed at #{step_label} (#{code}): #{trail_summary}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def code
|
|
73
|
+
@result.error&.[](:code)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def handled?
|
|
77
|
+
@result.ok? || @handled
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def enforce_safety_nets!
|
|
81
|
+
return if handled?
|
|
82
|
+
|
|
83
|
+
log_unhandled
|
|
84
|
+
|
|
85
|
+
case code
|
|
86
|
+
when :forbidden
|
|
87
|
+
raise Errors::Unauthorized.new(
|
|
88
|
+
"#{@sequencer_class.name} denied: #{@result.message}",
|
|
89
|
+
@result
|
|
90
|
+
)
|
|
91
|
+
when :not_found
|
|
92
|
+
raise Errors::NotFound, "#{@sequencer_class.name} reported not_found"
|
|
93
|
+
else
|
|
94
|
+
raise Errors::Failed, "#{@sequencer_class.name} failed (#{code}): #{@result.message}"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def log_unhandled
|
|
99
|
+
logger.error("Sequencer #{@sequencer_class.name} failed unhandled at #{step_label} (#{code}): #{trail_summary}")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def execute
|
|
105
|
+
@handled = true
|
|
106
|
+
@returned = yield
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def trail_summary
|
|
110
|
+
@result.trail.empty? ? "(no steps)" : @result.trail.map(&:to_s).join(" → ")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def step_label
|
|
114
|
+
step = @result.error && @result.error[:step]
|
|
115
|
+
step ? step.inspect : "(unknown step)"
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
module Substitute
|
|
120
|
+
include RecordInvocation
|
|
121
|
+
|
|
122
|
+
def succeed_with(**ctx_writes)
|
|
123
|
+
@configured_outcome = { kind: :success, ctx_writes: ctx_writes }
|
|
124
|
+
self
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def policy_failure(**error_attrs)
|
|
128
|
+
configure_failure(:forbidden, error_attrs)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def not_found(**error_attrs)
|
|
132
|
+
configure_failure(:not_found, error_attrs)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def validation_failure(**error_attrs)
|
|
136
|
+
configure_failure(:validation_failed, error_attrs)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def other_error(code:, **error_attrs)
|
|
140
|
+
configure_failure(code, error_attrs)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def ran_with?(sequencer_class, **expected_kwargs)
|
|
144
|
+
records.any? do |invocation|
|
|
145
|
+
next false unless invocation.method_name == :call
|
|
146
|
+
next false unless invocation.arguments[:sequencer_class] == sequencer_class
|
|
147
|
+
|
|
148
|
+
captured = invocation.arguments[:kwargs] || {}
|
|
149
|
+
expected_kwargs.all? { |key, value| captured[key] == value }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
record def call(sequencer_class, **kwargs, &block)
|
|
154
|
+
dispatch = Dispatch.new(sequencer_class, build_result)
|
|
155
|
+
block.call(dispatch) if block_given?
|
|
156
|
+
dispatch.enforce_safety_nets!
|
|
157
|
+
dispatch.returned
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
private
|
|
161
|
+
|
|
162
|
+
def configure_failure(code, error_attrs)
|
|
163
|
+
@configured_outcome = {
|
|
164
|
+
kind: :failure,
|
|
165
|
+
error: { code: code, **error_attrs }
|
|
166
|
+
}
|
|
167
|
+
self
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def build_result
|
|
171
|
+
outcome = @configured_outcome || { kind: :success, ctx_writes: {} }
|
|
172
|
+
ctx = Hubbado::Sequence::Ctx.new
|
|
173
|
+
|
|
174
|
+
if outcome[:kind] == :success
|
|
175
|
+
outcome[:ctx_writes].each { |key, value| ctx[key] = value }
|
|
176
|
+
Hubbado::Sequence::Result.ok(ctx)
|
|
177
|
+
else
|
|
178
|
+
Hubbado::Sequence::Result.fail(ctx, error: outcome[:error])
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Sequence
|
|
3
|
+
module Sequencer
|
|
4
|
+
def self.included(cls)
|
|
5
|
+
cls.send(:include, ::Dependency)
|
|
6
|
+
cls.send(:include, ::Configure)
|
|
7
|
+
cls.extend(ClassMethods)
|
|
8
|
+
|
|
9
|
+
install_default_substitute(cls)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Each sequencer gets a default `Substitute` module so it can be used as a
|
|
13
|
+
# dependency without bespoke test scaffolding. The user can reopen the
|
|
14
|
+
# module in their class body to add per-sequencer assertions; the defaults
|
|
15
|
+
# below are always available.
|
|
16
|
+
def self.install_default_substitute(cls)
|
|
17
|
+
return if cls.const_defined?(:Substitute, false)
|
|
18
|
+
|
|
19
|
+
cls.const_set(:Substitute, build_default_substitute_module)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.build_default_substitute_module
|
|
23
|
+
Module.new do
|
|
24
|
+
include ::RecordInvocation
|
|
25
|
+
|
|
26
|
+
def succeed_with(**ctx_writes)
|
|
27
|
+
@configured_writes = ctx_writes
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fail_with(**error_attrs)
|
|
32
|
+
@configured_error = error_attrs
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
record def call(ctx)
|
|
37
|
+
return ::Hubbado::Sequence::Result.fail(ctx, error: @configured_error) if @configured_error
|
|
38
|
+
|
|
39
|
+
if @configured_writes
|
|
40
|
+
@configured_writes.each { |k, v| ctx[k] = v }
|
|
41
|
+
end
|
|
42
|
+
::Hubbado::Sequence::Result.ok(ctx)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def called?(**kwargs)
|
|
46
|
+
invoked?(:call, **kwargs)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
module ClassMethods
|
|
52
|
+
# Bridge between the kwargs boundary (controllers and other top-level
|
|
53
|
+
# callers) and the ctx-passing convention used inside the framework.
|
|
54
|
+
# A caller can supply either an existing Ctx (the nested-sequencer case)
|
|
55
|
+
# or keyword arguments that become the initial ctx (the outermost case).
|
|
56
|
+
def call(ctx = nil, **kwargs)
|
|
57
|
+
if ctx.nil?
|
|
58
|
+
ctx = Ctx.build(kwargs)
|
|
59
|
+
elsif !kwargs.empty?
|
|
60
|
+
raise ArgumentError, "#{name}.() takes either a Ctx or keyword arguments, not both"
|
|
61
|
+
elsif !ctx.is_a?(Ctx)
|
|
62
|
+
ctx = Ctx.build(ctx)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
build.call(ctx)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Default factory: a sequencer with no configurable dependencies needs
|
|
69
|
+
# nothing more than `new`. Sequencers that have dependencies override
|
|
70
|
+
# `self.build` to run the corresponding `Macro.configure(instance, …)`
|
|
71
|
+
# calls.
|
|
72
|
+
def build
|
|
73
|
+
new
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def i18n_scope
|
|
77
|
+
@i18n_scope ||= ::Casing::Underscore::String.(name).gsub('/', '.')
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def i18n_scope
|
|
82
|
+
self.class.i18n_scope
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def failure(ctx, **error_attrs)
|
|
86
|
+
Result.fail(ctx, error: error_attrs, i18n_scope: i18n_scope)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Builds a Pipeline that auto-dispatches blockless `step(:foo)` calls to
|
|
90
|
+
# `self.foo(ctx)`. Use this inside a sequencer's `call` body in place of
|
|
91
|
+
# `Pipeline.(ctx)` whenever steps are local methods.
|
|
92
|
+
#
|
|
93
|
+
# Block form (`pipeline(ctx) { |p| ... }`) yields the pipeline, runs the
|
|
94
|
+
# block, and returns the final Result — no trailing `.result` needed. The
|
|
95
|
+
# non-block form returns the Pipeline so chained `.step(...)...result`
|
|
96
|
+
# calls still work.
|
|
97
|
+
def pipeline(ctx, &block)
|
|
98
|
+
pipe = Pipeline.new(ctx, dispatcher: self)
|
|
99
|
+
|
|
100
|
+
if block
|
|
101
|
+
block.call(pipe)
|
|
102
|
+
pipe.result
|
|
103
|
+
else
|
|
104
|
+
pipe
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "i18n"
|
|
2
|
+
require "casing"
|
|
3
|
+
require "configure"; Configure.activate
|
|
4
|
+
require "dependency"; Dependency.activate
|
|
5
|
+
require "hubbado/log"
|
|
6
|
+
require "record_invocation"
|
|
7
|
+
require "template_method"; TemplateMethod.activate
|
|
8
|
+
|
|
9
|
+
I18n.load_path += Dir[File.expand_path("../../config/locales", __dir__) + "/*.yml"]
|
|
10
|
+
|
|
11
|
+
module Hubbado
|
|
12
|
+
module Sequence
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require "hubbado/sequence/ctx"
|
|
17
|
+
require "hubbado/sequence/path"
|
|
18
|
+
require "hubbado/sequence/result"
|
|
19
|
+
require "hubbado/sequence/pipeline"
|
|
20
|
+
require "hubbado/sequence/sequencer"
|
|
21
|
+
require "hubbado/sequence/macros/model/find"
|
|
22
|
+
require "hubbado/sequence/macros/model/build"
|
|
23
|
+
require "hubbado/sequence/macros/contract/build"
|
|
24
|
+
require "hubbado/sequence/macros/contract/deserialize"
|
|
25
|
+
require "hubbado/sequence/macros/contract/validate"
|
|
26
|
+
require "hubbado/sequence/macros/contract/persist"
|
|
27
|
+
require "hubbado/sequence/macros/policy/check"
|
|
28
|
+
require "hubbado/sequence/errors"
|
|
29
|
+
require "hubbado/sequence/runner"
|
|
30
|
+
require "hubbado/sequence/run_sequence"
|
metadata
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hubbado-sequence
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Hubbado Devs
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-14 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: evt-casing
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: evt-configure
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: evt-dependency
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: evt-record_invocation
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: evt-template_method
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: hubbado-log
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: i18n
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: debug
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: hubbado-style
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: rake
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: test_bench
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
description: Sequencer takes input, runs a sequence of steps, and returns a Result
|
|
168
|
+
indicating success or failure plus the working context that was built up during
|
|
169
|
+
execution.
|
|
170
|
+
email:
|
|
171
|
+
- devs@hubbado.com
|
|
172
|
+
executables: []
|
|
173
|
+
extensions: []
|
|
174
|
+
extra_rdoc_files: []
|
|
175
|
+
files:
|
|
176
|
+
- CHANGELOG.md
|
|
177
|
+
- LICENSE
|
|
178
|
+
- README.md
|
|
179
|
+
- config/locales/en.yml
|
|
180
|
+
- hubbado-sequence.gemspec
|
|
181
|
+
- lib/hubbado/sequence.rb
|
|
182
|
+
- lib/hubbado/sequence/controls.rb
|
|
183
|
+
- lib/hubbado/sequence/controls/contract.rb
|
|
184
|
+
- lib/hubbado/sequence/controls/model.rb
|
|
185
|
+
- lib/hubbado/sequence/controls/policy.rb
|
|
186
|
+
- lib/hubbado/sequence/ctx.rb
|
|
187
|
+
- lib/hubbado/sequence/errors.rb
|
|
188
|
+
- lib/hubbado/sequence/macros/contract/build.rb
|
|
189
|
+
- lib/hubbado/sequence/macros/contract/deserialize.rb
|
|
190
|
+
- lib/hubbado/sequence/macros/contract/persist.rb
|
|
191
|
+
- lib/hubbado/sequence/macros/contract/validate.rb
|
|
192
|
+
- lib/hubbado/sequence/macros/model/build.rb
|
|
193
|
+
- lib/hubbado/sequence/macros/model/find.rb
|
|
194
|
+
- lib/hubbado/sequence/macros/policy/check.rb
|
|
195
|
+
- lib/hubbado/sequence/path.rb
|
|
196
|
+
- lib/hubbado/sequence/pipeline.rb
|
|
197
|
+
- lib/hubbado/sequence/result.rb
|
|
198
|
+
- lib/hubbado/sequence/run_sequence.rb
|
|
199
|
+
- lib/hubbado/sequence/runner.rb
|
|
200
|
+
- lib/hubbado/sequence/sequencer.rb
|
|
201
|
+
homepage: https://github.com/hubbado/hubbado-sequence
|
|
202
|
+
licenses:
|
|
203
|
+
- MIT
|
|
204
|
+
metadata:
|
|
205
|
+
homepage_uri: https://github.com/hubbado/hubbado-sequence
|
|
206
|
+
source_code_uri: https://github.com/hubbado/hubbado-sequence
|
|
207
|
+
changelog_uri: https://github.com/hubbado/hubbado-sequence/blob/master/CHANGELOG.md
|
|
208
|
+
post_install_message:
|
|
209
|
+
rdoc_options: []
|
|
210
|
+
require_paths:
|
|
211
|
+
- lib
|
|
212
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
213
|
+
requirements:
|
|
214
|
+
- - ">="
|
|
215
|
+
- !ruby/object:Gem::Version
|
|
216
|
+
version: '3.3'
|
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
|
+
requirements:
|
|
219
|
+
- - ">="
|
|
220
|
+
- !ruby/object:Gem::Version
|
|
221
|
+
version: '0'
|
|
222
|
+
requirements: []
|
|
223
|
+
rubygems_version: 3.5.22
|
|
224
|
+
signing_key:
|
|
225
|
+
specification_version: 4
|
|
226
|
+
summary: A small framework for orchestrating units of business behaviour
|
|
227
|
+
test_files: []
|