crack_pipe 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|