crack_pipe 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +18 -0
- data/README.md +51 -0
- data/lib/crack_pipe/action/result.rb +27 -0
- data/lib/crack_pipe/action/short_circuit.rb +14 -0
- data/lib/crack_pipe/action/step.rb +21 -0
- data/lib/crack_pipe/action.rb +166 -0
- data/lib/crack_pipe/version.rb +12 -0
- data/lib/crack_pipe.rb +4 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b90ccb391cbc78b10497f1978236cac94de4f715
|
4
|
+
data.tar.gz: 12f9809281e638984c9d59ac340f5cef7e5c6592
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8e53e038a829b08417c47ab9a961dc76169be2d6ca6393f70ab0bb2e4f1c249a17b2f58851354f1500e55515ab07d0f07060ac14822bb0e440a1c60665dc4168
|
7
|
+
data.tar.gz: 3d4f43c12cc6d2c9af68c2b28395d7fdf4da17bc81afbac25bdd9e07ee34b3051097f54044e330e277d2c03107f3a2e50fd3e5ce48892aed8ce2c18db522aa71
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2019 Joshua Hansen
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# CrackPipe
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
A little over a year ago, I discovered [Trailblazer](http://trailblazer.to) and used its concept of "operations" in a few projects. While I came to enjoy their concept of pipelines and result objects, I felt the entire project was entirely too complex for my use cases. I wanted much simpler pipelines and nesting.
|
6
|
+
|
7
|
+
### What's with the name?
|
8
|
+
|
9
|
+
Gem names are hard to come by these days. I'd considered something slightly more mature like `half_pipe`, but in the age of appending a codes of conduct to projects, I lean offensive in naming wherever possible. I'm kinda wishing I had named [my factory gem](https://github.com/binarypaladin/toil) `meth_lab` instead.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Pretty standard gem stuff.
|
14
|
+
|
15
|
+
```
|
16
|
+
$ gem install crack_pipe
|
17
|
+
```
|
18
|
+
|
19
|
+
If you're using [Bundler](https://bundler.io) (and who isn't?) it's likely you'll add this to your `Gemfile` like so:
|
20
|
+
|
21
|
+
```
|
22
|
+
gem 'crack_pipe'
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
One day I'll write some actual documentation. (Yeah, right.) In the meantime, [the action spec](spec/action_spec.rb) has a pretty good example of steps, always passing, fail tracks, short circuiting, and nesting actions.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
### Issue Guidelines
|
32
|
+
|
33
|
+
GitHub issues are for bugs, not support. As of right now, there is no official support for this gem. You can try reaching out to the author, [Joshua Hansen](mailto:joshua@epicbanality.com?subject=CrackPipe+sucks) if you're really stuck, but there's a pretty high chance that won't go anywhere at the moment or you'll get a response like this:
|
34
|
+
|
35
|
+
> Hi. I'm super busy. It's nothing personal. Check the README first if you haven't already. If you don't find your answer there, it's time to start reading the source. Have fun! Let me know if I screwed something up.
|
36
|
+
|
37
|
+
### Pull Request Guidelines
|
38
|
+
|
39
|
+
* Include tests with your PRs.
|
40
|
+
|
41
|
+
### Code of Conduct
|
42
|
+
|
43
|
+
Don't smoke crack.
|
44
|
+
|
45
|
+
## License
|
46
|
+
|
47
|
+
See [`LICENSE.txt`](LICENSE.txt).
|
48
|
+
|
49
|
+
## What if I stop maintaining this?
|
50
|
+
|
51
|
+
The codebase is pretty small. That was one of the main design goals. If you can figure out how to use it, I'm sure you can maintain it.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module CrackPipe
|
4
|
+
class Action
|
5
|
+
class Result
|
6
|
+
attr_reader :context, :output
|
7
|
+
|
8
|
+
def initialize(context: {}, output: nil, success:, **)
|
9
|
+
@context = context
|
10
|
+
@output = output
|
11
|
+
@success = success
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@context[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure?
|
19
|
+
!@success
|
20
|
+
end
|
21
|
+
|
22
|
+
def success?
|
23
|
+
@success
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module CrackPipe
|
4
|
+
class Action
|
5
|
+
class Step
|
6
|
+
attr_reader :exec, :context_override, :desc, :output_override, :track
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
track,
|
10
|
+
exec,
|
11
|
+
output_override = nil,
|
12
|
+
**context_override
|
13
|
+
)
|
14
|
+
@context_override = context_override.dup
|
15
|
+
@exec = exec
|
16
|
+
@output_override = output_override
|
17
|
+
@track = track
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'crack_pipe/action/result'
|
4
|
+
require 'crack_pipe/action/short_circuit'
|
5
|
+
require 'crack_pipe/action/step'
|
6
|
+
|
7
|
+
module CrackPipe
|
8
|
+
class Action
|
9
|
+
@steps = []
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_reader :steps
|
13
|
+
|
14
|
+
def call(context, &blk)
|
15
|
+
new(context, steps, &blk).result
|
16
|
+
end
|
17
|
+
|
18
|
+
def inherited(subclass)
|
19
|
+
subclass.instance_variable_set(:@steps, @steps.dup)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def fail(exec = nil, **kwargs, &blk)
|
26
|
+
step(exec, kwargs.merge(track: :fail), &blk)
|
27
|
+
end
|
28
|
+
|
29
|
+
def pass(exec = nil, **kwargs, &blk)
|
30
|
+
step(exec, kwargs.merge(output_override: true), &blk)
|
31
|
+
end
|
32
|
+
|
33
|
+
def step(
|
34
|
+
exec = nil,
|
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)]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :history, :result
|
50
|
+
|
51
|
+
def initialize(context, steps = self.class.steps, &blk)
|
52
|
+
@history = []
|
53
|
+
_result!(context.dup, steps.dup, &blk)
|
54
|
+
end
|
55
|
+
|
56
|
+
# NOTE: While this hook does nothing by default, it is here with the
|
57
|
+
# intention of dealing with potential default values being generated either
|
58
|
+
# for output or adding values to the context. A common example would be
|
59
|
+
# returning some kind of default failure object in place of a literal `nil`
|
60
|
+
# or `false`.
|
61
|
+
def after_step(output, _context, _step)
|
62
|
+
output
|
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
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
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]) }
|
95
|
+
end
|
96
|
+
|
97
|
+
def _exec_step!(context, step)
|
98
|
+
e = step.exec
|
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
|
155
|
+
end
|
156
|
+
|
157
|
+
def _wrap_output(output, context, step)
|
158
|
+
wrapped_output = _output_hash(after_step(output, context, step), step)
|
159
|
+
{
|
160
|
+
exec: step.exec,
|
161
|
+
next_track: wrapped_output[:success] ? :default : :fail,
|
162
|
+
context: context
|
163
|
+
}.merge(wrapped_output)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/crack_pipe.rb
ADDED
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crack_pipe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joshua
|
8
|
+
- Hansen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2019-07-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.16'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.16'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '5.0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '5.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '10.0'
|
56
|
+
description: Pipelines... on crack I guess.
|
57
|
+
email:
|
58
|
+
- joshua@epicbanality.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- lib/crack_pipe.rb
|
66
|
+
- lib/crack_pipe/action.rb
|
67
|
+
- lib/crack_pipe/action/result.rb
|
68
|
+
- lib/crack_pipe/action/short_circuit.rb
|
69
|
+
- lib/crack_pipe/action/step.rb
|
70
|
+
- lib/crack_pipe/version.rb
|
71
|
+
homepage: https://github.com/binarypaladin/crack_pipe
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 2.2.0
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.5.4
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Pipelines... on crack I guess.
|
95
|
+
test_files: []
|