pdf-storycards 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/Manifest.txt +17 -0
- data/README.txt +55 -0
- data/Rakefile +23 -0
- data/bin/stories2cards +129 -0
- data/lib/pdf/storycards.rb +10 -0
- data/lib/pdf/storycards/scenario.rb +21 -0
- data/lib/pdf/storycards/story.rb +24 -0
- data/lib/pdf/storycards/story_capturing_mediator.rb +52 -0
- data/lib/pdf/storycards/writer.rb +91 -0
- data/spec/addition +34 -0
- data/spec/calculator_stories +66 -0
- data/spec/pdf/storycards/story_capturing_mediator_spec.rb +39 -0
- data/spec/pdf/storycards/story_spec.rb +23 -0
- data/spec/pdf/storycards/writer_spec.rb +38 -0
- data/spec/rspec_suite.rb +10 -0
- data/spec/spec_helper.rb +9 -0
- metadata +98 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/stories2cards
|
6
|
+
lib/pdf/storycards.rb
|
7
|
+
lib/pdf/storycards/scenario.rb
|
8
|
+
lib/pdf/storycards/story.rb
|
9
|
+
lib/pdf/storycards/story_capturing_mediator.rb
|
10
|
+
lib/pdf/storycards/writer.rb
|
11
|
+
spec/addition
|
12
|
+
spec/calculator_stories
|
13
|
+
spec/pdf/storycards/story_capturing_mediator_spec.rb
|
14
|
+
spec/pdf/storycards/story_spec.rb
|
15
|
+
spec/pdf/storycards/writer_spec.rb
|
16
|
+
spec/rspec_suite.rb
|
17
|
+
spec/spec_helper.rb
|
data/README.txt
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
PDF StoryCards
|
2
|
+
by Luke Melia
|
3
|
+
http://www.lukemelia.com/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides a script and library to parses stories saved in the RSpec
|
8
|
+
plain text story format and saves a PDF file with printable 3"x5"
|
9
|
+
index cards suitable for using in agile planning and prioritization.
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* Create a PDF with each page as a 3x5 sheet, or as 4 cards per 8.5 x 11 sheet
|
14
|
+
* Currently reads stories from a single file.
|
15
|
+
* TODO: Take a directory and find all stories in it
|
16
|
+
* TODO: Take stories via STDIN
|
17
|
+
* TODO: Improve test coverage
|
18
|
+
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
StorycardPdfWriter.make_pdf("/tmp/stories.txt", "/tmp/storycards.pdf", :style => :card_1up)
|
22
|
+
|
23
|
+
== REQUIREMENTS:
|
24
|
+
|
25
|
+
* RSpec (for parsing stories)
|
26
|
+
* PDF::Writer (for creating PDFs)
|
27
|
+
|
28
|
+
== INSTALL:
|
29
|
+
|
30
|
+
sudo gem install storycards
|
31
|
+
|
32
|
+
== LICENSE:
|
33
|
+
|
34
|
+
(The MIT License)
|
35
|
+
|
36
|
+
Copyright (c) 2007 Luke Melia
|
37
|
+
|
38
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
39
|
+
a copy of this software and associated documentation files (the
|
40
|
+
'Software'), to deal in the Software without restriction, including
|
41
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
42
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
43
|
+
permit persons to whom the Software is furnished to do so, subject to
|
44
|
+
the following conditions:
|
45
|
+
|
46
|
+
The above copyright notice and this permission notice shall be
|
47
|
+
included in all copies or substantial portions of the Software.
|
48
|
+
|
49
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
50
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
51
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
52
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
53
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
54
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
55
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
lib_path = File.expand_path("#{dir}/lib")
|
7
|
+
$LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path)
|
8
|
+
require 'pdf/storycards'
|
9
|
+
ENV["VERSION"] = PDF::Storycards::VERSION
|
10
|
+
|
11
|
+
Hoe.new('pdf-storycards', PDF::Storycards::VERSION) do |p|
|
12
|
+
p.rubyforge_name = 'pdf-storycards'
|
13
|
+
p.author = 'Luke Melia'
|
14
|
+
p.email = 'luke@lukemelia.com'
|
15
|
+
p.summary = 'Utilities for generating printable story cards for agile planning and measurement'
|
16
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
17
|
+
# p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
18
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
19
|
+
p.extra_deps << ['pdf-writer', '>= 1.1.7']
|
20
|
+
p.extra_deps << ['rspec', '>= 1.1.1']
|
21
|
+
end
|
22
|
+
|
23
|
+
# vim: syntax=Ruby
|
data/bin/stories2cards
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
# == Synopsis
|
4
|
+
# stories2cards parses stories saved in the RSpec plain text story
|
5
|
+
# format and saves a PDF file with printable 3"x5"" index cards
|
6
|
+
# suitable for using in agile planning and prioritization.
|
7
|
+
#
|
8
|
+
# == Examples
|
9
|
+
# This command outputs a PDF consisting of one card for each story and
|
10
|
+
# narrative defined in stories.txt and saves it as stories.pdf
|
11
|
+
#
|
12
|
+
# stories2cards stories.txt
|
13
|
+
#
|
14
|
+
# Other examples
|
15
|
+
#
|
16
|
+
# stories2cards stories.txt -o /tmp/cards.pdf
|
17
|
+
# stories2cards stories.txt --style=4up
|
18
|
+
#
|
19
|
+
# == Usage
|
20
|
+
# stories2cards [options] text_file_with_stories
|
21
|
+
#
|
22
|
+
# For help use stories2cards -h
|
23
|
+
#
|
24
|
+
# == Options
|
25
|
+
# -h, --help Displays help message
|
26
|
+
# -v, --version Display the version, then exit
|
27
|
+
# -o, --output=PATH Specify the path to the PDF file
|
28
|
+
# to be created, defaults to storycards.pdf
|
29
|
+
# -s, --style=1up|4up Defaults to 1up
|
30
|
+
# -b, --[no-]borders Print borders (only has effect with 4up style
|
31
|
+
#
|
32
|
+
# == Author
|
33
|
+
# Luke Melia
|
34
|
+
#
|
35
|
+
# == Copyright
|
36
|
+
# Copyright (c) 2007 Luke Melia. Licensed under the MIT License
|
37
|
+
# http://www.opensource.org/licenses/mit-license.php
|
38
|
+
|
39
|
+
require 'optparse'
|
40
|
+
require 'rdoc/usage'
|
41
|
+
require 'ostruct'
|
42
|
+
|
43
|
+
begin
|
44
|
+
require 'pdf/storycards'
|
45
|
+
rescue LoadError => le
|
46
|
+
if le.message =~ %r{pdf/storycards$}
|
47
|
+
root = File.dirname(File.dirname(File.expand_path(__FILE__)))
|
48
|
+
$LOAD_PATH.unshift(File.join(root, "lib"))
|
49
|
+
require 'pdf/storycards'
|
50
|
+
else
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class App
|
56
|
+
|
57
|
+
attr_reader :options
|
58
|
+
|
59
|
+
def initialize(arguments, stdin)
|
60
|
+
@arguments = arguments
|
61
|
+
@stdin = stdin
|
62
|
+
|
63
|
+
# Set defaults
|
64
|
+
@options = OpenStruct.new
|
65
|
+
@options.output = "storycards.pdf"
|
66
|
+
@options.style = :"1up"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parse options, check arguments, then process the command
|
70
|
+
def run
|
71
|
+
if parsed_options? && arguments_valid?
|
72
|
+
process_arguments
|
73
|
+
process_command
|
74
|
+
else
|
75
|
+
output_usage
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def parsed_options?
|
82
|
+
|
83
|
+
# Specify options
|
84
|
+
opts = OptionParser.new
|
85
|
+
opts.on('-v', '--version') { output_version ; exit 0 }
|
86
|
+
opts.on('-h', '--help') { output_help }
|
87
|
+
opts.on('-o', '--output [PATH]') { |path| @options.output = path }
|
88
|
+
opts.on('-s', '--style [STYLE]', [:"1up", :"4up"]) { |style| @options.style = style }
|
89
|
+
opts.on("-b", "--[no-]borders", "Print borders") { |b| options.borders = b }
|
90
|
+
opts.parse!(@arguments) rescue return false
|
91
|
+
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
# True if required arguments were provided
|
96
|
+
def arguments_valid?
|
97
|
+
return false unless @arguments.length == 1
|
98
|
+
return false unless File.exists?(@arguments[0])
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
|
102
|
+
# Setup the arguments
|
103
|
+
def process_arguments
|
104
|
+
@source_file = @arguments[0]
|
105
|
+
end
|
106
|
+
|
107
|
+
def output_help
|
108
|
+
output_version
|
109
|
+
RDoc::usage() #exits app
|
110
|
+
end
|
111
|
+
|
112
|
+
def output_usage
|
113
|
+
RDoc::usage('usage') # gets usage from comments above
|
114
|
+
end
|
115
|
+
|
116
|
+
def output_version
|
117
|
+
puts "#{File.basename(__FILE__)} version #{PDF::Storycards::VERSION}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_command
|
121
|
+
style = :card_1up if @options.style == :"1up"
|
122
|
+
style = :letter_4up if @options.style == :"4up"
|
123
|
+
PDF::Storycards::Writer.make_pdf(@source_file, @options.output, :style => style)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Create and run the application
|
128
|
+
app = App.new(ARGV, STDIN)
|
129
|
+
app.run
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PDF
|
2
|
+
module Storycards
|
3
|
+
class Scenario
|
4
|
+
def initialize(name)
|
5
|
+
@name = name
|
6
|
+
@steps = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_step(step)
|
10
|
+
@steps << step
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Step
|
15
|
+
def initialize(type, name)
|
16
|
+
@type = type
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module PDF
|
2
|
+
module Storycards
|
3
|
+
class Story
|
4
|
+
def initialize(title, narrative)
|
5
|
+
@title = title
|
6
|
+
@narrative = narrative
|
7
|
+
def @narrative.to_sentence
|
8
|
+
self.sub(/^\s*I want/, ", I want").gsub(/^\s*So that/, ", so that").split($/).join(" ").gsub(' ,',',').squeeze(" ")
|
9
|
+
end
|
10
|
+
@scenarios = []
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :title, :narrative
|
14
|
+
|
15
|
+
def add_scenario(scenario)
|
16
|
+
@scenarios << scenario
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_scenario
|
20
|
+
@scenarios.last
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# RSpec's story parser needs to be constructed with a Mediator. The mediator defined in the class
|
2
|
+
# below simply collects stories and exposes the collection as StoryCapturingMediator#stories.
|
3
|
+
#
|
4
|
+
# It also sprinkles in a magic method on Story#narrative called to_sentence, which formats the
|
5
|
+
# story narrative as a single sentence without newlines.
|
6
|
+
module PDF
|
7
|
+
module Storycards
|
8
|
+
|
9
|
+
class StoryCapturingMediator
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@stories = []
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :stories
|
16
|
+
|
17
|
+
def create_story(title, narrative)
|
18
|
+
@stories << Story.new(title, narrative)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_scenario(title)
|
22
|
+
current_story.add_scenario Scenario.new(title)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_given(name)
|
26
|
+
current_scenario.add_step Step.new('Given', name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_given_scenario(name)
|
30
|
+
current_scenario.add_step Step.new('GivenScenario', name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_when(name)
|
34
|
+
current_scenario.add_step Step.new('When', name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_then(name)
|
38
|
+
current_scenario.add_step Step.new('Then', name)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def current_story
|
43
|
+
@stories.last
|
44
|
+
end
|
45
|
+
|
46
|
+
def current_scenario
|
47
|
+
current_story.current_scenario
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'pdf/writer'
|
2
|
+
require 'spec/story/runner/story_parser.rb'
|
3
|
+
|
4
|
+
module PDF
|
5
|
+
module Storycards
|
6
|
+
class Writer
|
7
|
+
|
8
|
+
def self.make_pdf( path, output_path = 'storycards.pdf', opts = {} )
|
9
|
+
output_path = 'storycards.pdf' if output_path.nil?
|
10
|
+
opts[:style] = :card_1up unless opts[:style]
|
11
|
+
opts[:border] = false unless opts[:border]
|
12
|
+
|
13
|
+
stories = self.parse_stories(path)
|
14
|
+
|
15
|
+
if opts[:style] == :card_1up
|
16
|
+
pdf = PDF::Writer.new(:paper => [7.62, 12.7], :orientation => :landscape) #3x5 card
|
17
|
+
pdf.margins_mm 8
|
18
|
+
pdf.move_pointer(-60)
|
19
|
+
|
20
|
+
stories.each_with_index do |story, index|
|
21
|
+
pdf.y = 0 if index > 0
|
22
|
+
print_card_text(pdf, story)
|
23
|
+
end
|
24
|
+
|
25
|
+
pdf.save_as(output_path)
|
26
|
+
return output_path
|
27
|
+
|
28
|
+
elsif opts[:style] == :letter_4up
|
29
|
+
|
30
|
+
pdf = PDF::Writer.new(:paper => "LETTER", :orientation => :landscape)
|
31
|
+
card_width = PDF::Writer.in2pts(5)
|
32
|
+
card_height = PDF::Writer.in2pts(3)
|
33
|
+
margin = PDF::Writer.in2pts(0.33)
|
34
|
+
gutter = margin
|
35
|
+
padding = 10
|
36
|
+
|
37
|
+
pdf.start_columns(2, gutter)
|
38
|
+
stories.each_with_index do |story, index|
|
39
|
+
|
40
|
+
is_laying_out_left_hand_column = (index < 2)
|
41
|
+
if is_laying_out_left_hand_column
|
42
|
+
rect_x = margin
|
43
|
+
else
|
44
|
+
rect_x = pdf.page_width - margin - card_width
|
45
|
+
end
|
46
|
+
|
47
|
+
is_laying_out_top_row = (index % 2 == 0)
|
48
|
+
if is_laying_out_top_row
|
49
|
+
pdf.start_new_page unless index == 0
|
50
|
+
pdf.y = pdf.page_height - margin - padding
|
51
|
+
rect_y = pdf.page_height - margin - card_height
|
52
|
+
else
|
53
|
+
rect_y = margin
|
54
|
+
pdf.y = margin + card_height - padding
|
55
|
+
end
|
56
|
+
|
57
|
+
print_border(pdf, rect_x, rect_y, card_width, card_height) if opts[:border]
|
58
|
+
print_card_text(pdf, story, padding)
|
59
|
+
end
|
60
|
+
|
61
|
+
pdf.save_as(output_path)
|
62
|
+
return output_path
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
private
|
67
|
+
|
68
|
+
def self.print_border(pdf, x, y, width, height)
|
69
|
+
pdf.stroke_color(Color::RGB::Black)
|
70
|
+
pdf.stroke_style(PDF::Writer::StrokeStyle.new(1))
|
71
|
+
pdf.rectangle(rect_x, rect_y, card_width, card_height).close_stroke
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.parse_stories(path)
|
75
|
+
story_mediator = StoryCapturingMediator.new
|
76
|
+
story_parser = Spec::Story::Runner::StoryParser.new(story_mediator)
|
77
|
+
story_text = File.read(path)
|
78
|
+
story_parser.parse(story_text.split("\n"))
|
79
|
+
return story_mediator.stories
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.print_card_text(pdf, story, padding = 0)
|
83
|
+
pdf.text "<b>#{story.title}</b>", :font_size => 18, :justification => :center,
|
84
|
+
:left => padding, :right => padding
|
85
|
+
pdf.move_pointer(12)
|
86
|
+
pdf.text story.narrative.to_sentence, :justification => :left, :font_size => 14,
|
87
|
+
:left => padding, :right => padding
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/addition
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
This is a story about a calculator. The text up here above the Story: declaration
|
2
|
+
won't be processed, so you can write whatever you wish!
|
3
|
+
|
4
|
+
Story: simple addition
|
5
|
+
|
6
|
+
As an accountant
|
7
|
+
I want to add numbers
|
8
|
+
So that I can count beans
|
9
|
+
|
10
|
+
Scenario: add one plus one
|
11
|
+
Given an addend of 1
|
12
|
+
And an addend of 1
|
13
|
+
|
14
|
+
When the addends are addeds
|
15
|
+
|
16
|
+
Then the sum should be 3
|
17
|
+
And the corks should be popped
|
18
|
+
|
19
|
+
Scenario: add two plus five
|
20
|
+
Given an addend of 2
|
21
|
+
And an addend of 5
|
22
|
+
|
23
|
+
When the addends are added
|
24
|
+
|
25
|
+
Then the sum should be 7
|
26
|
+
Then it should snow
|
27
|
+
|
28
|
+
Scenario: add three more
|
29
|
+
GivenScenario add two plus five
|
30
|
+
And an addend of 3
|
31
|
+
|
32
|
+
When the addends are added
|
33
|
+
|
34
|
+
Then the sum should be 10
|
@@ -0,0 +1,66 @@
|
|
1
|
+
This is a story about a calculator. The text up here above the Story: declaration
|
2
|
+
won't be processed, so you can write whatever you wish!
|
3
|
+
|
4
|
+
Story: simple addition
|
5
|
+
|
6
|
+
As an accountant
|
7
|
+
I want to add numbers
|
8
|
+
So that I can count beans
|
9
|
+
|
10
|
+
Scenario: add one plus one
|
11
|
+
Given an addend of 1
|
12
|
+
And an addend of 1
|
13
|
+
|
14
|
+
When the addends are addeds
|
15
|
+
|
16
|
+
Then the sum should be 3
|
17
|
+
And the corks should be popped
|
18
|
+
|
19
|
+
Scenario: add two plus five
|
20
|
+
Given an addend of 2
|
21
|
+
And an addend of 5
|
22
|
+
|
23
|
+
When the addends are added
|
24
|
+
|
25
|
+
Then the sum should be 7
|
26
|
+
Then it should snow
|
27
|
+
|
28
|
+
Scenario: add three more
|
29
|
+
GivenScenario add two plus five
|
30
|
+
And an addend of 3
|
31
|
+
|
32
|
+
When the addends are added
|
33
|
+
|
34
|
+
Then the sum should be 10
|
35
|
+
|
36
|
+
Story: simple subtraction
|
37
|
+
|
38
|
+
As an accountant
|
39
|
+
I want to subtract numbers
|
40
|
+
So that I can count beans
|
41
|
+
|
42
|
+
Scenario: subtract one minus one
|
43
|
+
Given an argument of 1
|
44
|
+
And an argument of 1
|
45
|
+
|
46
|
+
When the second argument is subtracted from the first argument
|
47
|
+
|
48
|
+
Then the different should be 0
|
49
|
+
And the corks should be popped
|
50
|
+
|
51
|
+
Scenario: subtract two from five
|
52
|
+
Given an argument of 5
|
53
|
+
And an argument of 2
|
54
|
+
|
55
|
+
When the second argument is subtracted from the first argument
|
56
|
+
|
57
|
+
Then the difference should be 3
|
58
|
+
Then it should snow
|
59
|
+
|
60
|
+
Scenario: subtract two more
|
61
|
+
GivenScenario subtract two from five
|
62
|
+
And an argument of 2
|
63
|
+
|
64
|
+
When the result is reduced by the argument
|
65
|
+
|
66
|
+
Then the result should be 1
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
describe PDF::Storycards::StoryCapturingMediator do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@mediator = PDF::Storycards::StoryCapturingMediator.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "initial state" do
|
10
|
+
it "should have an empty list of stories" do
|
11
|
+
@mediator.stories.should == []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should add stories when sent :create_story" do
|
16
|
+
@mediator.create_story('my title', 'my narrative')
|
17
|
+
@mediator.stories.length.should == 1
|
18
|
+
@mediator.stories.first.should be_an_instance_of(PDF::Storycards::Story)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "after having a story added" do
|
22
|
+
before(:each) do
|
23
|
+
@mediator.create_story('User joins a Group',
|
24
|
+
"As a user
|
25
|
+
I want to be able to join a group
|
26
|
+
So that I can connect to members with similar interests")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have a story with the specified title" do
|
30
|
+
@mediator.stories.first.title.should == 'User joins a Group'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have a story with the specified narrative" do
|
34
|
+
@mediator.stories.first.narrative.should =~ /As a user\s+I want to be able to join a group\s+So that I can connect to members with similar interests/
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
describe PDF::Storycards::Story do
|
4
|
+
before(:each) do
|
5
|
+
@story = PDF::Storycards::Story.new('User joins a Group',
|
6
|
+
"As a user
|
7
|
+
I want to be able to join a group
|
8
|
+
So that I can connect to members with similar interests")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have the specified title" do
|
12
|
+
@story.title.should == 'User joins a Group'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have the specified narrative" do
|
16
|
+
@story.narrative.should =~ /As a user\s+I want to be able to join a group\s+So that I can connect to members with similar interests/
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have a narrative that can be converted to a sentence without newlines" do
|
20
|
+
@story.narrative.to_sentence.should == 'As a user, I want to be able to join a group, so that I can connect to members with similar interests'
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
class Writer
|
5
|
+
def save_as(path)
|
6
|
+
#no op
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe PDF::Storycards::Writer do
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
@single_story_path = File.dirname(__FILE__) + '/../../addition'
|
15
|
+
@multiple_stories_path = File.dirname(__FILE__) + '/../../calculator_stories'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should parse an example plain text story file and create a new story" do
|
19
|
+
stories = PDF::Storycards::Writer.send(:parse_stories, @single_story_path)
|
20
|
+
stories.first.title.should == 'simple addition'
|
21
|
+
stories.first.narrative.should == "As an accountant\nI want to add numbers\nSo that I can count beans"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not print border when printing 1-up" do
|
25
|
+
PDF::Storycards::Writer.should_not_receive(:print_border)
|
26
|
+
PDF::Storycards::Writer.make_pdf(@multiple_stories_path, 'storycards.pdf', :style => :card_1up)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not print border when printing 4-up with default options" do
|
30
|
+
PDF::Storycards::Writer.should_not_receive(:print_border)
|
31
|
+
PDF::Storycards::Writer.make_pdf(@multiple_stories_path, 'storycards.pdf', :style => :letter_4up)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should print border when printing 4-up with border option set to true" do
|
35
|
+
PDF::Storycards::Writer.should_receive(:print_border).twice
|
36
|
+
PDF::Storycards::Writer.make_pdf(@multiple_stories_path, 'storycards.pdf', :style => :letter_4up, :border => true)
|
37
|
+
end
|
38
|
+
end
|
data/spec/rspec_suite.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pdf-storycards
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Luke Melia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2007-12-29 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: pdf-writer
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.1.7
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rspec
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 1.1.1
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: hoe
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.4.0
|
41
|
+
version:
|
42
|
+
description: "== FEATURES/PROBLEMS: * Create a PDF with each page as a 3x5 sheet, or as 4 cards per 8.5 x 11 sheet * Currently reads stories from a single file. * TODO: Take a directory and find all stories in it * TODO: Take stories via STDIN * TODO: Improve test coverage == SYNOPSIS: StorycardPdfWriter.make_pdf(\"/tmp/stories.txt\", \"/tmp/storycards.pdf\", :style => :card_1up) == REQUIREMENTS:"
|
43
|
+
email: luke@lukemelia.com
|
44
|
+
executables:
|
45
|
+
- stories2cards
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
extra_rdoc_files:
|
49
|
+
- History.txt
|
50
|
+
- Manifest.txt
|
51
|
+
- README.txt
|
52
|
+
files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- README.txt
|
56
|
+
- Rakefile
|
57
|
+
- bin/stories2cards
|
58
|
+
- lib/pdf/storycards.rb
|
59
|
+
- lib/pdf/storycards/scenario.rb
|
60
|
+
- lib/pdf/storycards/story.rb
|
61
|
+
- lib/pdf/storycards/story_capturing_mediator.rb
|
62
|
+
- lib/pdf/storycards/writer.rb
|
63
|
+
- spec/addition
|
64
|
+
- spec/calculator_stories
|
65
|
+
- spec/pdf/storycards/story_capturing_mediator_spec.rb
|
66
|
+
- spec/pdf/storycards/story_spec.rb
|
67
|
+
- spec/pdf/storycards/writer_spec.rb
|
68
|
+
- spec/rspec_suite.rb
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
has_rdoc: true
|
71
|
+
homepage: http://www.zenspider.com/ZSS/Products/pdf-storycards/
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options:
|
74
|
+
- --main
|
75
|
+
- README.txt
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project: pdf-storycards
|
93
|
+
rubygems_version: 1.0.1
|
94
|
+
signing_key:
|
95
|
+
specification_version: 2
|
96
|
+
summary: Utilities for generating printable story cards for agile planning and measurement
|
97
|
+
test_files: []
|
98
|
+
|