piece_pipe 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|