crack_pipe 0.1.0 → 0.2.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/lib/crack_pipe/action/exec.rb +80 -0
- data/lib/crack_pipe/action/result.rb +7 -5
- data/lib/crack_pipe/action/{short_circuit.rb → signal.rb} +4 -3
- data/lib/crack_pipe/action/step.rb +24 -10
- data/lib/crack_pipe/action.rb +33 -117
- data/lib/crack_pipe/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee187d314abb9bfab155824f0d2c92825ef2f0e7
|
4
|
+
data.tar.gz: fc720da093abcfbf35c625488d3904012114b193
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4704be40ad6adbf04cd9ed32a03915294e36479481aa1b2711171023d12d9ec17176b0286dd109e74c296fef46fc147e67211f1a27e79d55b257811137378052
|
7
|
+
data.tar.gz: fcfef7cacbaafd4ab99ccdc65ee8fe7daeb5b5c6cf0769ddbc3d841faee17a29e9b896217ed7b68feae10fde546c40920c342e57f172465d45066779eed5f8f6
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'crack_pipe/action/signal'
|
4
|
+
|
5
|
+
module CrackPipe
|
6
|
+
class Action
|
7
|
+
module Exec
|
8
|
+
class << self
|
9
|
+
def call(action, context, track = :default)
|
10
|
+
Result.new(action(action, context, [], track))
|
11
|
+
end
|
12
|
+
|
13
|
+
def action(action, context, results = [], track = :default)
|
14
|
+
action.steps.each_with_object(results) do |s, rslts|
|
15
|
+
next unless track == s.track
|
16
|
+
|
17
|
+
if s.exec.is_a?(Action)
|
18
|
+
self.action(s.exec, context, rslts)
|
19
|
+
else
|
20
|
+
rslts << step(action, s, context)
|
21
|
+
end
|
22
|
+
|
23
|
+
rslts.last.tap do |r|
|
24
|
+
context = r[:context]
|
25
|
+
track = r[:next]
|
26
|
+
return rslts if r[:signal] == :halt
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def flow_control_hash(action, step, context, output)
|
32
|
+
success = success_with_step?(action, step, output)
|
33
|
+
|
34
|
+
{
|
35
|
+
exec: step.exec,
|
36
|
+
track: step.track,
|
37
|
+
next: success ? step.track : :fail,
|
38
|
+
context: context.dup
|
39
|
+
}.merge(flow_control_with_output(output, success))
|
40
|
+
end
|
41
|
+
|
42
|
+
def flow_control_with_output(output, success)
|
43
|
+
case output
|
44
|
+
when Signal
|
45
|
+
{
|
46
|
+
signal: output.type,
|
47
|
+
output: output.value,
|
48
|
+
success: output.success.nil? ? success : output.success
|
49
|
+
}
|
50
|
+
else
|
51
|
+
{ output: output, success: success }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def halt(output, success = nil)
|
56
|
+
throw(:signal, Signal.new(:halt, output, success))
|
57
|
+
end
|
58
|
+
|
59
|
+
def step(action, step, context)
|
60
|
+
output = catch(:signal) do
|
61
|
+
case (e = step.exec)
|
62
|
+
when Symbol
|
63
|
+
action.public_send(e, context, **context)
|
64
|
+
when Proc
|
65
|
+
action.instance_exec(context, **context, &e)
|
66
|
+
else
|
67
|
+
e.call(context, **context)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
action.after_step(flow_control_hash(action, step, context, output))
|
72
|
+
end
|
73
|
+
|
74
|
+
def success_with_step?(action, step, output)
|
75
|
+
step.always_pass? || step.track != :fail && !action.failure?(output)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -3,12 +3,14 @@
|
|
3
3
|
module CrackPipe
|
4
4
|
class Action
|
5
5
|
class Result
|
6
|
-
attr_reader :context, :output
|
6
|
+
attr_reader :context, :history, :output
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
@
|
11
|
-
@
|
8
|
+
def initialize(history)
|
9
|
+
last_result = history.last
|
10
|
+
@context = last_result[:context]
|
11
|
+
@history = history
|
12
|
+
@output = last_result[:output]
|
13
|
+
@success = last_result[:success]
|
12
14
|
end
|
13
15
|
|
14
16
|
def [](key)
|
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
module CrackPipe
|
4
4
|
class Action
|
5
|
-
class
|
6
|
-
attr_reader :success, :value
|
5
|
+
class Signal
|
6
|
+
attr_reader :success, :type, :value
|
7
7
|
|
8
|
-
def initialize(value, success = nil)
|
8
|
+
def initialize(type, value, success = nil)
|
9
|
+
@type = type
|
9
10
|
@value = value
|
10
11
|
@success = success
|
11
12
|
end
|
@@ -3,19 +3,33 @@
|
|
3
3
|
module CrackPipe
|
4
4
|
class Action
|
5
5
|
class Step
|
6
|
-
attr_reader :exec, :
|
6
|
+
attr_reader :exec, :track
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
8
|
+
def initialize(exec = nil, always_pass: false, track: :default, **, &blk)
|
9
|
+
if block_given?
|
10
|
+
raise ArgumentError, '`exec` must be `nil` with a block' unless
|
11
|
+
exec.nil?
|
12
|
+
exec = blk
|
13
|
+
end
|
14
|
+
|
15
|
+
@always_pass = always_pass
|
16
|
+
@exec = instantiate_action(exec)
|
17
17
|
@track = track
|
18
18
|
end
|
19
|
+
|
20
|
+
def always_pass?
|
21
|
+
@always_pass
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# NOTE: This allows actions to be passed in as a class rather than as an
|
27
|
+
# instance. It's the difference betweem `step SomeAction` vs
|
28
|
+
# `step SomeAction.new` when nesting actions.
|
29
|
+
def instantiate_action(obj)
|
30
|
+
return obj.new if obj.is_a?(Class) && obj < Action
|
31
|
+
obj
|
32
|
+
end
|
19
33
|
end
|
20
34
|
end
|
21
35
|
end
|
data/lib/crack_pipe/action.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require 'crack_pipe/action/exec'
|
3
4
|
require 'crack_pipe/action/result'
|
4
|
-
require 'crack_pipe/action/
|
5
|
+
require 'crack_pipe/action/signal'
|
5
6
|
require 'crack_pipe/action/step'
|
6
7
|
|
7
8
|
module CrackPipe
|
9
|
+
# NOTE: The reason for using an instantiated class rather than doing all of
|
10
|
+
# this functionally, since there is no real state data, is so that symbols
|
11
|
+
# operate as instance methods and that procs and the like are executed with
|
12
|
+
# `instance_exec`, giving them access to the local scope.
|
8
13
|
class Action
|
9
14
|
@steps = []
|
10
15
|
|
@@ -12,9 +17,13 @@ module CrackPipe
|
|
12
17
|
attr_reader :steps
|
13
18
|
|
14
19
|
def call(context, &blk)
|
15
|
-
new(
|
20
|
+
new(&blk).call(context)
|
16
21
|
end
|
17
22
|
|
23
|
+
# NOTE: In general, the only time you should use inheritence is when
|
24
|
+
# created a more specilized generic action with some of its own methods
|
25
|
+
# similar such as `pass!` or overiding behavior in `after_step` or
|
26
|
+
# `failure?`.
|
18
27
|
def inherited(subclass)
|
19
28
|
subclass.instance_variable_set(:@steps, @steps.dup)
|
20
29
|
super
|
@@ -27,30 +36,26 @@ module CrackPipe
|
|
27
36
|
end
|
28
37
|
|
29
38
|
def pass(exec = nil, **kwargs, &blk)
|
30
|
-
step(exec, kwargs.merge(
|
39
|
+
step(exec, kwargs.merge(always_pass: true), &blk)
|
31
40
|
end
|
32
41
|
|
33
|
-
def step(
|
34
|
-
|
35
|
-
output_override: nil,
|
36
|
-
track: :default,
|
37
|
-
**opts,
|
38
|
-
&blk
|
39
|
-
)
|
40
|
-
if block_given?
|
41
|
-
raise ArgumentError, '`exec` must be `nil` with a block' unless
|
42
|
-
exec.nil?
|
43
|
-
exec = blk
|
44
|
-
end
|
45
|
-
@steps += [Step.new(track, exec, output_override, opts)]
|
42
|
+
def step(*args, &blk)
|
43
|
+
@steps += [Step.new(*args, &blk)]
|
46
44
|
end
|
47
45
|
end
|
48
46
|
|
49
|
-
attr_reader :
|
47
|
+
attr_reader :steps
|
50
48
|
|
51
|
-
def initialize(
|
52
|
-
@
|
53
|
-
|
49
|
+
def initialize(steps = nil, **default_context, &blk)
|
50
|
+
@__default_context__ = default_context.dup
|
51
|
+
@__wrapper__ = block_given? ? blk : nil
|
52
|
+
@steps = steps ? steps.dup : self.class.steps
|
53
|
+
end
|
54
|
+
|
55
|
+
def call(context)
|
56
|
+
context = @__default_context__.merge(context)
|
57
|
+
return @__wrapper__.call(Exec.(self, context)) if @__wrapper__
|
58
|
+
Exec.(self, context)
|
54
59
|
end
|
55
60
|
|
56
61
|
# NOTE: While this hook does nothing by default, it is here with the
|
@@ -58,109 +63,20 @@ module CrackPipe
|
|
58
63
|
# for output or adding values to the context. A common example would be
|
59
64
|
# returning some kind of default failure object in place of a literal `nil`
|
60
65
|
# or `false`.
|
61
|
-
def after_step(
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
def fail!(value)
|
66
|
-
ShortCircuit.new(value, false)
|
67
|
-
end
|
68
|
-
|
69
|
-
def pass!(value)
|
70
|
-
ShortCircuit.new(value, true)
|
71
|
-
end
|
72
|
-
|
73
|
-
def step_failure?(output)
|
74
|
-
case output
|
75
|
-
when Result
|
76
|
-
output.failure?
|
77
|
-
else
|
78
|
-
!output
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def to_a
|
83
|
-
history.dup
|
66
|
+
def after_step(flow_control_hash)
|
67
|
+
flow_control_hash
|
84
68
|
end
|
85
69
|
|
86
|
-
|
87
|
-
|
88
|
-
def _action?(obj)
|
89
|
-
obj.is_a?(Class) && obj < Action
|
90
|
-
end
|
91
|
-
|
92
|
-
def _exec_nested!(context, action)
|
93
|
-
@history += action.new(context).history
|
94
|
-
@history.last.tap { |h| context.merge!(h[:context]) }
|
70
|
+
def fail!(output)
|
71
|
+
Exec.halt(output, false)
|
95
72
|
end
|
96
73
|
|
97
|
-
def
|
98
|
-
|
99
|
-
return _exec_nested!(context, e) if _action?(e)
|
100
|
-
|
101
|
-
output =
|
102
|
-
case e
|
103
|
-
when Symbol
|
104
|
-
public_send(e, context, **context)
|
105
|
-
when Proc
|
106
|
-
instance_exec(context, **context, &e)
|
107
|
-
else
|
108
|
-
e.call(context, **context)
|
109
|
-
end
|
110
|
-
|
111
|
-
_wrap_output(after_step(output, context, step), context.dup, step)
|
112
|
-
.tap { |wo| @history << wo }
|
113
|
-
end
|
114
|
-
|
115
|
-
def _exec_steps!(context, steps)
|
116
|
-
next_track = :default
|
117
|
-
steps.map do |s|
|
118
|
-
next unless next_track == s.track
|
119
|
-
_exec_step!(context, s).tap do |wrapped_output|
|
120
|
-
next_track =
|
121
|
-
wrapped_output[:short_circuit] ? nil : wrapped_output[:next_track]
|
122
|
-
end
|
123
|
-
end.compact.last
|
124
|
-
end
|
125
|
-
|
126
|
-
def _output_hash(output, step)
|
127
|
-
output = step.output_override unless step.output_override.nil?
|
128
|
-
success = step.track != :fail && !step_failure?(output)
|
129
|
-
case output
|
130
|
-
when ShortCircuit
|
131
|
-
{
|
132
|
-
output: output.value,
|
133
|
-
short_circuit: true,
|
134
|
-
success: output.success.nil? ? success : output.success
|
135
|
-
}
|
136
|
-
else
|
137
|
-
{
|
138
|
-
output: output,
|
139
|
-
short_circuit: false,
|
140
|
-
success: success
|
141
|
-
}
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# NOTE: The optional block here allows you to wrap the the execution in
|
146
|
-
# external functionality, such as a default `rescue` or something like a
|
147
|
-
# database transaction if it's necessary to span multiple steps.
|
148
|
-
def _result!(context, steps)
|
149
|
-
if block_given?
|
150
|
-
yield(@result = Result.new(_exec_steps!(context, steps)))
|
151
|
-
else
|
152
|
-
@result = Result.new(_exec_steps!(context, steps))
|
153
|
-
end
|
154
|
-
@result
|
74
|
+
def failure?(output)
|
75
|
+
!output
|
155
76
|
end
|
156
77
|
|
157
|
-
def
|
158
|
-
|
159
|
-
{
|
160
|
-
exec: step.exec,
|
161
|
-
next_track: wrapped_output[:success] ? :default : :fail,
|
162
|
-
context: context
|
163
|
-
}.merge(wrapped_output)
|
78
|
+
def pass!(output)
|
79
|
+
Exec.halt(output, true)
|
164
80
|
end
|
165
81
|
end
|
166
82
|
end
|
data/lib/crack_pipe/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crack_pipe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '5.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: pry
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 0.12.1
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.12.1
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: rake
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -64,8 +78,9 @@ files:
|
|
64
78
|
- README.md
|
65
79
|
- lib/crack_pipe.rb
|
66
80
|
- lib/crack_pipe/action.rb
|
81
|
+
- lib/crack_pipe/action/exec.rb
|
67
82
|
- lib/crack_pipe/action/result.rb
|
68
|
-
- lib/crack_pipe/action/
|
83
|
+
- lib/crack_pipe/action/signal.rb
|
69
84
|
- lib/crack_pipe/action/step.rb
|
70
85
|
- lib/crack_pipe/version.rb
|
71
86
|
homepage: https://github.com/binarypaladin/crack_pipe
|