cards 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +8 -0
- data/Manifest.txt +29 -0
- data/README.txt +44 -0
- data/Rakefile +54 -0
- data/lib/cards.rb +17 -0
- data/lib/cards/builder.rb +9 -0
- data/lib/cards/card.rb +44 -0
- data/lib/cards/card_wall.rb +95 -0
- data/lib/cards/csv_builder.rb +46 -0
- data/lib/cards/csv_parser.rb +60 -0
- data/lib/cards/extensions.rb +59 -0
- data/lib/cards/layouts/column_layout.rb +34 -0
- data/lib/cards/layouts/nil_layout.rb +14 -0
- data/lib/cards/layouts/row_layout.rb +21 -0
- data/lib/cards/master_story_list.rb +24 -0
- data/lib/cards/swim_lanes.rb +9 -0
- data/lib/cards/text.rb +23 -0
- data/lib/cards/tracker_csv.rb +19 -0
- data/lib/cards/writers/dot_writer.rb +85 -0
- data/lib/cards/writers/graffle_writer.rb +69 -0
- data/lib/cards/writers/text_writer.rb +23 -0
- data/spec/cards/card_spec.rb +141 -0
- data/spec/cards/card_wall_spec.rb +76 -0
- data/spec/cards/csv_parser_spec.rb +28 -0
- data/spec/cards/extensions_spec.rb +24 -0
- data/spec/cards/swim_lanes_spec.rb +17 -0
- data/spec/cards/writers/dot_writer_spec.rb +70 -0
- data/spec/cards/writers/text_writer_spec.rb +26 -0
- data/spec/spec_helper.rb +13 -0
- metadata +92 -0
data/CHANGES.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
CHANGES.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/cards.rb
|
6
|
+
lib/cards/builder.rb
|
7
|
+
lib/cards/card.rb
|
8
|
+
lib/cards/card_wall.rb
|
9
|
+
lib/cards/csv_builder.rb
|
10
|
+
lib/cards/csv_parser.rb
|
11
|
+
lib/cards/extensions.rb
|
12
|
+
lib/cards/layouts/column_layout.rb
|
13
|
+
lib/cards/layouts/nil_layout.rb
|
14
|
+
lib/cards/layouts/row_layout.rb
|
15
|
+
lib/cards/master_story_list.rb
|
16
|
+
lib/cards/swim_lanes.rb
|
17
|
+
lib/cards/text.rb
|
18
|
+
lib/cards/tracker_csv.rb
|
19
|
+
lib/cards/writers/dot_writer.rb
|
20
|
+
lib/cards/writers/graffle_writer.rb
|
21
|
+
lib/cards/writers/text_writer.rb
|
22
|
+
spec/cards/card_spec.rb
|
23
|
+
spec/cards/card_wall_spec.rb
|
24
|
+
spec/cards/csv_parser_spec.rb
|
25
|
+
spec/cards/extensions_spec.rb
|
26
|
+
spec/cards/swim_lanes_spec.rb
|
27
|
+
spec/cards/writers/dot_writer_spec.rb
|
28
|
+
spec/cards/writers/text_writer_spec.rb
|
29
|
+
spec/spec_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= Project: CardWallGen
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
CardWallGen is a program that reads a requirements map and generates a view
|
6
|
+
of it. Typically it reads it from excel or csv and generates a graphical
|
7
|
+
view in visio or omnigraffle, though it may also generate another csv view of it.
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
We try to make it simple to configure. However, you still need a driver file
|
12
|
+
that might look something like :
|
13
|
+
|
14
|
+
CardWall.from csv_file("Business Goals") do
|
15
|
+
column :goal
|
16
|
+
end
|
17
|
+
|
18
|
+
CardWall.from csv_file("Current Workflow") do
|
19
|
+
row :activity
|
20
|
+
column :task
|
21
|
+
end
|
22
|
+
|
23
|
+
CardWall.from csv_file("Stories") do
|
24
|
+
row :activity
|
25
|
+
row :task
|
26
|
+
column :story, :wrap_at => 4 do |card, row|
|
27
|
+
card.name << "\n\n#{row[:description]}" unless row[:description].blank?
|
28
|
+
card.name << "\n\n "
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
MasterStoryList.from csv_file("Stories")
|
33
|
+
|
34
|
+
TrackerCsv.from csv_file("Stories")
|
35
|
+
|
36
|
+
== Contact
|
37
|
+
|
38
|
+
Author:: Jeremy Stell-Smith
|
39
|
+
Email:: jeremystellsmith@gmail.com
|
40
|
+
License:: LGPL License
|
41
|
+
|
42
|
+
== Home Page
|
43
|
+
|
44
|
+
http://rubyforge.org/projects/cardwallgen
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
ENV["FILE"] ||= "/Users/jeremy/Desktop/PunchList Master Story List/Card Wall-Card Wall.csv"
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem "rspec"
|
5
|
+
gem "hoe"
|
6
|
+
|
7
|
+
require 'rake/clean'
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
require 'hoe'
|
10
|
+
|
11
|
+
require 'lib/cards'
|
12
|
+
|
13
|
+
desc "Default Task"
|
14
|
+
task :default => [:spec]
|
15
|
+
|
16
|
+
task :test => :spec
|
17
|
+
|
18
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
19
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "export to dot file"
|
23
|
+
task :run_dot do
|
24
|
+
file = ENV["FILE"] || SAMPLE_FILE
|
25
|
+
file.as(:png).delete_if_exists
|
26
|
+
file.as(:dot).delete_if_exists
|
27
|
+
|
28
|
+
CardWall.convert(file, :dot, file.as(:dot))
|
29
|
+
|
30
|
+
"dot -Tpng #{file.as(:dot).no_spaces} > #{file.as(:png).no_spaces}".run
|
31
|
+
"open #{file.as(:png).no_spaces}".run if file.as(:png).exists?
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "export to text"
|
35
|
+
task :run_text do
|
36
|
+
CardWall.convert(ENV["FILE"] || SAMPLE_FILE, :text)
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "export to graffle"
|
40
|
+
task :run do
|
41
|
+
file = ENV["FILE"] || SAMPLE_FILE
|
42
|
+
file.as(:graffle).delete_if_exists
|
43
|
+
|
44
|
+
CardWall.convert(file, :graffle, file.as(:graffle))
|
45
|
+
end
|
46
|
+
|
47
|
+
Hoe.new('cards', Cards::VERSION) do |p|
|
48
|
+
p.rubyforge_name = 'cardwallgen'
|
49
|
+
p.summary = p.description = p.paragraphs_of('README.txt', 2).first
|
50
|
+
p.url = p.paragraphs_of('README.txt', -1).first.strip
|
51
|
+
p.author = 'Jeremy Stell-Smith'
|
52
|
+
p.email = 'jeremystellsmith@gmail.com'
|
53
|
+
p.changes = p.paragraphs_of('CHANGES.txt', 0..2).join("\n\n")
|
54
|
+
end
|
data/lib/cards.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'cards/extensions'
|
4
|
+
require 'cards/builder'
|
5
|
+
require 'cards/csv_parser'
|
6
|
+
|
7
|
+
require 'cards/card_wall'
|
8
|
+
# writers
|
9
|
+
require 'cards/writers/dot_writer'
|
10
|
+
require 'cards/writers/graffle_writer'
|
11
|
+
|
12
|
+
require 'cards/master_story_list'
|
13
|
+
require 'cards/tracker_csv'
|
14
|
+
|
15
|
+
module Cards
|
16
|
+
VERSION = "0.6"
|
17
|
+
end
|
data/lib/cards/card.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Cards
|
2
|
+
class Card
|
3
|
+
attr_accessor :name, :color, :children, :x, :y
|
4
|
+
attr_writer :layout
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
@x, @y = 0, 0
|
9
|
+
@children = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(*cards)
|
13
|
+
@children += cards
|
14
|
+
end
|
15
|
+
|
16
|
+
def visit(&block)
|
17
|
+
yield self
|
18
|
+
@children.each {|c| c.visit(&block) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def layout
|
22
|
+
my_layout.layout(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def width
|
26
|
+
return 1 if children.empty?
|
27
|
+
my_layout.width(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def height
|
31
|
+
return 1 if children.empty?
|
32
|
+
my_layout.height(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def cell() [x, y]; end
|
36
|
+
def cell=(cells) self.x, self.y = *cells; end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def my_layout
|
41
|
+
@layout || NilLayout.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'cards/builder'
|
2
|
+
|
3
|
+
module Cards
|
4
|
+
class CardWall < Builder
|
5
|
+
def define(&block)
|
6
|
+
DefinitionContext.new(self).instance_eval(&block)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.writer=(writer_class)
|
11
|
+
@writer_class = writer_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.colors
|
15
|
+
@colors ||= {
|
16
|
+
:role => :blue,
|
17
|
+
:activity => :blue,
|
18
|
+
:task => :red,
|
19
|
+
:story => :yellow
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.from(parser, &block)
|
24
|
+
builder = self.new.define(&block)
|
25
|
+
|
26
|
+
parser.each_row do |row|
|
27
|
+
builder.process(row)
|
28
|
+
end
|
29
|
+
|
30
|
+
builder.show(@writer_class.new(parser.default_output_file))
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@handlers = []
|
35
|
+
@layouts = []
|
36
|
+
@root = Card.new("root")
|
37
|
+
@root.layout = RowLayout.new
|
38
|
+
@root.y = -1
|
39
|
+
@last_cards = [@root]
|
40
|
+
end
|
41
|
+
|
42
|
+
def process(row)
|
43
|
+
@handlers.each do |handler|
|
44
|
+
unless row[handler].blank?
|
45
|
+
send("add_#{handler}", row[handler], row)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_handler(type, layout, &block)
|
51
|
+
i = @handlers.size
|
52
|
+
@handlers << type
|
53
|
+
@root.layout = layout if @layouts.empty?
|
54
|
+
@layouts << layout
|
55
|
+
(class << self; self; end).module_eval do
|
56
|
+
define_method("add_#{type}") do |name, row|
|
57
|
+
card = yield(name, row)
|
58
|
+
card.layout = @layouts[i+1]
|
59
|
+
card.color ||= CardWall.colors[type] || :white
|
60
|
+
@last_cards[i].add(card)
|
61
|
+
@last_cards[i+1] = card
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def show(output)
|
67
|
+
@root.layout
|
68
|
+
@root.visit {|card| output.create_card(card.name, card.color, card.cell) unless card == @root}
|
69
|
+
end
|
70
|
+
|
71
|
+
class DefinitionContext
|
72
|
+
def initialize(wraps)
|
73
|
+
@this = wraps
|
74
|
+
@i = 0
|
75
|
+
end
|
76
|
+
|
77
|
+
def row(name, *options)
|
78
|
+
@this.add_handler(name, RowLayout.new(*options)) do |name, row|
|
79
|
+
Card.new(name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def column(name, *options)
|
84
|
+
@this.add_handler(name, ColumnLayout.new(*options)) do |name, row|
|
85
|
+
c = Card.new(name)
|
86
|
+
yield c, row if block_given?
|
87
|
+
c
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def lanes(name, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class CsvBuilder
|
2
|
+
def initialize(file_name)
|
3
|
+
@file_name = file_name
|
4
|
+
@file = File.open(file_name, "wb")
|
5
|
+
@csv = CSV::Writer.create(@file)
|
6
|
+
@csv << header
|
7
|
+
end
|
8
|
+
|
9
|
+
def header
|
10
|
+
raise "implement me"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.from(parser)
|
14
|
+
builder = self.new(parser.default_output_file + ".#{self.to_s.gsub("Cards::", "").downcase}.csv")
|
15
|
+
|
16
|
+
parser.each_row do |row|
|
17
|
+
builder.process(row)
|
18
|
+
end
|
19
|
+
|
20
|
+
builder.done
|
21
|
+
end
|
22
|
+
|
23
|
+
def process(row)
|
24
|
+
[:activity, :task, :story].each do |handler|
|
25
|
+
send("add_#{handler}", row[handler], row) if row[handler]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_activity(name, params = {})
|
30
|
+
@activity = name
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_task(name, params = {})
|
34
|
+
@task = name
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_story(name, params = {})
|
38
|
+
raise "implement me"
|
39
|
+
end
|
40
|
+
|
41
|
+
def done
|
42
|
+
@csv.close
|
43
|
+
@file.close
|
44
|
+
`open #{@file_name.gsub(' ', '\\ ')}`
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Cards
|
4
|
+
class CsvParser
|
5
|
+
def initialize(file)
|
6
|
+
@file = file
|
7
|
+
end
|
8
|
+
|
9
|
+
def rows
|
10
|
+
rows = []
|
11
|
+
each_row {|row| rows << row}
|
12
|
+
rows
|
13
|
+
end
|
14
|
+
|
15
|
+
def each_row
|
16
|
+
header = nil
|
17
|
+
CSV.open(@file, 'r') do |row|
|
18
|
+
if header.nil?
|
19
|
+
header = define_header(row)
|
20
|
+
else
|
21
|
+
yield Row.new(header, row)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_output_file
|
27
|
+
File.basename(@file).sub(/\..*/, '')
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def define_header(header_row)
|
33
|
+
header = {}
|
34
|
+
header_row.each_with_index do |name, i|
|
35
|
+
header[name.downcase.gsub(' ', '_').to_sym] = i
|
36
|
+
end
|
37
|
+
return header
|
38
|
+
end
|
39
|
+
|
40
|
+
class Row
|
41
|
+
def initialize(header, row)
|
42
|
+
@header, @row = header, row
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](key)
|
46
|
+
index = @header[key] || raise("can't find column header #{key.inspect} in #{@header.inspect}")
|
47
|
+
@row[index]
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_h
|
51
|
+
h = {}
|
52
|
+
@header.each_key do |column_header|
|
53
|
+
val = @row[@header[column_header]]
|
54
|
+
h[column_header] = val unless val.blank?
|
55
|
+
end
|
56
|
+
h
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class String
|
2
|
+
def blank?
|
3
|
+
self.size == 0 || self.strip.size == 0
|
4
|
+
end
|
5
|
+
|
6
|
+
def no_spaces
|
7
|
+
self.gsub(' ', '\\ ')
|
8
|
+
end
|
9
|
+
|
10
|
+
def as(extension)
|
11
|
+
self.gsub(/\.[^\/]+$/, '') + ".#{extension}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def exists?
|
15
|
+
File.exist?(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_if_exists
|
19
|
+
File.delete(self) if self.exists?
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
puts self
|
24
|
+
`#{self}`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Array
|
29
|
+
def sum
|
30
|
+
s = 0
|
31
|
+
each {|i| s += i}
|
32
|
+
s
|
33
|
+
end
|
34
|
+
|
35
|
+
def widths
|
36
|
+
map {|c| c.width}
|
37
|
+
end
|
38
|
+
|
39
|
+
def heights
|
40
|
+
map {|c| c.height}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Numeric
|
45
|
+
def inches # as points
|
46
|
+
self * 72.0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Object
|
51
|
+
def blank?
|
52
|
+
nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def dump_methods(ignore = Object)
|
56
|
+
ignore = ignore.instance_methods if ignore.is_a? Class
|
57
|
+
p (methods - ignore).sort
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module Cards
|
4
|
+
class ColumnLayout
|
5
|
+
def initialize(options = {})
|
6
|
+
@wrap_at = options.delete(:wrap_at) || 10
|
7
|
+
end
|
8
|
+
|
9
|
+
def layout(card)
|
10
|
+
x = card.x
|
11
|
+
card.children.each_slice(@wrap_at) do |slice|
|
12
|
+
y = card.y + 1
|
13
|
+
slice.each do |child|
|
14
|
+
child.x, child.y = x, y
|
15
|
+
child.layout
|
16
|
+
y += child.height
|
17
|
+
end
|
18
|
+
x += slice.widths.max
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def width(card)
|
23
|
+
width = 0
|
24
|
+
card.children.each_slice(@wrap_at) do |slice|
|
25
|
+
width += slice.widths.max
|
26
|
+
end
|
27
|
+
width
|
28
|
+
end
|
29
|
+
|
30
|
+
def height(card)
|
31
|
+
card.children.heights.sum
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Cards
|
2
|
+
class RowLayout
|
3
|
+
def layout(card)
|
4
|
+
x = card.x
|
5
|
+
card.children.each_with_index do |child, i|
|
6
|
+
child.x = x
|
7
|
+
child.y = card.y + 1
|
8
|
+
child.layout
|
9
|
+
x += child.width
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def width(card)
|
14
|
+
card.children.map {|c| c.width}.sum
|
15
|
+
end
|
16
|
+
|
17
|
+
def height(card)
|
18
|
+
card.children.map {|c| c.height}.max
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'cards/csv_builder'
|
2
|
+
|
3
|
+
module Cards
|
4
|
+
# this is an example of a custom csv builder that you can create
|
5
|
+
class MasterStoryList < CsvBuilder
|
6
|
+
def header
|
7
|
+
["Activity", "Task", "Story", "Description", "Estimate", "Phase", "Priority"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_story(name, params = {})
|
11
|
+
return unless [nil, '', 'Story', 'Chore'].include?(params[:story_type])
|
12
|
+
|
13
|
+
@csv << [
|
14
|
+
@activity,
|
15
|
+
@task,
|
16
|
+
name,
|
17
|
+
params[:description],
|
18
|
+
nil, # params[:estimate],
|
19
|
+
nil, # params[:phase],
|
20
|
+
nil # params[:priority]
|
21
|
+
]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/cards/text.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class Text
|
2
|
+
def add_role(name, params = {})
|
3
|
+
puts name
|
4
|
+
end
|
5
|
+
|
6
|
+
def add_activity(name, params = {})
|
7
|
+
puts " #{name}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_task(name, params = {})
|
11
|
+
puts " #{name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_story(name, params = {})
|
15
|
+
string = " #{name}"
|
16
|
+
string << " - #{params[:description]}" unless params[:description].blank?
|
17
|
+
string << " - #{params[:estimate]}" unless params[:estimate].blank?
|
18
|
+
puts string
|
19
|
+
end
|
20
|
+
|
21
|
+
def done
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'cards/csv_builder'
|
2
|
+
|
3
|
+
class TrackerCsv < CsvBuilder
|
4
|
+
def header
|
5
|
+
["Story", "Labels", "Story Type", "Estimate", "Description"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_story(name, params = {})
|
9
|
+
return unless [nil, '', 'Story', 'Chore'].include?(params[:story_type])
|
10
|
+
|
11
|
+
@csv << [
|
12
|
+
name,
|
13
|
+
@task,
|
14
|
+
params[:story_type],
|
15
|
+
nil, # params[:estimate],
|
16
|
+
"#{@activity}\n#{params[:description]}"
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Cards
|
2
|
+
class DotWriter
|
3
|
+
def initialize(file)
|
4
|
+
@file = file
|
5
|
+
@node_color = {:green => "darkseagreen1", :blue => "lightblue", :red => "pink", :yellow => "lightyellow"}
|
6
|
+
@nodes = []
|
7
|
+
|
8
|
+
puts "digraph G {"
|
9
|
+
puts "graph [nodesep=.1, ranksep=.1, ordering=out];"
|
10
|
+
puts "node [shape=box, color=black, style=filled, width=2, height=1.2, fixedsize=true, labeljust=\"l\"];"
|
11
|
+
puts "edge [style=invis, weight=1];"
|
12
|
+
end
|
13
|
+
|
14
|
+
def puts(string)
|
15
|
+
@file.puts string
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_card(name, color, position)
|
19
|
+
create_node("card", position, %{label="#{wrap(name)}",fillcolor=#{@node_color[color]}})
|
20
|
+
end
|
21
|
+
|
22
|
+
def done
|
23
|
+
create_gap_nodes
|
24
|
+
create_ranks
|
25
|
+
create_edges
|
26
|
+
puts "}"
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def create_node(name, position, properties)
|
32
|
+
node_id = name.gsub(/\s*/, "")[0, 10] << "_" << position.join("_")
|
33
|
+
x = position[0]; y = position[1]
|
34
|
+
column = @nodes[x] == nil ? @nodes[x] = [] : @nodes[x]
|
35
|
+
column[y] = node_id
|
36
|
+
puts %{#{node_id} [#{properties}];}
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_gap_nodes
|
40
|
+
@nodes.each_index do |x|
|
41
|
+
if !@nodes[x]
|
42
|
+
create_gap([x,0])
|
43
|
+
else
|
44
|
+
@nodes[x].each_index do |y|
|
45
|
+
create_gap([x,y]) unless @nodes[x][y]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_gap(position)
|
52
|
+
create_node("gap", position, "style=invis")
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_ranks
|
56
|
+
3.times do |y|
|
57
|
+
rank = "{rank=same"
|
58
|
+
@nodes.each_index { |x| rank += "; #{@nodes[x][y]}" if @nodes[x][y] }
|
59
|
+
rank << "};"
|
60
|
+
puts rank
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_edges
|
65
|
+
(@nodes.length-1).times do |x|
|
66
|
+
create_edge(@nodes[x][0], @nodes[x+1][0])
|
67
|
+
end
|
68
|
+
|
69
|
+
@nodes.each do |column|
|
70
|
+
(column.length-1).times do |y|
|
71
|
+
create_edge(column[y], column[y+1])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_edge(node1, node2)
|
77
|
+
puts %{#{node1} -> #{node2};}
|
78
|
+
end
|
79
|
+
|
80
|
+
def wrap(text)
|
81
|
+
label_width = 20
|
82
|
+
text.gsub(/\n/," ").scan(/\S.{0,#{label_width-2}}\S(?=\s|$)|\S+/).join("\\n")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rb-appscript'
|
3
|
+
gem 'rubyosa'
|
4
|
+
require 'appscript'
|
5
|
+
require 'rbosa'
|
6
|
+
|
7
|
+
module Cards
|
8
|
+
class GraffleWriter
|
9
|
+
CARD_WALL_STENCIL = File.expand_path(File.dirname(__FILE__) + "/CardWall.gstencil")
|
10
|
+
COLORS = {
|
11
|
+
:green => [0xcccc, 0xffff, 0xcccc],
|
12
|
+
:red => [0xffff, 0xcccc, 0xcccc],
|
13
|
+
:yellow => [0xffff, 0xffff, 0xcccc],
|
14
|
+
:blue => [0xcccc, 0xcccc, 0xffff],
|
15
|
+
:white => [0xffff, 0xffff, 0xffff]
|
16
|
+
}
|
17
|
+
CARD_WIDTH = 2.875.inches
|
18
|
+
CARD_HEIGHT = 2.875.inches
|
19
|
+
CARD_PADDING = 0.4.inches
|
20
|
+
|
21
|
+
attr_reader :app
|
22
|
+
|
23
|
+
def initialize(name = "Card Wall")
|
24
|
+
@app = Appscript.app('OmniGraffle 4')
|
25
|
+
@doc = @app.documents[0]
|
26
|
+
puts "opening #{name}"
|
27
|
+
open_document(name)
|
28
|
+
activate
|
29
|
+
clear_shapes
|
30
|
+
end
|
31
|
+
|
32
|
+
def open_document(name)
|
33
|
+
@doc = @app.documents[name]
|
34
|
+
return @doc if @doc.exists
|
35
|
+
|
36
|
+
@doc = @app.make :new => :document, :with_properties => {:name => name}
|
37
|
+
canvas.adjusts_pages.set true
|
38
|
+
@app.windows[name].zoom.set 0.2
|
39
|
+
end
|
40
|
+
|
41
|
+
def clear_shapes
|
42
|
+
canvas.shapes.get.each do |shape|
|
43
|
+
shape.delete
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_card(text, color = :yellow, cell = [0, 0])
|
48
|
+
canvas.make :new => :shape,
|
49
|
+
:with_properties => {
|
50
|
+
:origin => [cell[0] * (CARD_WIDTH + CARD_PADDING),
|
51
|
+
cell[1] * (CARD_HEIGHT + CARD_PADDING)],
|
52
|
+
:size => [CARD_WIDTH, CARD_HEIGHT],
|
53
|
+
:fill_color => COLORS[color],
|
54
|
+
:text => {:size => 16.0, :text => text}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def done
|
59
|
+
end
|
60
|
+
|
61
|
+
def canvas
|
62
|
+
@doc.canvases[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_missing(sym, *args)
|
66
|
+
@app.send(sym, *args)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cards
|
2
|
+
class TextWriter
|
3
|
+
def initialize
|
4
|
+
@rows = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def show(card)
|
8
|
+
create_card(card.name, card.color, card.cell)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_card(name, color, cell)
|
12
|
+
(@rows[cell[1]] ||= [])[cell[0]] = name
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@rows.map{|row| row.map{|val| val ? val : " " }.join }.join("\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
@rows.map{|row| "|#{row.join("|")}|" }.join("\n")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require 'cards/card'
|
4
|
+
require 'cards/layouts/row_layout'
|
5
|
+
require 'cards/layouts/column_layout'
|
6
|
+
include Cards
|
7
|
+
|
8
|
+
module Cards
|
9
|
+
def a() @a ||= new_card("a"); end
|
10
|
+
def b() @b ||= new_card("b"); end
|
11
|
+
def c() @c ||= new_card("c"); end
|
12
|
+
def d() @d ||= new_card("d"); end
|
13
|
+
def e() @e ||= new_card("e"); end
|
14
|
+
def f() @f ||= new_card("f"); end
|
15
|
+
def g() @g ||= new_card("g"); end
|
16
|
+
def h() @h ||= new_card("h"); end
|
17
|
+
def i() @i ||= new_card("i"); end
|
18
|
+
def j() @j ||= new_card("j"); end
|
19
|
+
def k() @k ||= new_card("k"); end
|
20
|
+
def l() @l ||= new_card("l"); end
|
21
|
+
def m() @m ||= new_card("m"); end
|
22
|
+
|
23
|
+
def new_card(name)
|
24
|
+
Card.new(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe Card do
|
29
|
+
include Cards
|
30
|
+
|
31
|
+
it "should have x & y" do
|
32
|
+
a.x = 1
|
33
|
+
a.y = 2
|
34
|
+
a.x.should == 1
|
35
|
+
a.y.should == 2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe Card, "row layout" do
|
40
|
+
include Cards
|
41
|
+
|
42
|
+
it "should have 1 row" do
|
43
|
+
a.add(b, c, d)
|
44
|
+
|
45
|
+
cards_to_s(a).should ==
|
46
|
+
"a
|
47
|
+
bcd"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should show with 2 rows" do
|
51
|
+
a.add(b, g)
|
52
|
+
b.add(c, f)
|
53
|
+
c.add(d, e)
|
54
|
+
|
55
|
+
cards_to_s(a).should ==
|
56
|
+
"a
|
57
|
+
b g
|
58
|
+
c f
|
59
|
+
de"
|
60
|
+
end
|
61
|
+
|
62
|
+
def cards_to_s(card)
|
63
|
+
out = TextWriter.new
|
64
|
+
card.layout
|
65
|
+
card.visit {|c| out.show(c)}
|
66
|
+
out.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def new_card(name)
|
70
|
+
c = Card.new(name)
|
71
|
+
c.layout = RowLayout.new
|
72
|
+
c
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe Card, "column layout" do
|
77
|
+
include Cards
|
78
|
+
|
79
|
+
before do
|
80
|
+
a.layout = RowLayout.new
|
81
|
+
@layout = ColumnLayout.new
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should have 1 column" do
|
85
|
+
a.add(b, c, d)
|
86
|
+
d.add(e, f)
|
87
|
+
c.add(g, h, i)
|
88
|
+
|
89
|
+
cards_to_s(a).should ==
|
90
|
+
"a
|
91
|
+
bcd
|
92
|
+
ge
|
93
|
+
hf
|
94
|
+
i"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should show with 2 rows and then columns" do
|
98
|
+
a.add(b, c, d)
|
99
|
+
a.children.each {|child| child.layout = RowLayout.new}
|
100
|
+
b.add(e, f, g)
|
101
|
+
d.add(h)
|
102
|
+
h.add(i, j)
|
103
|
+
f.add(k, l, m)
|
104
|
+
|
105
|
+
cards_to_s(a).should ==
|
106
|
+
"a
|
107
|
+
b cd
|
108
|
+
efg h
|
109
|
+
k i
|
110
|
+
l j
|
111
|
+
m"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should wrap" do
|
115
|
+
@layout = ColumnLayout.new(:wrap_at => 3)
|
116
|
+
a.add(b, c, d)
|
117
|
+
c.add(e, f, g, h, i, j, k)
|
118
|
+
d.add(l, m)
|
119
|
+
|
120
|
+
cards_to_s(a).should ==
|
121
|
+
"a
|
122
|
+
bc d
|
123
|
+
ehkl
|
124
|
+
fi m
|
125
|
+
gj"
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
def cards_to_s(card)
|
130
|
+
out = TextWriter.new
|
131
|
+
card.layout
|
132
|
+
card.visit {|c| out.show(c)}
|
133
|
+
out.to_s
|
134
|
+
end
|
135
|
+
|
136
|
+
def new_card(name)
|
137
|
+
c = Card.new(name)
|
138
|
+
c.layout = @layout
|
139
|
+
c
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require 'cards/card_wall'
|
4
|
+
include Cards
|
5
|
+
|
6
|
+
describe CardWall, "row, col" do
|
7
|
+
before do
|
8
|
+
@builder = CardWall.new.define do
|
9
|
+
row :activity
|
10
|
+
column :task
|
11
|
+
end
|
12
|
+
@out = TextWriter.new
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should display cards" do
|
16
|
+
@builder.process :activity => 'a', :task => 1
|
17
|
+
@builder.process :task => 2
|
18
|
+
@builder.process :task => 3
|
19
|
+
@builder.process :activity => 'b', :task => 4
|
20
|
+
@builder.process :activity => 'c', :task => 5
|
21
|
+
@builder.process :task => 6
|
22
|
+
@builder.process :task => 7
|
23
|
+
@builder.process :task => 8
|
24
|
+
@builder.process :task => 9
|
25
|
+
@builder.process :task => 0
|
26
|
+
@builder.process :task => 1
|
27
|
+
@builder.process :activity => 'd', :task => 2
|
28
|
+
|
29
|
+
@builder.show(@out)
|
30
|
+
@out.to_s.should ==
|
31
|
+
"abcd
|
32
|
+
1452
|
33
|
+
2 6
|
34
|
+
3 7
|
35
|
+
8
|
36
|
+
9
|
37
|
+
0
|
38
|
+
1"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe CardWall, "row, row, col" do
|
43
|
+
before do
|
44
|
+
@builder = CardWall.new.define do
|
45
|
+
row :activity
|
46
|
+
row :task
|
47
|
+
column :story
|
48
|
+
end
|
49
|
+
@out = TextWriter.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should display cards" do
|
53
|
+
@builder.process :activity => 'a', :task => 1, :story => 'A'
|
54
|
+
@builder.process :task => 2, :story => 'B'
|
55
|
+
@builder.process :story => 'C'
|
56
|
+
@builder.process :task => 3, :story => 'D'
|
57
|
+
@builder.process :activity => 'b', :task => 4
|
58
|
+
@builder.process :activity => 'c', :task => 5
|
59
|
+
@builder.process :task => 6, :story => 'E'
|
60
|
+
@builder.process :story => 'F'
|
61
|
+
@builder.process :story => 'G'
|
62
|
+
@builder.process :story => 'H'
|
63
|
+
@builder.process :task => 7
|
64
|
+
@builder.process :task => 8
|
65
|
+
@builder.process :activity => 'd', :task => 2, :story => 'I'
|
66
|
+
|
67
|
+
@builder.show(@out)
|
68
|
+
@out.to_s.should ==
|
69
|
+
"a bc d
|
70
|
+
123456782
|
71
|
+
ABD E I
|
72
|
+
C F
|
73
|
+
G
|
74
|
+
H"
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require 'file_sandbox_behavior'
|
4
|
+
require 'cards/csv_parser'
|
5
|
+
include Cards
|
6
|
+
|
7
|
+
describe CsvParser do
|
8
|
+
include FileSandbox
|
9
|
+
|
10
|
+
it "should parse csv" do
|
11
|
+
sandbox.new :file => 'tmp.csv', :with_contents =>
|
12
|
+
"activity,task,story,estimate
|
13
|
+
manage email,,
|
14
|
+
,read email,
|
15
|
+
,,display email body
|
16
|
+
manage email,think about email,,1
|
17
|
+
,read email,,2
|
18
|
+
,,display email body,3"
|
19
|
+
|
20
|
+
rows = CsvParser.new(sandbox['tmp.csv'].path).rows
|
21
|
+
rows[0].to_h.should == {:activity => 'manage email'}
|
22
|
+
rows[1].to_h.should == {:task => 'read email'}
|
23
|
+
rows[2].to_h.should == {:story => 'display email body'}
|
24
|
+
rows[3].to_h.should == {:activity => 'manage email', :task => 'think about email', :estimate => "1"}
|
25
|
+
rows[4].to_h.should == {:task => 'read email', :estimate => "2"}
|
26
|
+
rows[5].to_h.should == {:story => 'display email body', :estimate => "3"}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "String extensions" do
|
4
|
+
it "should know what's blank" do
|
5
|
+
nil.should be_blank
|
6
|
+
"".should be_blank
|
7
|
+
" \t\n".should be_blank
|
8
|
+
"a".should_not be_blank
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Array extensions" do
|
13
|
+
it "should have max" do
|
14
|
+
[1, 2, 5, 4].max.should == 5
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have min" do
|
18
|
+
[1, 2, 5, 4].min.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should compute sum" do
|
22
|
+
[1, 2, 3].sum.should == 6
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'cards/swim_lanes'
|
3
|
+
include Cards
|
4
|
+
|
5
|
+
describe SwimLanes do
|
6
|
+
before do
|
7
|
+
@lanes = SwimLanes.new.define do
|
8
|
+
row :area
|
9
|
+
lanes :pain_points, :by => %w(H M L)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should show rows w/ no swim lanes" do
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require 'cards/writers/dot_writer'
|
3
|
+
require 'stringio'
|
4
|
+
include Cards
|
5
|
+
|
6
|
+
describe DotWriter do
|
7
|
+
CardStruct = Struct.new(:name, :color, :position)
|
8
|
+
{
|
9
|
+
CardStruct["role", :green, [0,0]] => /card_0_0 \[label="role",fillcolor=darkseagreen1\];/,
|
10
|
+
CardStruct["activity", :blue, [0,1]] => /card_0_1 \[label="activity",fillcolor=lightblue\];/,
|
11
|
+
CardStruct["task", :red, [0,2]] => /card_0_2 \[label="task",fillcolor=pink\];/,
|
12
|
+
CardStruct["feature", :yellow, [0,3]] => /card_0_3 \[label="feature",fillcolor=lightyellow\];/,
|
13
|
+
}.each do |card, expected_node|
|
14
|
+
it %{given #{card.color} card created with name #{card.name.inspect} in position #{card.position.inspect} it should create node #{expected_node.inspect}} do
|
15
|
+
create_card(card.name, card.color, card.position)
|
16
|
+
@actual_output.should =~ expected_node
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
{
|
21
|
+
[[0,0],[0,2]] => /gap_0_1 \[style=invis\];/,
|
22
|
+
[[0,0],[2,0]] => /gap_1_0 \[style=invis\];/,
|
23
|
+
[[0,0],[1,1]] => /gap_1_0 \[style=invis\];/,
|
24
|
+
}.each do |card_positions, expected_gap_node|
|
25
|
+
it %{given cards created in positions #{card_positions.inspect} it should create invisible node #{expected_gap_node.inspect}} do
|
26
|
+
create_cards_in_positions(card_positions)
|
27
|
+
@actual_output.should =~ expected_gap_node
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
{
|
32
|
+
[[0,0],[1,0],[2,0]] => [/card_0_0 -> card_1_0;/,/card_1_0 -> card_2_0;/],
|
33
|
+
[[0,0],[0,1],[0,2]] => [/card_0_0 -> card_0_1;/,/card_0_1 -> card_0_2;/],
|
34
|
+
}.each do |card_positions, edges|
|
35
|
+
it %{given cards created in positions #{card_positions.inspect} it should create edges #{edges.inspect} } do
|
36
|
+
create_cards_in_positions(card_positions)
|
37
|
+
@actual_output.expect(edges)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
{
|
42
|
+
[[0,0],[1,0]] => [/\{rank=same; card_0_0; card_1_0\};/],
|
43
|
+
}.each do |card_positions, ranks|
|
44
|
+
it %{given cards created in positions #{card_positions.inspect} it should create ranks #{ranks.inspect} } do
|
45
|
+
create_cards_in_positions(card_positions)
|
46
|
+
@actual_output.expect(ranks)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build
|
51
|
+
@builder = DotWriter.new(StringIO.new(@actual_output = "", "w+"))
|
52
|
+
yield
|
53
|
+
@builder.done
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_cards_in_positions(positions)
|
57
|
+
build { positions.each { |position| @builder.create_card("card", :yellow, position)} }
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_card(name, color, position)
|
61
|
+
build { @builder.create_card(name, color, position) }
|
62
|
+
end
|
63
|
+
|
64
|
+
class String
|
65
|
+
def expect(lines)
|
66
|
+
lines.each { |line| self.should =~ line }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
include Cards
|
3
|
+
|
4
|
+
describe TextWriter do
|
5
|
+
before do
|
6
|
+
@output = TextWriter.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should show simple output for to_s" do
|
10
|
+
@output.create_card('a', nil, [0, 0])
|
11
|
+
@output.create_card('b', nil, [0, 1])
|
12
|
+
@output.create_card('c', nil, [0, 2])
|
13
|
+
@output.create_card('d', nil, [2, 2])
|
14
|
+
@output.create_card('e', nil, [1, 2])
|
15
|
+
@output.create_card('f', nil, [3, 0])
|
16
|
+
|
17
|
+
@output.to_s.should ==
|
18
|
+
"a f
|
19
|
+
b
|
20
|
+
ced"
|
21
|
+
@output.inspect.should ==
|
22
|
+
"|a|||f|
|
23
|
+
|b|
|
24
|
+
|c|e|d|"
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
gem "rspec"
|
5
|
+
gem "mocha"
|
6
|
+
gem "file_sandbox"
|
7
|
+
|
8
|
+
require 'cards/extensions'
|
9
|
+
require 'cards/writers/text_writer'
|
10
|
+
|
11
|
+
Spec::Runner.configure do |config|
|
12
|
+
#config.mock_with :mocha
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cards
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.6"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Stell-Smith
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-06-05 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.5.3
|
23
|
+
version:
|
24
|
+
description: CardWallGen is a program that reads a requirements map and generates a view of it. Typically it reads it from excel or csv and generates a graphical view in visio or omnigraffle, though it may also generate another csv view of it.
|
25
|
+
email: jeremystellsmith@gmail.com
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- CHANGES.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
files:
|
35
|
+
- CHANGES.txt
|
36
|
+
- Manifest.txt
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- lib/cards.rb
|
40
|
+
- lib/cards/builder.rb
|
41
|
+
- lib/cards/card.rb
|
42
|
+
- lib/cards/card_wall.rb
|
43
|
+
- lib/cards/csv_builder.rb
|
44
|
+
- lib/cards/csv_parser.rb
|
45
|
+
- lib/cards/extensions.rb
|
46
|
+
- lib/cards/layouts/column_layout.rb
|
47
|
+
- lib/cards/layouts/nil_layout.rb
|
48
|
+
- lib/cards/layouts/row_layout.rb
|
49
|
+
- lib/cards/master_story_list.rb
|
50
|
+
- lib/cards/swim_lanes.rb
|
51
|
+
- lib/cards/text.rb
|
52
|
+
- lib/cards/tracker_csv.rb
|
53
|
+
- lib/cards/writers/dot_writer.rb
|
54
|
+
- lib/cards/writers/graffle_writer.rb
|
55
|
+
- lib/cards/writers/text_writer.rb
|
56
|
+
- spec/cards/card_spec.rb
|
57
|
+
- spec/cards/card_wall_spec.rb
|
58
|
+
- spec/cards/csv_parser_spec.rb
|
59
|
+
- spec/cards/extensions_spec.rb
|
60
|
+
- spec/cards/swim_lanes_spec.rb
|
61
|
+
- spec/cards/writers/dot_writer_spec.rb
|
62
|
+
- spec/cards/writers/text_writer_spec.rb
|
63
|
+
- spec/spec_helper.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://rubyforge.org/projects/cardwallgen
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --main
|
69
|
+
- README.txt
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: cardwallgen
|
87
|
+
rubygems_version: 1.1.1
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: CardWallGen is a program that reads a requirements map and generates a view of it. Typically it reads it from excel or csv and generates a graphical view in visio or omnigraffle, though it may also generate another csv view of it.
|
91
|
+
test_files: []
|
92
|
+
|