piece_pipe 0.0.1
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.
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +44 -0
- data/Rakefile +12 -0
- data/lib/piece_pipe/assembly_station.rb +40 -0
- data/lib/piece_pipe/collector.rb +11 -0
- data/lib/piece_pipe/debug_step.rb +17 -0
- data/lib/piece_pipe/hashed_aggregator.rb +27 -0
- data/lib/piece_pipe/map_step.rb +15 -0
- data/lib/piece_pipe/method_assembly_station.rb +12 -0
- data/lib/piece_pipe/method_element.rb +27 -0
- data/lib/piece_pipe/pipeline.rb +75 -0
- data/lib/piece_pipe/pipeline_element.rb +35 -0
- data/lib/piece_pipe/tap_step.rb +12 -0
- data/lib/piece_pipe/version.rb +3 -0
- data/lib/piece_pipe.rb +12 -0
- data/piece_pipe.gemspec +21 -0
- data/spec/assembly_station_spec.rb +162 -0
- data/spec/collector_spec.rb +18 -0
- data/spec/hashed_aggregator_spec.rb +51 -0
- data/spec/map_step_spec.rb +23 -0
- data/spec/method_assembly_station_spec.rb +46 -0
- data/spec/method_element_spec.rb +86 -0
- data/spec/pipeline_element_spec.rb +125 -0
- data/spec/pipeline_spec.rb +90 -0
- data/spec/spec_helper.rb +97 -0
- metadata +134 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Atomic Object
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# PiecePipe
|
2
|
+
|
3
|
+
PiecePipe is about breaking your problem into its smallest, most interesting pieces, solving those pieces and not spending time on the glue code between them.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'piece_pipe'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install piece_pipe
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```
|
22
|
+
class NuclearPowerPlantHealthSummaryGenerator
|
23
|
+
def generate(region)
|
24
|
+
PiecePipe::Pipeline.new.
|
25
|
+
source([{region: region}]).
|
26
|
+
step(FetchPowerPlantsByRegion).
|
27
|
+
step(FindWorstReactor).
|
28
|
+
step(DetermineStatusClass).
|
29
|
+
step(BuildPlantHealthSummary).
|
30
|
+
step(SortByRadiationLevelsDescending).
|
31
|
+
collect(:plant_health_summary).
|
32
|
+
to_enum
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module PiecePipe
|
3
|
+
class AssemblyStation < PipelineElement
|
4
|
+
def process(item)
|
5
|
+
ensure_hash_like_object item
|
6
|
+
@assembly = item
|
7
|
+
begin
|
8
|
+
receive(item)
|
9
|
+
ensure
|
10
|
+
@assembly = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive(assembly)
|
15
|
+
noop
|
16
|
+
end
|
17
|
+
|
18
|
+
def noop
|
19
|
+
install({})
|
20
|
+
end
|
21
|
+
|
22
|
+
def install(value,opts={})
|
23
|
+
output = @assembly.merge(value || {})
|
24
|
+
|
25
|
+
if opts[:drop]
|
26
|
+
opts[:drop].each do |key|
|
27
|
+
output.delete(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
produce output
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def ensure_hash_like_object(obj)
|
35
|
+
unless obj.respond_to?(:[]) and obj.respond_to?(:merge) and obj.respond_to?(:dup)
|
36
|
+
raise "AssemblyStation object #{self.class.name} requires its source to produce Hash-like elements; not acceptable: #{obj.inspect}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PiecePipe
|
2
|
+
class DebugStep < PipelineElement
|
3
|
+
def initialize(opts={},&block)
|
4
|
+
@opts = opts
|
5
|
+
@block = block
|
6
|
+
end
|
7
|
+
|
8
|
+
def process(item)
|
9
|
+
title = @opts[:title] || "DEBUG:"
|
10
|
+
if @opts[:inspect_keys]
|
11
|
+
puts "#{title} #{item.slice(*@opts[:inspect_keys])}"
|
12
|
+
end
|
13
|
+
@block.call if @block
|
14
|
+
produce item
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PiecePipe
|
2
|
+
class HashedAggregator < PipelineElement
|
3
|
+
def initialize
|
4
|
+
@hash = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def generate_sequence
|
8
|
+
super
|
9
|
+
@hash.each do |key,values|
|
10
|
+
aggregate key, values
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def process(item)
|
15
|
+
raise "HashedAggregator requires inputs to be Hashes with :key and :value" unless item.keys.include?(:key) and item.keys.include?(:value)
|
16
|
+
# TODO : check key/val keys in item
|
17
|
+
#
|
18
|
+
@hash[item[:key]] ||= []
|
19
|
+
@hash[item[:key]] << item[:value]
|
20
|
+
end
|
21
|
+
|
22
|
+
def aggregate(key, values)
|
23
|
+
produce key: key, values: values
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PiecePipe
|
2
|
+
class MethodElement < PipelineElement
|
3
|
+
def initialize(meth)
|
4
|
+
raise "method cannot be nil" if meth.nil?
|
5
|
+
raise "method must accept 1 or 2 arguments; it accepts #{meth.arity}" if meth.arity != 1 and meth.arity != 2
|
6
|
+
@method = meth
|
7
|
+
end
|
8
|
+
|
9
|
+
public :produce # We will provide a ref to self as a producer for use by @method.
|
10
|
+
|
11
|
+
def process(item)
|
12
|
+
case @method.arity
|
13
|
+
when 1
|
14
|
+
# The method takes 1 argument, which we assume to be the input item.
|
15
|
+
# We also assume that the method RETURNS the ONLY item it wishes to produce.
|
16
|
+
# Will always produce exactly 1 output, even if that's just nil.
|
17
|
+
produce @method.call(item)
|
18
|
+
when 2
|
19
|
+
# The method takes 2 arguments. Assume first is input item, second is "producer",
|
20
|
+
# ie, a means for the method to produce 0, 1 or many outputs at its discretion.
|
21
|
+
# (by calling producer.produce)
|
22
|
+
# Return value of @method.call is NOT considered.
|
23
|
+
@method.call item, self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module PiecePipe
|
2
|
+
class Pipeline
|
3
|
+
def initialize
|
4
|
+
@latest_source = nil
|
5
|
+
end
|
6
|
+
|
7
|
+
def source(source)
|
8
|
+
raise "Source already set to #{@latest_source.inspect}" unless @latest_source.nil?
|
9
|
+
@latest_source = source
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def input(item)
|
14
|
+
source [item]
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def step(step)
|
19
|
+
case step
|
20
|
+
when Class
|
21
|
+
step = step.new # If the arg is a Class, instantiate it
|
22
|
+
when Method
|
23
|
+
step = MethodElement.new(step)
|
24
|
+
end
|
25
|
+
add_step step
|
26
|
+
end
|
27
|
+
|
28
|
+
def assembly_step(step)
|
29
|
+
case step
|
30
|
+
when Class
|
31
|
+
step = step.new # If the arg is a Class, instantiate it
|
32
|
+
when Method
|
33
|
+
step = MethodAssemblyStation.new(step)
|
34
|
+
end
|
35
|
+
add_step step
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_step(step)
|
39
|
+
step.source = @latest_source
|
40
|
+
@latest_source = step
|
41
|
+
self
|
42
|
+
end
|
43
|
+
private :add_step
|
44
|
+
|
45
|
+
def tap(&block)
|
46
|
+
step(TapStep.new(&block))
|
47
|
+
end
|
48
|
+
|
49
|
+
def debug(opts)
|
50
|
+
step(DebugStep.new(opts))
|
51
|
+
end
|
52
|
+
|
53
|
+
def collect(key)
|
54
|
+
step(Collector.new(key))
|
55
|
+
end
|
56
|
+
|
57
|
+
def result
|
58
|
+
to_enum.first
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_enum
|
62
|
+
@latest_source.to_enum
|
63
|
+
end
|
64
|
+
alias smoke to_enum
|
65
|
+
|
66
|
+
def to_a
|
67
|
+
to_enum.to_a
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_pipeline
|
72
|
+
::Pipeline::Pipeline.new
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PiecePipe
|
2
|
+
class PipelineElement
|
3
|
+
attr_accessor :source
|
4
|
+
|
5
|
+
def to_enum
|
6
|
+
Enumerator.new do |yielder|
|
7
|
+
@yielder = yielder
|
8
|
+
begin
|
9
|
+
generate_sequence
|
10
|
+
ensure
|
11
|
+
@yielder = nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def generate_sequence
|
19
|
+
if source.nil?
|
20
|
+
raise "The source of PipelineElement #{self.class.name} is nil"
|
21
|
+
end
|
22
|
+
source.to_enum.each do |item|
|
23
|
+
process(item)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def process(item)
|
28
|
+
produce item
|
29
|
+
end
|
30
|
+
|
31
|
+
def produce(something)
|
32
|
+
@yielder << something
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/piece_pipe.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'piece_pipe/pipeline'
|
2
|
+
require 'piece_pipe/pipeline_element'
|
3
|
+
|
4
|
+
require 'piece_pipe/assembly_station'
|
5
|
+
require 'piece_pipe/collector'
|
6
|
+
require 'piece_pipe/debug_step'
|
7
|
+
require 'piece_pipe/hashed_aggregator'
|
8
|
+
require 'piece_pipe/map_step'
|
9
|
+
require 'piece_pipe/method_assembly_station'
|
10
|
+
require 'piece_pipe/method_element'
|
11
|
+
require 'piece_pipe/tap_step'
|
12
|
+
require 'piece_pipe/version'
|
data/piece_pipe.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/piece_pipe/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["David Crosby", "Shawn Anderson"]
|
6
|
+
gem.email = ["crosby@atomicobject.com", "shawn42@gmail.com"]
|
7
|
+
gem.description = %q{PiecePipe is about breaking your problem into its smallest, most interesting pieces, solving those pieces and not spending time on the glue code between them. }
|
8
|
+
gem.summary = %q{ PiecePipe helps you break your code into small interesting pieces and provides the glue for pipelining them together to provide elegant, readable code. }
|
9
|
+
gem.homepage = "http://atomicobject.com"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "piece_pipe"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = PiecePipe::VERSION
|
17
|
+
gem.add_development_dependency "mocha"
|
18
|
+
gem.add_development_dependency "rspec"
|
19
|
+
gem.add_development_dependency "rake"
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe PiecePipe::AssemblyStation do
|
4
|
+
|
5
|
+
context "default AssemblyStation" do
|
6
|
+
it "produces unaltered items from its source" do
|
7
|
+
as = PiecePipe::AssemblyStation.new
|
8
|
+
as.source = [
|
9
|
+
{hello: "hippo"},
|
10
|
+
{hello: "lion"}
|
11
|
+
]
|
12
|
+
as.to_enum.to_a.should == [
|
13
|
+
{hello: "hippo"},
|
14
|
+
{hello: "lion"}
|
15
|
+
]
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "a typical AssemblyStation" do
|
21
|
+
class AnimalPhraser < PiecePipe::AssemblyStation
|
22
|
+
def receive(inputs)
|
23
|
+
install phrase: "There are #{inputs[:count]} #{inputs[:animal]}s"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can install new information into the work stream based on inputs" do
|
28
|
+
ap = AnimalPhraser.new
|
29
|
+
ap.source = [
|
30
|
+
{ animal: "Hippo", count: 2 },
|
31
|
+
{ animal: "Bird", count: 5 },
|
32
|
+
]
|
33
|
+
ap.to_enum.to_a.should == [
|
34
|
+
{ animal: "Hippo", count: 2, phrase: "There are 2 Hippos" },
|
35
|
+
{ animal: "Bird", count: 5, phrase: "There are 5 Birds" },
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
class Cuber < PiecePipe::AssemblyStation
|
40
|
+
def receive(inputs)
|
41
|
+
num = inputs[:num]
|
42
|
+
install squared: num * num, cubed: num * num * num
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can install multiple keys per item" do
|
47
|
+
ap = Cuber.new
|
48
|
+
ap.source = [
|
49
|
+
{ num: 2 },
|
50
|
+
{ num: 5 },
|
51
|
+
]
|
52
|
+
ap.to_enum.to_a.should == [
|
53
|
+
{ num: 2, squared: 4, cubed: 8 },
|
54
|
+
{ num: 5, squared: 25, cubed: 125 },
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "a filtering AssemblyStation" do
|
60
|
+
class HippoHater < PiecePipe::AssemblyStation
|
61
|
+
def receive(inputs)
|
62
|
+
unless inputs[:animal] == "Hippo"
|
63
|
+
install phrase: "There are #{inputs[:count]} #{inputs[:animal]}s"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can remove elements from the work stream by NOT invoking #install for certain items" do
|
69
|
+
hh = HippoHater.new
|
70
|
+
hh.source = [
|
71
|
+
{ animal: "Hippo", count: 2 },
|
72
|
+
{ animal: "Bird", count: 5 },
|
73
|
+
{ animal: "Hippo", count: 4 },
|
74
|
+
{ animal: "Cow", count: 1 },
|
75
|
+
]
|
76
|
+
hh.to_enum.to_a.should == [
|
77
|
+
{ animal: "Bird", count: 5, phrase: "There are 5 Birds" },
|
78
|
+
{ animal: "Cow", count: 1, phrase: "There are 1 Cows" },
|
79
|
+
]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "an exploding AssemblyStation" do
|
84
|
+
class LionCrusher < PiecePipe::AssemblyStation
|
85
|
+
def receive(inputs)
|
86
|
+
if inputs[:animal] == "Hippo"
|
87
|
+
inputs[:lion_count].times do
|
88
|
+
install injured_lion: true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "can insert new elements into the work stream by invoking #install more than once for a given input" do
|
95
|
+
lc = LionCrusher.new
|
96
|
+
lc.source = [
|
97
|
+
{ animal: "Hippo", lion_count: 3 }
|
98
|
+
]
|
99
|
+
lc.to_enum.to_a.should == [
|
100
|
+
{ injured_lion: true, animal: "Hippo", lion_count: 3 },
|
101
|
+
{ injured_lion: true, animal: "Hippo", lion_count: 3 },
|
102
|
+
{ injured_lion: true, animal: "Hippo", lion_count: 3 },
|
103
|
+
]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "error and oddball cases" do
|
108
|
+
context "source produces non-Hash inputs" do
|
109
|
+
class MyOtherSomething < PiecePipe::AssemblyStation
|
110
|
+
end
|
111
|
+
|
112
|
+
it "raises an error indicating requisite input type" do
|
113
|
+
as = MyOtherSomething.new
|
114
|
+
as.source = [1,2,3]
|
115
|
+
lambda do as.to_enum.to_a end.should raise_error(/AssemblyStation object MyOtherSomething.*Hash-like/)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "overridden receive() installs nil" do
|
120
|
+
class NilInstaller < PiecePipe::AssemblyStation
|
121
|
+
def receive(inputs)
|
122
|
+
install nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
it "does a no-op" do
|
126
|
+
as = NilInstaller.new
|
127
|
+
as.source = [
|
128
|
+
{hello: "hippo"},
|
129
|
+
{hello: "lion"}
|
130
|
+
]
|
131
|
+
as.to_enum.to_a.should == [
|
132
|
+
{hello: "hippo"},
|
133
|
+
{hello: "lion"}
|
134
|
+
]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "using noop()" do
|
139
|
+
class UseTheNoOp < PiecePipe::AssemblyStation
|
140
|
+
def receive(inputs)
|
141
|
+
noop
|
142
|
+
end
|
143
|
+
end
|
144
|
+
it "does a no-op" do
|
145
|
+
as = UseTheNoOp.new
|
146
|
+
as.source = [
|
147
|
+
{hello: "hippo"},
|
148
|
+
{hello: "lion"}
|
149
|
+
]
|
150
|
+
as.to_enum.to_a.should == [
|
151
|
+
{hello: "hippo"},
|
152
|
+
{hello: "lion"}
|
153
|
+
]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "overwriting keys that are already present in the inputs" # not sure what that might mean?
|
158
|
+
|
159
|
+
context "accessing keys that are NOT present in the inputs" # BOOM of course.... do something more interesting?
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::Collector do
|
4
|
+
let(:key) { :the_key }
|
5
|
+
subject { described_class.new(key) }
|
6
|
+
|
7
|
+
let(:pipeline) { PiecePipe::Pipeline.new }
|
8
|
+
|
9
|
+
context "processing hashes" do
|
10
|
+
it "produces the single item pulled from incoming Hash as named by the key" do
|
11
|
+
pipeline.
|
12
|
+
source([
|
13
|
+
{one: "1", two: "2", the_key: "big"},
|
14
|
+
{one: "11", two: "22", the_key: "momma"}]).
|
15
|
+
collect(:the_key).to_enum.to_a.should == [ "big", "momma" ]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::HashedAggregator do
|
4
|
+
let(:burps_and_chirps) do
|
5
|
+
[
|
6
|
+
{key: "burp", value: 2 },
|
7
|
+
{key: "chirp", value: 2 },
|
8
|
+
{key: "chirp", value: 3 },
|
9
|
+
{key: "burp", value: 4 },
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
context "default impl" do
|
14
|
+
it "produces the accumulated values for each key" do
|
15
|
+
|
16
|
+
results = PiecePipe::Pipeline.new.
|
17
|
+
source(burps_and_chirps).
|
18
|
+
step(subject).
|
19
|
+
to_a
|
20
|
+
|
21
|
+
results[0].should == { key: "burp", values: [2,4] }
|
22
|
+
results[1].should == { key: "chirp", values: [2,3] }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises if the input items don't have :key and :value set" do
|
26
|
+
lambda do ezpipe(subject, {}).to_a end.should raise_error(/key.*value/)
|
27
|
+
lambda do ezpipe(subject, key: "ok").to_a end.should raise_error(/key.*value/)
|
28
|
+
lambda do ezpipe(subject, value: "ok").to_a end.should raise_error(/key.*value/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "custom #aggregate implementation" do
|
33
|
+
class Countup < PiecePipe::HashedAggregator
|
34
|
+
def aggregate(key,values)
|
35
|
+
sum = values.inject do |a,b| a+b end
|
36
|
+
produce [key, sum]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "creates output for each key" do
|
41
|
+
results = PiecePipe::Pipeline.new.
|
42
|
+
source(burps_and_chirps).
|
43
|
+
step(Countup).
|
44
|
+
to_a
|
45
|
+
results[0].should == [ "burp", 6 ]
|
46
|
+
results[1].should == [ "chirp", 5 ]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::MapStep do
|
4
|
+
context "default impl" do
|
5
|
+
it "maps the input item to itself" do
|
6
|
+
ezpipe(subject, "hello").to_a.first.should == {key: "hello", value: "hello"}
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context "sample subclass of MapStep" do
|
11
|
+
class MyMapping < PiecePipe::MapStep
|
12
|
+
def map(item)
|
13
|
+
emit item[:name], item[:age]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
subject do MyMapping.new end
|
18
|
+
|
19
|
+
it "can use #emit to produce Hashes w :key and :value set" do
|
20
|
+
ezpipe(subject, name: "Cosby", age: 65).to_a.first.should == {key:"Cosby", value: 65}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::MethodAssemblyStation do
|
4
|
+
let(:pipeline) { PiecePipe::Pipeline.new }
|
5
|
+
|
6
|
+
context "wrapping methods with single arguments" do
|
7
|
+
it "creates a pipeline step that 'installs' the hash returned from the Method" do
|
8
|
+
pipeline.
|
9
|
+
input(a: 1, b: 2).
|
10
|
+
assembly_step( method(:sum_a_b) ).
|
11
|
+
to_enum.to_a.should == [{a: 1, b: 2, sum: 3}]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "produces unchanged if the method returns an empty hash" do
|
15
|
+
pipeline.
|
16
|
+
input(a: 1, b: 2).
|
17
|
+
assembly_step( method(:return_empty_hash) ).
|
18
|
+
to_enum.to_a.should == [{a:1, b:2}]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "produces unchanged if the method returns nothing" do
|
22
|
+
pipeline.
|
23
|
+
input(a: 1, b: 2).
|
24
|
+
assembly_step( method(:return_nothing) ).
|
25
|
+
to_enum.to_a.should == [{a:1, b:2}]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# HELPERS
|
32
|
+
#
|
33
|
+
|
34
|
+
def sum_a_b(inputs)
|
35
|
+
{ sum: inputs[:a] + inputs[:b] }
|
36
|
+
end
|
37
|
+
|
38
|
+
def return_empty_hash(inputs)
|
39
|
+
{ }
|
40
|
+
end
|
41
|
+
|
42
|
+
def return_nothing(inputs)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::MethodElement do
|
4
|
+
let(:pipeline) { PiecePipe::Pipeline.new }
|
5
|
+
|
6
|
+
context "wrapping methods with single arguments" do
|
7
|
+
it "creates a pipeline step that produces the return of the Method" do
|
8
|
+
pipeline.
|
9
|
+
input(42).
|
10
|
+
step( method(:stringify_number) ).
|
11
|
+
to_enum.to_a.should == ["!42!"]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "produces nil if the method returns nothing" do
|
15
|
+
pipeline.
|
16
|
+
input(42).
|
17
|
+
step( method(:do_nothing) ).
|
18
|
+
to_enum.to_a.should == [nil]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "wrapping methods with single arguments" do
|
23
|
+
it "creates a pipeline step wrapped around a Method, providing a 'producer' object as the second argument" do
|
24
|
+
pipeline.
|
25
|
+
input(3).
|
26
|
+
step( method(:spew_strings) ).
|
27
|
+
to_enum.to_a.should == [ ">>3", ">>3", ">>3" ]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "produces NOTHING if the method doesn't invoke producer.produce" do
|
31
|
+
pipeline.
|
32
|
+
input(42).
|
33
|
+
step( method(:do_nothing2) ).
|
34
|
+
to_enum.to_a.should == []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises an error if null is provided" do
|
39
|
+
lambda do pipeline.input(42).step( nil ) end.should raise_error(/nil/)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "raises an error if arity is 0" do
|
43
|
+
lambda do pipeline.input(42).step( method(:no_args) ) end.should raise_error(/arguments/)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raises an error if arity greater than 2" do
|
47
|
+
lambda do pipeline.input(42).step( method(:three_args) ) end.should raise_error(/arguments.* 3/)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "raises an error if arity is abitrary" do
|
51
|
+
lambda do pipeline.input(42).step( method(:many_args) ) end.should raise_error(/arguments.* -1/)
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# HELPERS
|
56
|
+
#
|
57
|
+
|
58
|
+
def stringify_number(num)
|
59
|
+
"!#{num}!"
|
60
|
+
end
|
61
|
+
|
62
|
+
def do_nothing(num)
|
63
|
+
end
|
64
|
+
|
65
|
+
def spew_strings(num,producer)
|
66
|
+
num.times do
|
67
|
+
producer.produce ">>#{num}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def do_nothing2(num,producer)
|
72
|
+
end
|
73
|
+
|
74
|
+
def no_args
|
75
|
+
"should not work"
|
76
|
+
end
|
77
|
+
|
78
|
+
def three_args(a,b,c)
|
79
|
+
"still should not work"
|
80
|
+
end
|
81
|
+
|
82
|
+
def many_args(*a)
|
83
|
+
"still should not work at all"
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::PipelineElement do
|
4
|
+
|
5
|
+
let (:array_source) { [ "hippo", "distribution", "mechanism" ] }
|
6
|
+
|
7
|
+
context "default PipelineElement" do
|
8
|
+
|
9
|
+
it "provides an enumeration of its source's elements" do
|
10
|
+
e = PiecePipe::PipelineElement.new
|
11
|
+
e.source = array_source
|
12
|
+
|
13
|
+
en = e.to_enum
|
14
|
+
en.next.should == "hippo"
|
15
|
+
en.next.should == "distribution"
|
16
|
+
en.next.should == "mechanism"
|
17
|
+
lambda do en.next end.should raise_error(StopIteration)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "overriding #generate_sequence" do
|
22
|
+
class IntegerGenerator < PiecePipe::PipelineElement
|
23
|
+
def generate_sequence
|
24
|
+
process 1
|
25
|
+
process 2
|
26
|
+
process 3
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can produce a sequence of items by invoking #process multiple times" do
|
31
|
+
en = IntegerGenerator.new.to_enum
|
32
|
+
en.to_a.should == [1,2,3]
|
33
|
+
end
|
34
|
+
|
35
|
+
class NothingGenerator < PiecePipe::PipelineElement
|
36
|
+
def generate_sequence
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it "can produce an empty enumerator by never invoking #process" do
|
43
|
+
en = NothingGenerator.new.to_enum
|
44
|
+
en.to_a.should == []
|
45
|
+
end
|
46
|
+
|
47
|
+
context "bypassing the default #process invocation" do
|
48
|
+
class IntegerProducer < PiecePipe::PipelineElement
|
49
|
+
def generate_sequence
|
50
|
+
produce 1
|
51
|
+
produce 2
|
52
|
+
produce 3
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can produce a sequence of items by invoking #produce multiple times" do
|
57
|
+
en = IntegerProducer.new.to_enum
|
58
|
+
en.to_a.should == [1,2,3]
|
59
|
+
end
|
60
|
+
|
61
|
+
class NothingProducer < PiecePipe::PipelineElement
|
62
|
+
def generate_sequence
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can produce an empty enumerator by never invoking #produce" do
|
68
|
+
en = NothingProducer.new.to_enum
|
69
|
+
en.to_a.should ==[]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "overriding #process" do
|
75
|
+
class StringExpander < PiecePipe::PipelineElement
|
76
|
+
def process(item)
|
77
|
+
produce "x" * item
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can transform a sequence of items" do
|
82
|
+
se = StringExpander.new
|
83
|
+
se.source = [2,4]
|
84
|
+
se.to_enum.to_a.should == ["xx", "xxxx"]
|
85
|
+
end
|
86
|
+
|
87
|
+
class AllFilter < PiecePipe::PipelineElement
|
88
|
+
def process(item)
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "can filter items out of a sequence by neglecting to call #produce" do
|
94
|
+
al = AllFilter.new
|
95
|
+
al.source = [3,6]
|
96
|
+
al.to_enum.to_a.should == []
|
97
|
+
end
|
98
|
+
|
99
|
+
class Exploder < PiecePipe::PipelineElement
|
100
|
+
def process(item)
|
101
|
+
item.times do
|
102
|
+
produce "hi #{item}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "can inflate a sequence by invoking #produce multiple times per item" do
|
108
|
+
ex = Exploder.new
|
109
|
+
ex.source = [2,3]
|
110
|
+
ex.to_enum.to_a.should == ["hi 2", "hi 2", "hi 3", "hi 3", "hi 3"]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "nill source" do
|
115
|
+
class MySomething < PiecePipe::PipelineElement
|
116
|
+
end
|
117
|
+
|
118
|
+
it "raises an error" do
|
119
|
+
e = MySomething.new
|
120
|
+
e.source = nil
|
121
|
+
lambda do e.to_enum.to_a end.should raise_error(/source.*MySomething.*is nil/)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PiecePipe::Pipeline do
|
4
|
+
class OneTwoThree < PiecePipe::PipelineElement
|
5
|
+
def generate_sequence
|
6
|
+
produce 1
|
7
|
+
produce 2
|
8
|
+
produce 3
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class TheDoubler < PiecePipe::PipelineElement
|
13
|
+
def process(item)
|
14
|
+
produce item * 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "super simple" do
|
19
|
+
it "produces the values of its source" do
|
20
|
+
subject.step(OneTwoThree)
|
21
|
+
subject.to_enum.to_a.should == [ 1, 2, 3]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "two steps" do
|
26
|
+
it "produces transformed values by processing both steps for each item" do
|
27
|
+
subject.
|
28
|
+
step(OneTwoThree).
|
29
|
+
step(TheDoubler)
|
30
|
+
|
31
|
+
subject.to_enum.to_a.should == [ 2, 4, 6]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "using instantiated PipelineElements as steps" do
|
36
|
+
it "produces transformed values by processing both steps for each item" do
|
37
|
+
subject.
|
38
|
+
step(OneTwoThree.new).
|
39
|
+
step(TheDoubler.new)
|
40
|
+
|
41
|
+
subject.to_enum.to_a.should == [ 2, 4, 6]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "using an enumerable object as the pipeline source" do
|
46
|
+
it "works with Arrays" do
|
47
|
+
subject.
|
48
|
+
source([10,20,30]).
|
49
|
+
step(TheDoubler)
|
50
|
+
subject.to_enum.to_a.should == [20, 40, 60]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "works with PipelineElements" do
|
54
|
+
subject.
|
55
|
+
source(OneTwoThree.new).
|
56
|
+
step(TheDoubler)
|
57
|
+
subject.to_enum.to_a.should == [2, 4, 6]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "raises if there's already something set as a source" do
|
61
|
+
subject.source([10,20,30])
|
62
|
+
lambda do subject.source([1,2,3]) end.should raise_error(/source already set/i)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises if there's already a step" do
|
66
|
+
subject.step(OneTwoThree)
|
67
|
+
lambda do subject.source([1,2,3]) end.should raise_error(/source already set/i)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#collect" do
|
72
|
+
let(:project_info) {
|
73
|
+
[
|
74
|
+
{name: "Project 1", project_health_summary: "Summary 1"},
|
75
|
+
{name: "Project 2", project_health_summary: "Summary 2"},
|
76
|
+
]
|
77
|
+
}
|
78
|
+
|
79
|
+
it "maps the pipeline items down to the values indicated by the given key" do
|
80
|
+
subject.
|
81
|
+
source(project_info).
|
82
|
+
collect(:project_health_summary)
|
83
|
+
subject.to_enum.to_a.should == [
|
84
|
+
"Summary 1",
|
85
|
+
"Summary 2"
|
86
|
+
]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'piece_pipe'
|
2
|
+
|
3
|
+
module SpecHelpers
|
4
|
+
def ezpipe(single_step, inputz)
|
5
|
+
PiecePipe::Pipeline.new.
|
6
|
+
input(inputz).
|
7
|
+
step(single_step)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.mock_with :mocha
|
13
|
+
config.include SpecHelpers
|
14
|
+
end
|
15
|
+
|
16
|
+
class NilClass
|
17
|
+
def stubs(*args)
|
18
|
+
raise "Tried to stub a method on NilClass with arguments: #{args.inspect}. Are you using a non-existant mock instance?"
|
19
|
+
end
|
20
|
+
|
21
|
+
def expects(*args)
|
22
|
+
raise "Tried to expect a method on NilClass with arguments: #{args.inspect}. Are you using a non-existant mock instance?"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_regexp
|
26
|
+
# match empty string or
|
27
|
+
/^$| /
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def clever_girl
|
32
|
+
pending <<-EOVR
|
33
|
+
__ ______
|
34
|
+
,^.__.>--"~~'_.--~_)~^.
|
35
|
+
_L^~ ~ (~ _.-~ \\. |\\
|
36
|
+
CLEVER GIRL.... ,-~ __ __,^"/\\_A_/ /' \\
|
37
|
+
/ ,-" "~~" _) \\ ~_,^ /\\
|
38
|
+
// / ,-~\\ x~" \\._"-~ ~ _Y
|
39
|
+
Y' Y. (__.// / " , "\\_r ' ]
|
40
|
+
J-._l_>---r{ ~ \\__/ \\ _/
|
41
|
+
(_ ( (~ ( ~"--- _.-~ `\\ / \\ !
|
42
|
+
(_"~--^----^--------" _.-c Y /Y'
|
43
|
+
l~--v---.,____.--" / !_/ |
|
44
|
+
\\.__!.__./~-. _/ / \\ !
|
45
|
+
`x.\\_____\\_,>---"~___Y\\__/Y'
|
46
|
+
~ ~(~~()"~___)/ /\\|
|
47
|
+
(~~ ~~__) \\_t
|
48
|
+
(~~ ~~__)\\_/ |
|
49
|
+
(~~ ~~__)\\_/ |
|
50
|
+
{ ~~ ~~ }/ \\
|
51
|
+
EOVR
|
52
|
+
end
|
53
|
+
|
54
|
+
def pending_tiger_no_dragon
|
55
|
+
pending <<-EOT
|
56
|
+
|
57
|
+
,''',
|
58
|
+
.' ., .', ../'''',
|
59
|
+
.'. %%, %.', .,/' .,% :
|
60
|
+
.'.% %%%,`%%%'. .....,,,,,,..... .,%%% .,%%'. .'
|
61
|
+
: %%% %%%%%%',:%%>>%>' .,>>%>>%>%>>%%>,. `%%%',% :
|
62
|
+
: %%%%%%%'.,>>>%' .,%>%>%'.,>%>%' . `%>>>,. `%%%:'
|
63
|
+
` %%%%'.,>>%' .,%>>%>%' .,%>%>%' .>>%,. `%%>>,. `%
|
64
|
+
`%'.,>>>%'.,%%%%%%%' .,%%>%%>%' >>%%>%>>%.`%% %% `,
|
65
|
+
,`%% %%>>>%%%>>%%>%%>>%>>%>%%% %%>%%>%%>>%>%%%' % %,
|
66
|
+
,%>%'.>>%>%'%>>%%>%%%%>%' `%>%>>%%.`%>>%.
|
67
|
+
,%%>' .>%>%'.%>%>>%%%>>%' ,%%>>%%>%>>%>>%>%%,.`%%%>%%. `%>%.
|
68
|
+
` ,%' .>%%%'.%>%>>%' .,%%%%%%%%' `%%%%%%.`%%>%% .%%>
|
69
|
+
.%>% .%%>' :%>>%%'.,%%%%%%%%%'.%%%%%' `%%%%.`%%%%%.%%%%> %%>%.
|
70
|
+
,%>%' >>%% >%' `%%%%' `%%%%%%%'.,>,. `%%%%' `%%%>>%%>%
|
71
|
+
.%%>%' .%%>' %>>%, %% oO ~ Oo %%%>>'.>>>>>>. `% oO ~ Oo'.%%%'%>%,
|
72
|
+
%>'%> .%>%>% %%>%%%' `OoooO'.%%>>'.>>>%>>%>>.`%`OoooO'.%%>% '%>%
|
73
|
+
%',%' %>%>%' %>%>%>% .%,>,>, `>'.>>%>%%>>>%>.`%,>,>' %%%%> .>%>,
|
74
|
+
` %>% `%>>%%. `%%% %' >%%%%%%>, ' >>%>>%%%>%>>> >>%%' ,%%>%'.%%>>%.
|
75
|
+
.%%' %%%%>%. `>%%. %>%%>>>%.>> >>>%>%%%%>%>>.>>>'.>%>%>' %>>%>%%
|
76
|
+
`.%% `%>>%%> %%>% %>>>%%%>>'.>%>>>>%%%>>%>>.>',%>>%' ,>%'>% '
|
77
|
+
%>' %%%%%%' `%%' %%%%%> >' >>>>%>>%%>>%>>%> %%>%>' .%>%% .%%
|
78
|
+
%>%>, %>%%>>%%, %>%>% `%% %>> >>>%>>>%%>>>>%>> %%>>,%>%%'.%>%,
|
79
|
+
%>%>%%, `%>%%>%>%, %>%%> ,%>%>>>.>>`.,. `" ..'>.%. % %>%>%'.%>%%;
|
80
|
+
%'`%%>% %%>%% %>% %'.>%>>%>%%>>%::. `, /' ,%>>>%>. >%>%'.%>%'%'
|
81
|
+
` .%>%' >%%% %>%%'.>%>%;''.,>>%%>%%::. ..'.,%>>%>%>,`% %'.>%%' '
|
82
|
+
%>%>%% `%> >%%'.%%>%>>%>%>%>>>%>%>>%,,::,%>>%%>%>>%>%% `>>%>'
|
83
|
+
%'`%%>%>>% %>'.%>>%>%>>;'' ..,,%>%>%%/::%>%%>>%%,,.``% .%>%%
|
84
|
+
` `%>%>>%%' %>%%>>%>>%>%>%>%%>%/' `%>%%>%>>%%% ' .%'
|
85
|
+
%' `%>% `%>%%;'' .,>>%>%/',;;;;;,;;;;,`%>%>%,`%' '
|
86
|
+
` ` ` `%>%%%>%%>%%;/ @a;;;;;;;;;;;a@ >%>%%'
|
87
|
+
`/////////';, `@a@@a@@a@@aa@',;`//'
|
88
|
+
`//////.;;,,............,,;;//'
|
89
|
+
`////;;;;;;;;;;;;;;;;;/'
|
90
|
+
`/////////////////'
|
91
|
+
/
|
92
|
+
/
|
93
|
+
/
|
94
|
+
HAS TO COME CORRECT!!!!!
|
95
|
+
EOT
|
96
|
+
end
|
97
|
+
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: piece_pipe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Crosby
|
9
|
+
- Shawn Anderson
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-05-30 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mocha
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rspec
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: ! 'PiecePipe is about breaking your problem into its smallest, most interesting
|
64
|
+
pieces, solving those pieces and not spending time on the glue code between them. '
|
65
|
+
email:
|
66
|
+
- crosby@atomicobject.com
|
67
|
+
- shawn42@gmail.com
|
68
|
+
executables: []
|
69
|
+
extensions: []
|
70
|
+
extra_rdoc_files: []
|
71
|
+
files:
|
72
|
+
- .gitignore
|
73
|
+
- Gemfile
|
74
|
+
- LICENSE
|
75
|
+
- README.md
|
76
|
+
- Rakefile
|
77
|
+
- lib/piece_pipe.rb
|
78
|
+
- lib/piece_pipe/assembly_station.rb
|
79
|
+
- lib/piece_pipe/collector.rb
|
80
|
+
- lib/piece_pipe/debug_step.rb
|
81
|
+
- lib/piece_pipe/hashed_aggregator.rb
|
82
|
+
- lib/piece_pipe/map_step.rb
|
83
|
+
- lib/piece_pipe/method_assembly_station.rb
|
84
|
+
- lib/piece_pipe/method_element.rb
|
85
|
+
- lib/piece_pipe/pipeline.rb
|
86
|
+
- lib/piece_pipe/pipeline_element.rb
|
87
|
+
- lib/piece_pipe/tap_step.rb
|
88
|
+
- lib/piece_pipe/version.rb
|
89
|
+
- piece_pipe.gemspec
|
90
|
+
- spec/assembly_station_spec.rb
|
91
|
+
- spec/collector_spec.rb
|
92
|
+
- spec/hashed_aggregator_spec.rb
|
93
|
+
- spec/map_step_spec.rb
|
94
|
+
- spec/method_assembly_station_spec.rb
|
95
|
+
- spec/method_element_spec.rb
|
96
|
+
- spec/pipeline_element_spec.rb
|
97
|
+
- spec/pipeline_spec.rb
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
homepage: http://atomicobject.com
|
100
|
+
licenses: []
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.24
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: PiecePipe helps you break your code into small interesting pieces and provides
|
123
|
+
the glue for pipelining them together to provide elegant, readable code.
|
124
|
+
test_files:
|
125
|
+
- spec/assembly_station_spec.rb
|
126
|
+
- spec/collector_spec.rb
|
127
|
+
- spec/hashed_aggregator_spec.rb
|
128
|
+
- spec/map_step_spec.rb
|
129
|
+
- spec/method_assembly_station_spec.rb
|
130
|
+
- spec/method_element_spec.rb
|
131
|
+
- spec/pipeline_element_spec.rb
|
132
|
+
- spec/pipeline_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
has_rdoc:
|