pipely 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +4 -0
- data/Rakefile +21 -0
- data/bin/pipely +24 -0
- data/lib/pipely.rb +20 -0
- data/lib/pipely/component.rb +111 -0
- data/lib/pipely/definition.rb +72 -0
- data/lib/pipely/dependency.rb +14 -0
- data/lib/pipely/graph_builder.rb +51 -0
- data/lib/pipely/reference_list.rb +32 -0
- data/lib/pipely/version.rb +3 -0
- data/spec/lib/pipely/component_spec.rb +44 -0
- data/spec/lib/pipely/definition_spec.rb +56 -0
- data/spec/lib/pipely/dependency_spec.rb +11 -0
- data/spec/lib/pipely/graph_builder_spec.rb +42 -0
- data/spec/lib/pipely/reference_list_spec.rb +45 -0
- data/spec/lib/pipely_spec.rb +38 -0
- metadata +125 -0
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
Bundler::GemHelper.install_tasks
|
9
|
+
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
require 'cane/rake_task'
|
12
|
+
|
13
|
+
RSpec::Core::RakeTask.new do |t|
|
14
|
+
t.pattern = 'spec/**/*_spec.rb'
|
15
|
+
end
|
16
|
+
|
17
|
+
Cane::RakeTask.new(:quality) do |cane|
|
18
|
+
cane.canefile = '.cane'
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => [:spec, :quality]
|
data/bin/pipely
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pipely'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
output_path = nil
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: pipely [options] definition.json"
|
10
|
+
|
11
|
+
opts.on("-o", "--output PATH",
|
12
|
+
"Path to write PNG file(s) of graph visualization(s)") do |output|
|
13
|
+
output_path = output
|
14
|
+
end
|
15
|
+
end.parse!
|
16
|
+
|
17
|
+
definition_file = ARGV.last
|
18
|
+
definition_json = File.open(definition_file).read
|
19
|
+
|
20
|
+
output_base = File.basename(definition_file,".*") + '.png'
|
21
|
+
output_file = output_path ? File.join(output_path, output_base) : output_base
|
22
|
+
|
23
|
+
puts "Generating #{output_file}"
|
24
|
+
Pipely.draw(definition_json, output_file)
|
data/lib/pipely.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pipely/definition'
|
2
|
+
require 'pipely/graph_builder'
|
3
|
+
|
4
|
+
# The top-level module for this gem. It provides the recommended public
|
5
|
+
# interface for using Pipely to visualize and manipulate your Data Pipeline
|
6
|
+
# definitions.
|
7
|
+
#
|
8
|
+
module Pipely
|
9
|
+
|
10
|
+
def self.draw(definition_json, filename, node_attributes=nil)
|
11
|
+
definition = Definition.parse(definition_json)
|
12
|
+
definition.apply_node_attributes(node_attributes) if node_attributes
|
13
|
+
|
14
|
+
graph_builder = GraphBuilder.new
|
15
|
+
|
16
|
+
graph = graph_builder.build(definition.components_for_graph)
|
17
|
+
graph.output( :png => filename )
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'virtus'
|
2
|
+
require 'pipely/reference_list'
|
3
|
+
|
4
|
+
module Pipely
|
5
|
+
|
6
|
+
# Represents a Component within a Data Pipeline Definition
|
7
|
+
# http://amzn.to/16lbBKx
|
8
|
+
#
|
9
|
+
class Component
|
10
|
+
|
11
|
+
REFERENCE_KEYS = [
|
12
|
+
'dependsOn',
|
13
|
+
'input',
|
14
|
+
'output',
|
15
|
+
'runsOn',
|
16
|
+
'schedule',
|
17
|
+
'onFail',
|
18
|
+
'onSuccess',
|
19
|
+
'dataFormat',
|
20
|
+
]
|
21
|
+
|
22
|
+
STATE_COLORS = {
|
23
|
+
'FINISHED' => 'deepskyblue1',
|
24
|
+
'RUNNING' => 'chartreuse',
|
25
|
+
'WAITING_ON_DEPENDENCIES' => 'gray',
|
26
|
+
'WAITING_FOR_RUNNER' => 'bisque4',
|
27
|
+
'FAILED' => 'orangered',
|
28
|
+
}
|
29
|
+
|
30
|
+
include Virtus
|
31
|
+
|
32
|
+
attribute :id, String
|
33
|
+
attribute :type, String
|
34
|
+
attribute :color, String
|
35
|
+
attribute :execution_state, String
|
36
|
+
|
37
|
+
attribute :dependsOn, ReferenceList
|
38
|
+
attribute :input, ReferenceList
|
39
|
+
attribute :output, ReferenceList
|
40
|
+
attribute :runsOn, ReferenceList
|
41
|
+
attribute :schedule, ReferenceList
|
42
|
+
attribute :onFail, ReferenceList
|
43
|
+
attribute :onSuccess, ReferenceList
|
44
|
+
attribute :dataFormat, ReferenceList
|
45
|
+
|
46
|
+
def initialize(args)
|
47
|
+
@original_args = args.clone
|
48
|
+
super
|
49
|
+
coerce_references
|
50
|
+
end
|
51
|
+
|
52
|
+
def coerce_references
|
53
|
+
REFERENCE_KEYS.each do |key|
|
54
|
+
value = send(key)
|
55
|
+
unless value.is_a?(ReferenceList)
|
56
|
+
send("#{key}=", ReferenceList.new(value))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def graphviz_options
|
62
|
+
{
|
63
|
+
:shape => 'record',
|
64
|
+
:label => "{#{label}}",
|
65
|
+
:color => color || 'black',
|
66
|
+
:fillcolor => STATE_COLORS[execution_state] || 'white',
|
67
|
+
:style => 'filled',
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def dependencies(scope=nil)
|
72
|
+
deps = dependsOn.build_dependencies('dependsOn') +
|
73
|
+
input.build_dependencies('input') +
|
74
|
+
output.build_dependencies('output')
|
75
|
+
|
76
|
+
if :all == scope
|
77
|
+
deps += runsOn.build_dependencies(:runsOn)
|
78
|
+
deps += schedule.build_dependencies(:schedule)
|
79
|
+
deps += onFail.build_dependencies(:onFail)
|
80
|
+
deps += onSuccess.build_dependencies(:onSuccess)
|
81
|
+
deps += dataFormat.build_dependencies(:dataFormat)
|
82
|
+
end
|
83
|
+
|
84
|
+
deps
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_json(options={})
|
88
|
+
h = @original_args
|
89
|
+
|
90
|
+
REFERENCE_KEYS.each do |key|
|
91
|
+
value = send(key)
|
92
|
+
|
93
|
+
if value.present?
|
94
|
+
h[key] = value
|
95
|
+
else
|
96
|
+
h.delete(key)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
h.to_json(options)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def label
|
106
|
+
[id, type, execution_state].compact.join('|')
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pipely/component'
|
3
|
+
|
4
|
+
module Pipely
|
5
|
+
|
6
|
+
# Pipely's representation of a Pipeline Definition for AWS Data Pipeline
|
7
|
+
# http://amzn.to/1bpW8Ru
|
8
|
+
#
|
9
|
+
class Definition
|
10
|
+
|
11
|
+
# Showing all component types leads to an unwieldy graph.
|
12
|
+
# TODO: make this list configurable.
|
13
|
+
NON_GRAPH_COMPONENT_TYPES = [
|
14
|
+
'Schedule',
|
15
|
+
'SnsAlarm',
|
16
|
+
'Ec2Resource',
|
17
|
+
'EmrCluster',
|
18
|
+
'CSV',
|
19
|
+
nil,
|
20
|
+
]
|
21
|
+
|
22
|
+
def self.parse(content)
|
23
|
+
objects = JSON.parse(content)['objects']
|
24
|
+
components = objects.map{|obj| Component.new(obj)}
|
25
|
+
|
26
|
+
new(components)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(components)
|
30
|
+
@components = components
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :components
|
34
|
+
|
35
|
+
def components_for_graph
|
36
|
+
components.reject { |component|
|
37
|
+
NON_GRAPH_COMPONENT_TYPES.include?(component['type'])
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_json
|
42
|
+
{ :objects => components }.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def apply_component_attributes(component_attributes)
|
46
|
+
self.components.each do |component|
|
47
|
+
if attributes = component_attributes[component.id]
|
48
|
+
component.attributes = attributes
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def get_components(target_component_ids)
|
56
|
+
components.select { |component|
|
57
|
+
target_component_ids.include?(component.id)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def dependencies_of(selected_components)
|
62
|
+
all_dependencies = selected_components.map { |component|
|
63
|
+
component.dependencies(:all)
|
64
|
+
}.flatten.uniq
|
65
|
+
|
66
|
+
Set.new(get_components(all_dependencies.map(&:target_id)))
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'graphviz'
|
3
|
+
|
4
|
+
module Pipely
|
5
|
+
|
6
|
+
# Builds a GraphViz graph from a set of Components and their Dependencies
|
7
|
+
class GraphBuilder
|
8
|
+
|
9
|
+
def initialize(graph=nil)
|
10
|
+
@graph = graph || GraphViz.new(:G, :type => :digraph)
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(components)
|
14
|
+
add_nodes(components)
|
15
|
+
add_edges(components)
|
16
|
+
@graph
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Represent Components as nodes on the graph
|
22
|
+
def add_nodes(components)
|
23
|
+
components.each do |component|
|
24
|
+
@graph.add_nodes(component.id, component.graphviz_options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Represent Dependencies as edges on the graph
|
29
|
+
def add_edges(components)
|
30
|
+
components.each do |component|
|
31
|
+
component.dependencies.each do |dependency|
|
32
|
+
options = {
|
33
|
+
:label => dependency.label,
|
34
|
+
:color => dependency.color,
|
35
|
+
}
|
36
|
+
|
37
|
+
options[:dir] = 'back' if ('input' == dependency.label)
|
38
|
+
|
39
|
+
@graph.add_edges(
|
40
|
+
component.id,
|
41
|
+
dependency.target_id,
|
42
|
+
options
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pipely/dependency'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
|
5
|
+
# A list of references to Components for managing dependencies
|
6
|
+
#
|
7
|
+
class ReferenceList
|
8
|
+
|
9
|
+
def initialize(input)
|
10
|
+
@raw_references = [input].flatten.compact
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_dependencies(label)
|
14
|
+
@raw_references.map{|h| Dependency.new(label, h['ref'])}
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_json(options={})
|
18
|
+
if 1 == @raw_references.count
|
19
|
+
@raw_references.first.to_json(options)
|
20
|
+
else
|
21
|
+
@raw_references.to_json(options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def present?
|
26
|
+
!@raw_references.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'pipely/component'
|
2
|
+
require 'pipely/dependency'
|
3
|
+
|
4
|
+
describe Pipely::Component do
|
5
|
+
|
6
|
+
subject {
|
7
|
+
described_class.new(
|
8
|
+
id: 'my-component',
|
9
|
+
type: 'OreoSalad',
|
10
|
+
dependsOn: {'ref' => 'asdf'},
|
11
|
+
input: {'ref' => 'infile'},
|
12
|
+
output: {'ref' => 'outfile'},
|
13
|
+
color: 'yellow',
|
14
|
+
execution_state: 'WAITING_FOR_RUNNER',
|
15
|
+
)
|
16
|
+
}
|
17
|
+
|
18
|
+
it 'coerces dependsOn into a ReferenceList' do
|
19
|
+
expect(subject.dependsOn).to be_a(Pipely::ReferenceList)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#graphviz_options' do
|
23
|
+
it 'builds properties for graphviz node representing this component' do
|
24
|
+
expect(subject.graphviz_options).to eq({
|
25
|
+
:shape => 'record',
|
26
|
+
:label => '{my-component|OreoSalad|WAITING_FOR_RUNNER}',
|
27
|
+
:color => 'yellow',
|
28
|
+
:fillcolor => 'bisque4',
|
29
|
+
:style => 'filled',
|
30
|
+
})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#dependencies' do
|
35
|
+
it 'includes dependsOn edges' do
|
36
|
+
expect(subject.dependencies).to eq([
|
37
|
+
Pipely::Dependency.new('dependsOn', 'asdf'),
|
38
|
+
Pipely::Dependency.new('input', 'infile'),
|
39
|
+
Pipely::Dependency.new('output', 'outfile'),
|
40
|
+
])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'pipely/definition'
|
2
|
+
|
3
|
+
describe Pipely::Definition do
|
4
|
+
|
5
|
+
subject { described_class.parse(definition_json) }
|
6
|
+
|
7
|
+
let(:definition_json) {
|
8
|
+
<<EOF
|
9
|
+
{
|
10
|
+
"objects": [
|
11
|
+
{
|
12
|
+
"id": "DoStuff",
|
13
|
+
"type": "ShellCommandActivity",
|
14
|
+
"onFail": { "ref": "FailureNotify" }
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"id": "FailureNotify",
|
18
|
+
"type": "SnsAlarm"
|
19
|
+
}
|
20
|
+
]
|
21
|
+
}
|
22
|
+
EOF
|
23
|
+
}
|
24
|
+
|
25
|
+
describe '#components' do
|
26
|
+
it 'builds a Component for each object in the definition JSON' do
|
27
|
+
expect(subject.components.count).to eq(2)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#components_for_graph' do
|
32
|
+
it 'filters out node types we do not want on the graph' do
|
33
|
+
expect(subject.components_for_graph.count).to eq(1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#to_json' do
|
38
|
+
it 'renders the components as JSON' do
|
39
|
+
original = JSON.parse(definition_json)
|
40
|
+
expect(JSON.parse(subject.to_json)).to eq(original)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#apply_component_attributes' do
|
45
|
+
it 'applies attributes to nodes with matching ids' do
|
46
|
+
subject.apply_component_attributes({
|
47
|
+
'DoStuff' => { color: 'pink' },
|
48
|
+
})
|
49
|
+
|
50
|
+
pink_node = subject.components.detect{|n| n.id == 'DoStuff'}
|
51
|
+
|
52
|
+
expect(pink_node.color).to eq('pink')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pipely/graph_builder'
|
2
|
+
|
3
|
+
describe Pipely::GraphBuilder do
|
4
|
+
|
5
|
+
let(:graph) { stub(:graph) }
|
6
|
+
|
7
|
+
let(:node1) {
|
8
|
+
Pipely::Component.new(
|
9
|
+
:id => '1',
|
10
|
+
:dependsOn => { 'ref' => '2' },
|
11
|
+
)
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:node2) {
|
15
|
+
Pipely::Component.new(
|
16
|
+
:id => '2',
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
subject { described_class.new(graph) }
|
21
|
+
|
22
|
+
describe '#build' do
|
23
|
+
it 'builds a graph from a list of Components' do
|
24
|
+
graph.should_receive(:add_nodes).
|
25
|
+
with(node1.id, node1.graphviz_options).ordered
|
26
|
+
|
27
|
+
graph.should_receive(:add_nodes).
|
28
|
+
with(node2.id, node2.graphviz_options).ordered
|
29
|
+
|
30
|
+
graph.should_receive(:add_edges).
|
31
|
+
with(
|
32
|
+
node1.id,
|
33
|
+
node2.id,
|
34
|
+
:label => 'dependsOn',
|
35
|
+
:color => 'black',
|
36
|
+
).ordered
|
37
|
+
|
38
|
+
subject.build([node1, node2])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pipely/reference_list'
|
2
|
+
|
3
|
+
describe Pipely::ReferenceList do
|
4
|
+
|
5
|
+
context 'given nil input' do
|
6
|
+
subject { described_class.new(nil) }
|
7
|
+
|
8
|
+
describe '#build_dependencies' do
|
9
|
+
it 'returns an empty array' do
|
10
|
+
expect(subject.build_dependencies('dependsOn')).to eq([])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'given a single input' do
|
16
|
+
subject { described_class.new({ 'ref' => 'foo' }) }
|
17
|
+
|
18
|
+
describe '#build_dependencies' do
|
19
|
+
it 'returns an array of the single reference' do
|
20
|
+
expect(subject.build_dependencies('dependsOn')).to eq([
|
21
|
+
Pipely::Dependency.new('dependsOn', 'foo'),
|
22
|
+
])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'given an array of references as input' do
|
28
|
+
subject {
|
29
|
+
described_class.new([
|
30
|
+
{ 'ref' => 'foo' },
|
31
|
+
{ 'ref' => 'bar' },
|
32
|
+
])
|
33
|
+
}
|
34
|
+
|
35
|
+
describe '#build_dependencies' do
|
36
|
+
it 'returns an array of the single reference' do
|
37
|
+
expect(subject.build_dependencies('dependsOn')).to eq([
|
38
|
+
Pipely::Dependency.new('dependsOn', 'foo'),
|
39
|
+
Pipely::Dependency.new('dependsOn', 'bar'),
|
40
|
+
])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'pipely'
|
2
|
+
|
3
|
+
describe Pipely do
|
4
|
+
let(:definition_json) { stub }
|
5
|
+
let(:filename) { stub }
|
6
|
+
let(:definition) { stub }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Pipely::Definition.stub(:parse).with(definition_json) { definition }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.draw' do
|
13
|
+
let(:components) { stub }
|
14
|
+
let(:definition) { stub(:definition, :components_for_graph => components) }
|
15
|
+
let(:graph) { stub(:graph, :output => nil) }
|
16
|
+
|
17
|
+
before do
|
18
|
+
Pipely::GraphBuilder.any_instance.stub(:build).with(components) { graph }
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'parses a JSON definition and builds a graph' do
|
22
|
+
graph.should_receive(:output).with(:png => filename)
|
23
|
+
|
24
|
+
described_class.draw(definition_json, filename)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with node_attributes' do
|
28
|
+
let(:node_attributes) { stub }
|
29
|
+
|
30
|
+
it 'applies the node_attributes to the definition' do
|
31
|
+
definition.should_receive(:apply_node_attributes).with(node_attributes)
|
32
|
+
|
33
|
+
described_class.draw(definition_json, filename, node_attributes)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pipely
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Gillooly
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-02 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby-graphviz
|
16
|
+
requirement: &70104078435520 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70104078435520
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70104078435100 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70104078435100
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: virtus
|
38
|
+
requirement: &70104078434680 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70104078434680
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &70104078434260 !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: *70104078434260
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: cane
|
60
|
+
requirement: &70104078433840 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70104078433840
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- matt@swipely.com
|
72
|
+
executables:
|
73
|
+
- pipely
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/pipely/component.rb
|
78
|
+
- lib/pipely/definition.rb
|
79
|
+
- lib/pipely/dependency.rb
|
80
|
+
- lib/pipely/graph_builder.rb
|
81
|
+
- lib/pipely/reference_list.rb
|
82
|
+
- lib/pipely/version.rb
|
83
|
+
- lib/pipely.rb
|
84
|
+
- Rakefile
|
85
|
+
- README.md
|
86
|
+
- spec/lib/pipely/component_spec.rb
|
87
|
+
- spec/lib/pipely/definition_spec.rb
|
88
|
+
- spec/lib/pipely/dependency_spec.rb
|
89
|
+
- spec/lib/pipely/graph_builder_spec.rb
|
90
|
+
- spec/lib/pipely/reference_list_spec.rb
|
91
|
+
- spec/lib/pipely_spec.rb
|
92
|
+
- !binary |-
|
93
|
+
YmluL3BpcGVseQ==
|
94
|
+
homepage: http://github.com/swipely/pipely
|
95
|
+
licenses: []
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.11
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Generate dependency graphs from pipeline definitions.
|
118
|
+
test_files:
|
119
|
+
- spec/lib/pipely/component_spec.rb
|
120
|
+
- spec/lib/pipely/definition_spec.rb
|
121
|
+
- spec/lib/pipely/dependency_spec.rb
|
122
|
+
- spec/lib/pipely/graph_builder_spec.rb
|
123
|
+
- spec/lib/pipely/reference_list_spec.rb
|
124
|
+
- spec/lib/pipely_spec.rb
|
125
|
+
has_rdoc:
|