saga 0.8.1 → 0.9.0
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/Rakefile +1 -16
- data/lib/saga/document.rb +4 -0
- data/lib/saga/formatter.rb +1 -1
- metadata +29 -34
- data/.kick +0 -25
- data/design/requirements.html +0 -83
- data/design/requirements.txt +0 -16
- data/saga.gemspec +0 -81
- data/test/cases/author.txt +0 -8
- data/test/cases/definition.txt +0 -10
- data/test/cases/document.txt +0 -15
- data/test/cases/nested_stories.txt +0 -4
- data/test/cases/story.txt +0 -6
- data/test/cases/story_attributes.txt +0 -22
- data/test/fixtures/document.erb +0 -80
- data/test/saga_document_spec.rb +0 -112
- data/test/saga_formatter_spec.rb +0 -57
- data/test/saga_parser_spec.rb +0 -169
- data/test/saga_planning_spec.rb +0 -79
- data/test/saga_runner_spec.rb +0 -137
- data/test/saga_spec.rb +0 -35
- data/test/saga_tokenizer_spec.rb +0 -141
- data/test/spec_helper.rb +0 -11
data/Rakefile
CHANGED
@@ -15,19 +15,4 @@ namespace :documentation do
|
|
15
15
|
rd.rdoc_files.include("README.rdoc", "LICENSE", "bin/**/*.rb", "lib/**/*.rb", "templates/**/*.rb")
|
16
16
|
rd.options << "--all" << "--charset" << "utf-8"
|
17
17
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
begin
|
21
|
-
require 'jeweler'
|
22
|
-
Jeweler::Tasks.new do |s|
|
23
|
-
s.name = "saga"
|
24
|
-
s.summary = s.description = "Saga is a tool to convert stories syntax to a nicely formatted document."
|
25
|
-
s.homepage = "http://fingertips.github.com"
|
26
|
-
s.email = "manfred@fngtps.com"
|
27
|
-
s.authors = ["Manfred Stienstra"]
|
28
|
-
s.add_dependency('erubis', '>= 2.6')
|
29
|
-
s.add_dependency('activesupport', '>= 2.3')
|
30
|
-
s.add_development_dependency('mocha-on-bacon')
|
31
|
-
end
|
32
|
-
rescue LoadError
|
33
|
-
end
|
18
|
+
end
|
data/lib/saga/document.rb
CHANGED
data/lib/saga/formatter.rb
CHANGED
@@ -20,7 +20,7 @@ module Saga
|
|
20
20
|
template_file = File.join(@options[:template], 'document.erb')
|
21
21
|
if File.exist?(template_file)
|
22
22
|
template = Erubis::Eruby.new(File.read(template_file))
|
23
|
-
template.result(@document.
|
23
|
+
template.result(@document._binding)
|
24
24
|
else
|
25
25
|
raise ArgumentError, "The template at path `#{template_file}' could not be found."
|
26
26
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saga
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Manfred Stienstra
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-07-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: erubis
|
@@ -48,7 +48,7 @@ dependencies:
|
|
48
48
|
type: :runtime
|
49
49
|
version_requirements: *id002
|
50
50
|
- !ruby/object:Gem::Dependency
|
51
|
-
name:
|
51
|
+
name: bacon
|
52
52
|
prerelease: false
|
53
53
|
requirement: &id003 !ruby/object:Gem::Requirement
|
54
54
|
none: false
|
@@ -61,8 +61,22 @@ dependencies:
|
|
61
61
|
version: "0"
|
62
62
|
type: :development
|
63
63
|
version_requirements: *id003
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: mocha-on-bacon
|
66
|
+
prerelease: false
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
type: :development
|
77
|
+
version_requirements: *id004
|
64
78
|
description: Saga is a tool to convert stories syntax to a nicely formatted document.
|
65
|
-
email: manfred@
|
79
|
+
email: manfred@fngtpspec.com
|
66
80
|
executables:
|
67
81
|
- saga
|
68
82
|
extensions: []
|
@@ -71,43 +85,24 @@ extra_rdoc_files:
|
|
71
85
|
- LICENSE
|
72
86
|
- README.rdoc
|
73
87
|
files:
|
74
|
-
- .kick
|
75
88
|
- LICENSE
|
76
89
|
- README.rdoc
|
77
90
|
- Rakefile
|
78
91
|
- VERSION
|
79
|
-
-
|
80
|
-
-
|
81
|
-
-
|
82
|
-
-
|
92
|
+
- templates/default/document.erb
|
93
|
+
- templates/default/helpers.rb
|
94
|
+
- templates/requirements.txt.erb
|
95
|
+
- templates/saga/document.erb
|
96
|
+
- templates/saga/helpers.rb
|
83
97
|
- lib/saga/document.rb
|
84
98
|
- lib/saga/formatter.rb
|
85
99
|
- lib/saga/parser.rb
|
86
100
|
- lib/saga/planning.rb
|
87
101
|
- lib/saga/runner.rb
|
88
102
|
- lib/saga/tokenizer.rb
|
89
|
-
- saga.
|
90
|
-
-
|
91
|
-
|
92
|
-
- templates/requirements.txt.erb
|
93
|
-
- templates/saga/document.erb
|
94
|
-
- templates/saga/helpers.rb
|
95
|
-
- test/cases/author.txt
|
96
|
-
- test/cases/definition.txt
|
97
|
-
- test/cases/document.txt
|
98
|
-
- test/cases/nested_stories.txt
|
99
|
-
- test/cases/story.txt
|
100
|
-
- test/cases/story_attributes.txt
|
101
|
-
- test/fixtures/document.erb
|
102
|
-
- test/saga_document_spec.rb
|
103
|
-
- test/saga_formatter_spec.rb
|
104
|
-
- test/saga_parser_spec.rb
|
105
|
-
- test/saga_planning_spec.rb
|
106
|
-
- test/saga_runner_spec.rb
|
107
|
-
- test/saga_spec.rb
|
108
|
-
- test/saga_tokenizer_spec.rb
|
109
|
-
- test/spec_helper.rb
|
110
|
-
homepage: http://fingertips.github.com
|
103
|
+
- lib/saga.rb
|
104
|
+
- bin/saga
|
105
|
+
homepage:
|
111
106
|
licenses: []
|
112
107
|
|
113
108
|
post_install_message:
|
data/.kick
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
recipe :ignore
|
2
|
-
ignore(/(^tmp|\.DS_Store)$/)
|
3
|
-
|
4
|
-
recipe :ruby
|
5
|
-
process do |files|
|
6
|
-
Ruby.run_tests(files.take_and_map do |file|
|
7
|
-
case file
|
8
|
-
when %r{^lib/(.+)\.rb$}
|
9
|
-
"test/#{$1.gsub('/', '_')}_spec.rb"
|
10
|
-
when %r{^templates}
|
11
|
-
"test/saga_formatter_spec.rb"
|
12
|
-
when %r{^test/(.+)_spec\.rb$}
|
13
|
-
file
|
14
|
-
when %r{^test/cases}
|
15
|
-
"test/saga_tokenizer_spec.rb"
|
16
|
-
end
|
17
|
-
end)
|
18
|
-
files.take_and_map do |file|
|
19
|
-
case file
|
20
|
-
when 'design/requirements.txt'
|
21
|
-
log `./bin/saga design/requirements.txt > design/requirements.html`
|
22
|
-
files.delete(file)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/design/requirements.html
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
2
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
4
|
-
<head>
|
5
|
-
<title>Requirements Saga · Fingertips</title>
|
6
|
-
<link rel="stylesheet" type="text/css" media="all" href="http://resources.fngtps.com/2006/doc.css" />
|
7
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
-
<style type="text/css">
|
9
|
-
td.id { text-align: right !important; }
|
10
|
-
td.id a { color: #000; }
|
11
|
-
table.review { border-bottom: 1px solid #ccc; margin-top: -1em;}
|
12
|
-
table.review td { padding: .25em 0; vertical-align: top; text-align: left; min-width: 1em; }
|
13
|
-
table.review th { padding: .25em 0; vertical-align: top; text-align: right; }
|
14
|
-
table.review td.story { width: 100%; border-top: 1px solid #ccc; }
|
15
|
-
table.review td.meta { padding-left: 1em; border-top: 1px solid #ccc; text-align: right !important; white-space: nowrap; }
|
16
|
-
table.review td.notes { color: #666; padding: 0 0 .25em 0; font-style: italic; }
|
17
|
-
table.review .dropped { color: #666; text-decoration: line-through; }
|
18
|
-
table.review .done { color: #666; background-color: #f0f8ff; }
|
19
|
-
table.review tr.nested td.story { padding-left: 1em; }
|
20
|
-
</style>
|
21
|
-
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
|
22
|
-
</head>
|
23
|
-
<body id="doc" class="requirements">
|
24
|
-
|
25
|
-
<p id="logo"><img src="http://resources.fngtps.com/2006/print-logo.png" alt="Fingertips design & development" /> </p>
|
26
|
-
|
27
|
-
<h1>Requirements <br />Saga</h1>
|
28
|
-
|
29
|
-
<p class="author">Manfred Stienstra, <a href="mailto:manfred@fngtps.com">manfred@fngtps.com</a></p>
|
30
|
-
|
31
|
-
|
32
|
-
<h2>User stories</h2>
|
33
|
-
<table class="review">
|
34
|
-
<tr>
|
35
|
-
<th></th>
|
36
|
-
<th title="id">#</th>
|
37
|
-
<th title="Estimate">e</th>
|
38
|
-
<th title="Iteration">i</th>
|
39
|
-
<th title="Status">s</th>
|
40
|
-
</tr>
|
41
|
-
<tr class="done" id="story1">
|
42
|
-
<td class="story">As a writer I would like to write stories so developers can implement them.</td>
|
43
|
-
<td class="meta id">1</td>
|
44
|
-
<td class="meta estimate"></td>
|
45
|
-
<td class="meta iteration"></td>
|
46
|
-
<td class="meta status">done</td>
|
47
|
-
</tr>
|
48
|
-
<tr class="todo" id="story2">
|
49
|
-
<td class="story">As a writer I would like to add priorities to my stories so that I can show their importance.</td>
|
50
|
-
<td class="meta id">2</td>
|
51
|
-
<td class="meta estimate"></td>
|
52
|
-
<td class="meta iteration"></td>
|
53
|
-
<td class="meta status">todo</td>
|
54
|
-
</tr>
|
55
|
-
<tr class="todo" id="story3">
|
56
|
-
<td class="story">As a writer I would like to order my stories so that I can easily find the story I need.</td>
|
57
|
-
<td class="meta id">3</td>
|
58
|
-
<td class="meta estimate"></td>
|
59
|
-
<td class="meta iteration"></td>
|
60
|
-
<td class="meta status">todo</td>
|
61
|
-
</tr>
|
62
|
-
<tr class="todo">
|
63
|
-
<td class="notes" colspan="5">Improve the HTML template to allow ordering by ID, estimate, iteration, status, and priority.</td>
|
64
|
-
</tr>
|
65
|
-
<tr class="todo" id="story4">
|
66
|
-
<td class="story">As a writer I would like to ignore certain themes during autofill and planning so that I can specify non-functional stories.</td>
|
67
|
-
<td class="meta id">4</td>
|
68
|
-
<td class="meta estimate"></td>
|
69
|
-
<td class="meta iteration"></td>
|
70
|
-
<td class="meta status">todo</td>
|
71
|
-
</tr>
|
72
|
-
<tr class="todo">
|
73
|
-
<td class="notes" colspan="5">saga -n 'Non-functional' -n 'Optional' autofill design/requirements.txt</td>
|
74
|
-
</tr>
|
75
|
-
</table>
|
76
|
-
|
77
|
-
<h2>Roles</h2>
|
78
|
-
<dl>
|
79
|
-
<dt>Writer</dt>
|
80
|
-
<dd>Someone who is responsible for writing down requirements in the form of stories</dd>
|
81
|
-
</dl>
|
82
|
-
</body>
|
83
|
-
</html>
|
data/design/requirements.txt
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
Requirements Saga
|
2
|
-
|
3
|
-
- Manfred Stienstra, manfred@fngtps.com
|
4
|
-
|
5
|
-
USER STORIES
|
6
|
-
|
7
|
-
As a writer I would like to write stories so developers can implement them. - #1 done
|
8
|
-
As a writer I would like to add priorities to my stories so that I can show their importance. - #2 todo
|
9
|
-
As a writer I would like to order my stories so that I can easily find the story I need. - #3 todo
|
10
|
-
Improve the HTML template to allow ordering by ID, estimate, iteration, status, and priority.
|
11
|
-
As a writer I would like to ignore certain themes during autofill and planning so that I can specify non-functional stories. - #4 todo
|
12
|
-
saga -n 'Non-functional' -n 'Optional' autofill design/requirements.txt
|
13
|
-
|
14
|
-
ROLES
|
15
|
-
|
16
|
-
Writer: Someone who is responsible for writing down requirements in the form of stories
|
data/saga.gemspec
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = "saga"
|
8
|
-
s.version = "0.8.1"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Manfred Stienstra"]
|
12
|
-
s.date = "2012-06-18"
|
13
|
-
s.description = "Saga is a tool to convert stories syntax to a nicely formatted document."
|
14
|
-
s.email = "manfred@fngtps.com"
|
15
|
-
s.executables = ["saga"]
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
"LICENSE",
|
18
|
-
"README.rdoc"
|
19
|
-
]
|
20
|
-
s.files = [
|
21
|
-
".kick",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"bin/saga",
|
27
|
-
"design/requirements.html",
|
28
|
-
"design/requirements.txt",
|
29
|
-
"lib/saga.rb",
|
30
|
-
"lib/saga/document.rb",
|
31
|
-
"lib/saga/formatter.rb",
|
32
|
-
"lib/saga/parser.rb",
|
33
|
-
"lib/saga/planning.rb",
|
34
|
-
"lib/saga/runner.rb",
|
35
|
-
"lib/saga/tokenizer.rb",
|
36
|
-
"saga.gemspec",
|
37
|
-
"templates/default/document.erb",
|
38
|
-
"templates/default/helpers.rb",
|
39
|
-
"templates/requirements.txt.erb",
|
40
|
-
"templates/saga/document.erb",
|
41
|
-
"templates/saga/helpers.rb",
|
42
|
-
"test/cases/author.txt",
|
43
|
-
"test/cases/definition.txt",
|
44
|
-
"test/cases/document.txt",
|
45
|
-
"test/cases/nested_stories.txt",
|
46
|
-
"test/cases/story.txt",
|
47
|
-
"test/cases/story_attributes.txt",
|
48
|
-
"test/fixtures/document.erb",
|
49
|
-
"test/saga_document_spec.rb",
|
50
|
-
"test/saga_formatter_spec.rb",
|
51
|
-
"test/saga_parser_spec.rb",
|
52
|
-
"test/saga_planning_spec.rb",
|
53
|
-
"test/saga_runner_spec.rb",
|
54
|
-
"test/saga_spec.rb",
|
55
|
-
"test/saga_tokenizer_spec.rb",
|
56
|
-
"test/spec_helper.rb"
|
57
|
-
]
|
58
|
-
s.homepage = "http://fingertips.github.com"
|
59
|
-
s.require_paths = ["lib"]
|
60
|
-
s.rubygems_version = "1.8.18"
|
61
|
-
s.summary = "Saga is a tool to convert stories syntax to a nicely formatted document."
|
62
|
-
|
63
|
-
if s.respond_to? :specification_version then
|
64
|
-
s.specification_version = 3
|
65
|
-
|
66
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
67
|
-
s.add_runtime_dependency(%q<erubis>, [">= 2.6"])
|
68
|
-
s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
|
69
|
-
s.add_development_dependency(%q<mocha-on-bacon>, [">= 0"])
|
70
|
-
else
|
71
|
-
s.add_dependency(%q<erubis>, [">= 2.6"])
|
72
|
-
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
73
|
-
s.add_dependency(%q<mocha-on-bacon>, [">= 0"])
|
74
|
-
end
|
75
|
-
else
|
76
|
-
s.add_dependency(%q<erubis>, [">= 2.6"])
|
77
|
-
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
78
|
-
s.add_dependency(%q<mocha-on-bacon>, [">= 0"])
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
data/test/cases/author.txt
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
- Thijs van der Vossen
|
2
|
-
=> {:name => 'Thijs van der Vossen'}
|
3
|
-
- Thijs van der Vossen, thijs@fngtps.com
|
4
|
-
=> {:name => 'Thijs van der Vossen', :email => 'thijs@fngtps.com'}
|
5
|
-
- Thijs van der Vossen, thijs@fngtps.com, Fingertips
|
6
|
-
=> {:name => 'Thijs van der Vossen', :email => 'thijs@fngtps.com', :company => 'Fingertips'}
|
7
|
-
- Thijs van der Vossen, thijs@fngtps.com, Fingertips, http://www.fngtps.com
|
8
|
-
=> {:name => 'Thijs van der Vossen', :email => 'thijs@fngtps.com', :company => 'Fingertips', :website => 'http://www.fngtps.com'}
|
data/test/cases/definition.txt
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
|
2
|
-
=> {}
|
3
|
-
User: A user
|
4
|
-
=> {:title => 'User', :definition => 'A user'}
|
5
|
-
Administrator: Any user with full administrative access.
|
6
|
-
=> {:title => 'Administrator', :definition => 'Any user with full administrative access.'}
|
7
|
-
Non-functional: Stories related to non-functional requirements.
|
8
|
-
=> {:title => 'Non-functional', :definition => 'Stories related to non-functional requirements.'}
|
9
|
-
Service: The part of the web service used by clients to provide search and retrieval as well as account, device, and user management.
|
10
|
-
=> {:title => 'Service', :definition => 'The part of the web service used by clients to provide search and retrieval as well as account, device, and user management.'}
|
data/test/cases/document.txt
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
Requirements Case
|
2
|
-
|
3
|
-
- Manfred Stienstra, manfred@fngtps.com
|
4
|
-
|
5
|
-
Saga is a tool for writing and publishing <requirements> in the form of user stories.
|
6
|
-
|
7
|
-
USER STORIES
|
8
|
-
|
9
|
-
As a writer I would like to write stories so that developers can implement them. - #1 todo
|
10
|
-
Stories are written in Saga format.
|
11
|
-
| As a writer I would like to add nested stories so that I can write requirements on two zoom levels. - todo
|
12
|
-
| Nested stories are basicaly substories of regular stories.
|
13
|
-
| As a writer I would like to add header so that I can group stories by theme. - todo
|
14
|
-
|
15
|
-
Writer: Someone who is responsible for writing down requirements in the form of stories
|
@@ -1,4 +0,0 @@
|
|
1
|
-
As an editor I would like to manage content so that I can keep the site up to date.
|
2
|
-
| As an editor I would like to add a page so that I can create new pages.
|
3
|
-
| As an editor I would like to remove a page so that I can remove unwanted pages.
|
4
|
-
=> {:description => 'As an editor I would like to manage content so that I can keep the site up to date.', :stories => [{:description => 'As an editor I would like to add a page so that I can create new pages.'}, {:description => 'As an editor I would like to remove a page so that I can remove unwanted pages.'}]}
|
data/test/cases/story.txt
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
As a developer I would like to have created database indexes so that the queries run as fast as possible.
|
2
|
-
=> {:description => 'As a developer I would like to have created database indexes so that the queries run as fast as possible.'}
|
3
|
-
As a recorder I would like to use TLS (SSL) so that my connection with the storage API is secure and I can be sure of the API’s identity. - #4 todo
|
4
|
-
=> {:description => 'As a recorder I would like to use TLS (SSL) so that my connection with the storage API is secure and I can be sure of the API’s identity.', :id => 4, :status => 'todo' }
|
5
|
-
As a subscriber I would like to be able to buy additional pre-paid recording slots even though I also have a subscription so that I don’t have to upgrade my subscription level when I temporarily need to make a lot more recordings than usual. - #16 todo
|
6
|
-
=> {:description => 'As a subscriber I would like to be able to buy additional pre-paid recording slots even though I also have a subscription so that I don’t have to upgrade my subscription level when I temporarily need to make a lot more recordings than usual.', :id => 16, :status => 'todo'}
|
@@ -1,22 +0,0 @@
|
|
1
|
-
|
2
|
-
=> {}
|
3
|
-
|
4
|
-
=> {}
|
5
|
-
#4
|
6
|
-
=> {:id => 4}
|
7
|
-
#12 todo
|
8
|
-
=> {:id => 12, :status => 'todo'}
|
9
|
-
#3 partially done
|
10
|
-
=> {:id => 3, :status => 'partially done'}
|
11
|
-
12
|
12
|
-
=> {:estimate => [12, :hours]}
|
13
|
-
1d
|
14
|
-
=> {:estimate => [1, :days]}
|
15
|
-
#1 1w partially done
|
16
|
-
=> {:id => 1, :estimate => [1, :weeks], :status => 'partially done'}
|
17
|
-
i12
|
18
|
-
=> {:iteration => 12}
|
19
|
-
#1 i1
|
20
|
-
=> {:id => 1, :iteration => 1}
|
21
|
-
#3 1 i2
|
22
|
-
=> {:id => 3, :iteration => 2, :estimate => [1, :hours]}
|
data/test/fixtures/document.erb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
2
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
4
|
-
<head>
|
5
|
-
<title>Requirements <%= title %> · Fingertips</title>
|
6
|
-
<link rel="stylesheet" type="text/css" media="all" href="http://resources.fngtps.com/2006/doc.css" />
|
7
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
-
<style type="text/css">
|
9
|
-
td.id { text-align: right !important; }
|
10
|
-
td.id a { color: #000; }
|
11
|
-
table.review { border-bottom: 1px solid #ccc; margin-top: -1em;}
|
12
|
-
table.review td { padding: .25em 0; vertical-align: top; text-align: left; min-width: 1em; }
|
13
|
-
table.review th { padding: .25em 0; vertical-align: top; text-align: right; }
|
14
|
-
table.review td.story { width: 100%; border-top: 1px solid #ccc; }
|
15
|
-
table.review td.meta { padding-left: 1em; border-top: 1px solid #ccc; text-align: right !important; white-space: nowrap; }
|
16
|
-
table.review td.notes { color: #666; padding: 0 0 .25em 0; font-style: italic; }
|
17
|
-
table.review .dropped { color: #666; text-decoration: line-through; }
|
18
|
-
table.review .done { color: #666; background-color: #f0f8ff; }
|
19
|
-
</style>
|
20
|
-
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
|
21
|
-
</head>
|
22
|
-
<body id="doc" class="requirements">
|
23
|
-
|
24
|
-
<p id="logo"><img src="http://resources.fngtps.com/2006/print-logo.png" alt="Fingertips design & development" /> </p>
|
25
|
-
|
26
|
-
<h1>Requirements <br /><%= title %></h1>
|
27
|
-
|
28
|
-
<% authors.each do |author| %>
|
29
|
-
<p class="author"><%= author %></p>
|
30
|
-
<% end %>
|
31
|
-
|
32
|
-
<% introduction.each do |paragraph| %>
|
33
|
-
<p><%== paragraph %></p>
|
34
|
-
<% end %>
|
35
|
-
|
36
|
-
<% unless stories.empty? %>
|
37
|
-
<h2>User stories</h2>
|
38
|
-
<% stories.each do |header, stories| %>
|
39
|
-
<% unless header.strip == '' %>
|
40
|
-
<h3><%= header %></h3>
|
41
|
-
<% end %>
|
42
|
-
<table class="review">
|
43
|
-
<tr>
|
44
|
-
<th></th>
|
45
|
-
<th title="id">#</th>
|
46
|
-
<th title="Estimate">e</th>
|
47
|
-
<th title="Iteration">i</th>
|
48
|
-
<th title="Status">s</th>
|
49
|
-
</tr>
|
50
|
-
<% stories.each do |story| %>
|
51
|
-
<tr class="<%= story[:status] %>" id="story<%= story[:id] %>">
|
52
|
-
<td class="story"><%= story[:description] %></td>
|
53
|
-
<td class="meta id"><%= story[:id] %></td>
|
54
|
-
<td class="meta estimate"><%= story[:estimate] %></td>
|
55
|
-
<td class="meta iteration"><%= story[:iteration] %></td>
|
56
|
-
<td class="meta status"><%= story[:status] %></td>
|
57
|
-
</tr>
|
58
|
-
<% if story[:notes] %>
|
59
|
-
<tr class="<%= story[:status] %>">
|
60
|
-
<td class="notes" colspan="5"><%= story[:notes] %></td>
|
61
|
-
</tr>
|
62
|
-
<% end %>
|
63
|
-
<% end %>
|
64
|
-
</table>
|
65
|
-
<% end %>
|
66
|
-
<% end %>
|
67
|
-
|
68
|
-
<% definitions.each do |header, definitions| %>
|
69
|
-
<% unless header.strip == '' %>
|
70
|
-
<h2><%= header %></h2>
|
71
|
-
<% end %>
|
72
|
-
<dl>
|
73
|
-
<% definitions.each do |definition| %>
|
74
|
-
<dt><%= definition[:title] %></dt>
|
75
|
-
<dd><%= definition[:definition] %></dd>
|
76
|
-
<% end %>
|
77
|
-
</dl>
|
78
|
-
<% end %>
|
79
|
-
</body>
|
80
|
-
</html>
|
data/test/saga_document_spec.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
describe "A Document" do
|
4
|
-
it "responds to accessors" do
|
5
|
-
document = Saga::Document.new
|
6
|
-
[:title, :introduction, :authors, :stories, :definitions].each do |method|
|
7
|
-
document.should.respond_to(method)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
it "stores stories in the order it receives them" do
|
12
|
-
document = Saga::Document.new
|
13
|
-
sections = %w(First Second Third Fourth Fifth)
|
14
|
-
sections.each do |section|
|
15
|
-
document.stories[section] = {}
|
16
|
-
end
|
17
|
-
document.stories.keys.should == sections
|
18
|
-
end
|
19
|
-
|
20
|
-
it "stores definitions in the order it receives them" do
|
21
|
-
document = Saga::Document.new
|
22
|
-
sections = %w(First Second Third Fourth Fifth)
|
23
|
-
sections.each do |section|
|
24
|
-
document.definitions[section] = {}
|
25
|
-
end
|
26
|
-
document.definitions.keys.should == sections
|
27
|
-
end
|
28
|
-
|
29
|
-
it "returns the number of stories as its length" do
|
30
|
-
document = Saga::Document.new
|
31
|
-
document.length == 0
|
32
|
-
|
33
|
-
document.stories[''] = [{}, {}]
|
34
|
-
document.length.should == 2
|
35
|
-
|
36
|
-
document.stories['Non-functional'] = [{}]
|
37
|
-
document.length.should == 3
|
38
|
-
end
|
39
|
-
|
40
|
-
it "know whether the document does or does not have stories" do
|
41
|
-
document = Saga::Document.new
|
42
|
-
document.should.be.empty
|
43
|
-
|
44
|
-
document.stories[''] = [{},{}]
|
45
|
-
document.should.not.be.empty
|
46
|
-
end
|
47
|
-
|
48
|
-
it "returns a list of used IDs" do
|
49
|
-
document = Saga::Document.new
|
50
|
-
document.used_ids.should == []
|
51
|
-
|
52
|
-
document.stories[''] = []
|
53
|
-
document.stories[''] << { :id => 2 }
|
54
|
-
document.used_ids.should == [2]
|
55
|
-
|
56
|
-
document.stories['Non-functional'] = []
|
57
|
-
document.stories['Non-functional'] << { :id => 12 }
|
58
|
-
document.used_ids.should == [2, 12]
|
59
|
-
|
60
|
-
document.stories['Non-functional'] << { :id => 3 }
|
61
|
-
document.used_ids.should == [2, 12, 3]
|
62
|
-
|
63
|
-
document.stories[''][0][:stories] = [
|
64
|
-
{}, {:id => 14}, {}, {:id => 5}
|
65
|
-
]
|
66
|
-
document.used_ids.should == [2, 14, 5, 12, 3]
|
67
|
-
end
|
68
|
-
|
69
|
-
it "returns a list of unused IDs" do
|
70
|
-
document = Saga::Document.new
|
71
|
-
document.unused_ids(0).should == []
|
72
|
-
document.unused_ids(3).should == [1,2,3]
|
73
|
-
document.unused_ids(4).should == [1,2,3,4]
|
74
|
-
|
75
|
-
document.stories[''] = []
|
76
|
-
document.stories[''] << { :id => 2 }
|
77
|
-
document.stories[''] << { :id => 4 }
|
78
|
-
document.stories[''] << { :id => 5 }
|
79
|
-
document.stories[''] << { :id => 6 }
|
80
|
-
document.stories[''] << { :id => 100 }
|
81
|
-
|
82
|
-
document.unused_ids(0).should == []
|
83
|
-
document.unused_ids(1).should == [1]
|
84
|
-
document.unused_ids(2).should == [1,3]
|
85
|
-
document.unused_ids(3).should == [1,3,7]
|
86
|
-
document.unused_ids(4).should == [1,3,7,8]
|
87
|
-
end
|
88
|
-
|
89
|
-
it "autofills ids" do
|
90
|
-
document = Saga::Document.new
|
91
|
-
document.autofill_ids
|
92
|
-
|
93
|
-
document.stories[''] = []
|
94
|
-
document.stories[''] << { :description => 'First story'}
|
95
|
-
document.stories[''] << { :description => 'Second story', :stories => [
|
96
|
-
{ :description => 'First nested story' }, { :id => 15, :description => 'Second nested story'}
|
97
|
-
] }
|
98
|
-
|
99
|
-
document.stories['Non-functional'] = []
|
100
|
-
document.stories['Non-functional'] << { :id => 1, :description => 'Third story' }
|
101
|
-
|
102
|
-
document.stories['Developer'] = []
|
103
|
-
document.stories['Developer'] << { :description => 'Fourth story' }
|
104
|
-
document.stories['Developer'] << { :id => 3, :description => 'Fifth story' }
|
105
|
-
|
106
|
-
document.autofill_ids
|
107
|
-
document.stories[''].map { |story| story[:id] }.should == [2, 4]
|
108
|
-
document.stories[''][1][:stories].map { |story| story[:id] }.should == [5, 15]
|
109
|
-
document.stories['Non-functional'].map { |story| story[:id] }.should == [1]
|
110
|
-
document.stories['Developer'].map { |story| story[:id] }.should == [6, 3]
|
111
|
-
end
|
112
|
-
end
|
data/test/saga_formatter_spec.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
describe "Formatter" do
|
4
|
-
before do
|
5
|
-
@document = Saga::Document.new
|
6
|
-
@document.title = 'Requirements API'
|
7
|
-
@document.authors = [
|
8
|
-
{:name => 'Thijs van der Vossen', :email => 'thijs@fngtps.com', :company => 'Fingertips', :website => 'http://www.fngtps.com'},
|
9
|
-
{:name => 'Manfred Stienstra', :email => 'manfred@fngtps.com', :company => 'Fingertips', :website => 'http://www.fngtps.com'}
|
10
|
-
]
|
11
|
-
@document.introduction = [
|
12
|
-
'A web service for interfacing with the service.', 'Exposes a public API to the application.', 'Uses <20> levels of redirection.'
|
13
|
-
]
|
14
|
-
@document.stories = [
|
15
|
-
['General', [
|
16
|
-
{:description => 'As a consumer I would like to use TLS (SSL) so that my connection with the API is secure', :id => 4, :status => 'todo', :notes => 'Use a self-signed CA certificate to create the certificates.', :stories => [
|
17
|
-
{ :description => 'As a consumer I would like to receive a certificate from the provider.', :id => 12, :status => 'done', :notes => 'The certificate for the CA.' },
|
18
|
-
{ :description => 'As a consumer I would like to receive a hosts file from the provider.', :id => 13, :status => 'done' }
|
19
|
-
]
|
20
|
-
},
|
21
|
-
{ :description => 'As a consumer I would like to get a list of users', :id => 5, :status => 'todo' }
|
22
|
-
]]
|
23
|
-
]
|
24
|
-
end
|
25
|
-
|
26
|
-
it "formats a saga document to HTML" do
|
27
|
-
html = Saga::Formatter.format(@document)
|
28
|
-
html.should.include('<h1>Requirements <br />Requirements API</h1>')
|
29
|
-
html.should.include('receive a certificate')
|
30
|
-
end
|
31
|
-
|
32
|
-
it "include the escaped introduction in the HTML output" do
|
33
|
-
html = Saga::Formatter.format(@document)
|
34
|
-
html.should.include('<p>Uses <20> levels of redirection.</p>')
|
35
|
-
html.should.include('receive a certificate')
|
36
|
-
end
|
37
|
-
|
38
|
-
it "formats a saga document to saga" do
|
39
|
-
saga = Saga::Formatter.saga_format(@document)
|
40
|
-
saga.should.include('Requirements Requirements API')
|
41
|
-
saga.should.include('receive a certificate')
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "with an external template" do
|
45
|
-
it "raises when the document.erb file doesn't exist" do
|
46
|
-
lambda {
|
47
|
-
Saga::Formatter.format(@document, :template => '/does/not/exist')
|
48
|
-
}.should.raise(ArgumentError, "The template at path `/does/not/exist/document.erb' could not be found.")
|
49
|
-
end
|
50
|
-
|
51
|
-
it "omits the helpers.rb file when it doesn't exist" do
|
52
|
-
formatter = Saga::Formatter.new(@document, :template => File.expand_path('../fixtures', __FILE__))
|
53
|
-
formatter.expects(:load).never
|
54
|
-
formatter.format.should.not.be.empty
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/test/saga_parser_spec.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
module ParserHelper
|
4
|
-
def parser
|
5
|
-
@parser ||= Saga::Parser.new
|
6
|
-
end
|
7
|
-
|
8
|
-
def parse(input)
|
9
|
-
Saga::Parser.parse(input)
|
10
|
-
end
|
11
|
-
|
12
|
-
def parse_title
|
13
|
-
parser.parse("Requirements API\n\n")
|
14
|
-
end
|
15
|
-
|
16
|
-
def parse_introduction
|
17
|
-
parser.parse("This document describes our API.\n\n")
|
18
|
-
end
|
19
|
-
|
20
|
-
def parse_story_marker
|
21
|
-
parser.parse('USER STORIES')
|
22
|
-
end
|
23
|
-
|
24
|
-
def parse_header
|
25
|
-
parser.parse('Storage')
|
26
|
-
end
|
27
|
-
|
28
|
-
def parse_story
|
29
|
-
parser.parse('As a recorder I would like to add a recording so that it becomes available. - #1 todo')
|
30
|
-
end
|
31
|
-
|
32
|
-
def parse_story_notes
|
33
|
-
parser.parse(' “Your recording was created successfully.”')
|
34
|
-
end
|
35
|
-
|
36
|
-
def parse_nested_story
|
37
|
-
parser.parse('| As a recorder I would like to add a recording so that it becomes available. - todo')
|
38
|
-
end
|
39
|
-
|
40
|
-
def parse_nested_story_notes
|
41
|
-
parser.parse('| “Your recording was created successfully.”')
|
42
|
-
end
|
43
|
-
|
44
|
-
def parse_definition
|
45
|
-
parser.parse('Other: Stories that don’t fit anywhere else.')
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe "Parser" do
|
50
|
-
it "should initialize and parse" do
|
51
|
-
document = Saga::Parser.parse('')
|
52
|
-
document.should.be.kind_of?(Saga::Document)
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should initialize and parse a reference document" do
|
56
|
-
document = Saga::Parser.parse(File.read(File.expand_path('../cases/document.txt', __FILE__)))
|
57
|
-
document.should.be.kind_of?(Saga::Document)
|
58
|
-
document.length.should == 3
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "A Parser, concerning the handling of input" do
|
63
|
-
extend ParserHelper
|
64
|
-
before { @parser = nil }
|
65
|
-
|
66
|
-
it "interprets the first line of the document as a title" do
|
67
|
-
document = parse('Requirements API')
|
68
|
-
document.title.should == 'API'
|
69
|
-
|
70
|
-
document = parse('Record and Search')
|
71
|
-
document.title.should == 'Record and Search'
|
72
|
-
end
|
73
|
-
|
74
|
-
it "interprets authors" do
|
75
|
-
document = parse('- Manfred Stienstra')
|
76
|
-
document.authors.map { |author| author[:name] }.should == ['Manfred Stienstra']
|
77
|
-
end
|
78
|
-
|
79
|
-
it "interprets the introduction" do
|
80
|
-
parse_title
|
81
|
-
parse_introduction
|
82
|
-
parser.document.introduction.should == ['This document describes our API.']
|
83
|
-
end
|
84
|
-
|
85
|
-
it "interprets stories without a header as being a gobal story" do
|
86
|
-
parse_title
|
87
|
-
parse_introduction
|
88
|
-
parse_story_marker
|
89
|
-
parse_story
|
90
|
-
|
91
|
-
parser.document.stories.keys.should == ['']
|
92
|
-
parser.document.stories[''].length.should == 1
|
93
|
-
parser.document.stories[''].first[:id].should == 1
|
94
|
-
end
|
95
|
-
|
96
|
-
it "interprets stories with a header as being part of that section" do
|
97
|
-
parse_title
|
98
|
-
parse_introduction
|
99
|
-
parse_story_marker
|
100
|
-
parse_header
|
101
|
-
parse_story
|
102
|
-
|
103
|
-
parser.document.stories.keys.should == ['Storage']
|
104
|
-
parser.document.stories['Storage'].length.should == 1
|
105
|
-
parser.document.stories['Storage'].first[:id].should == 1
|
106
|
-
end
|
107
|
-
|
108
|
-
it "interprets a comment after a story as being part of the story" do
|
109
|
-
parse_story_marker
|
110
|
-
parse_story
|
111
|
-
parse_story_notes
|
112
|
-
|
113
|
-
parser.document.stories.keys.should == ['']
|
114
|
-
parser.document.stories[''].length.should == 1
|
115
|
-
parser.document.stories[''].first[:id].should == 1
|
116
|
-
parser.document.stories[''].first[:notes].should == '“Your recording was created successfully.”'
|
117
|
-
end
|
118
|
-
|
119
|
-
it "interprets nested story as part of the parent story" do
|
120
|
-
parse_story_marker
|
121
|
-
parse_story
|
122
|
-
parse_story_notes
|
123
|
-
parse_nested_story
|
124
|
-
parse_nested_story_notes
|
125
|
-
parse_nested_story
|
126
|
-
parse_nested_story_notes
|
127
|
-
|
128
|
-
parser.document.stories.keys.should == ['']
|
129
|
-
parser.document.stories[''].length.should == 1
|
130
|
-
first_story = parser.document.stories[''][0]
|
131
|
-
first_story[:id].should == 1
|
132
|
-
first_story[:notes].should == '“Your recording was created successfully.”'
|
133
|
-
|
134
|
-
first_story[:stories].length.should == 2
|
135
|
-
first_story[:stories][0][:notes].should == '“Your recording was created successfully.”'
|
136
|
-
first_story[:stories][1][:notes].should == '“Your recording was created successfully.”'
|
137
|
-
end
|
138
|
-
|
139
|
-
it "interprets definitions without a header as being a gobal definition" do
|
140
|
-
parse_title
|
141
|
-
parse_introduction
|
142
|
-
parse_story_marker
|
143
|
-
parse_definition
|
144
|
-
|
145
|
-
parser.document.definitions.keys.should == ['']
|
146
|
-
parser.document.definitions[''].length.should == 1
|
147
|
-
parser.document.definitions[''].first[:title].should == 'Other'
|
148
|
-
end
|
149
|
-
|
150
|
-
it "interprets definitions with a header as being part of that section" do
|
151
|
-
parse_title
|
152
|
-
parse_introduction
|
153
|
-
parse_story_marker
|
154
|
-
parse_header
|
155
|
-
parse_definition
|
156
|
-
|
157
|
-
parser.document.definitions.keys.should == ['Storage']
|
158
|
-
parser.document.definitions['Storage'].length.should == 1
|
159
|
-
parser.document.definitions['Storage'].first[:title].should == 'Other'
|
160
|
-
end
|
161
|
-
|
162
|
-
it "properly parses hard cases" do
|
163
|
-
parse_story_marker
|
164
|
-
parser.parse('As a member I would like the app to keep the information it got from Twitter up-to-date so that changes I make on Twitter get propagated to my listing.')
|
165
|
-
|
166
|
-
story = parser.document.stories[''].first
|
167
|
-
story[:description].should == 'As a member I would like the app to keep the information it got from Twitter up-to-date so that changes I make on Twitter get propagated to my listing.'
|
168
|
-
end
|
169
|
-
end
|
data/test/saga_planning_spec.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
describe "An empty Planning" do
|
4
|
-
before do
|
5
|
-
@document = Saga::Document.new
|
6
|
-
@planning = Saga::Planning.new(@document)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "shows an empty planning message" do
|
10
|
-
@planning.to_s.should == 'There are no stories yet.'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "A Planning for unestimated stories" do
|
15
|
-
before do
|
16
|
-
@document = Saga::Document.new
|
17
|
-
@document.stories[''] = [{}, {}, {:stories => [{}, {}]}]
|
18
|
-
@planning = Saga::Planning.new(@document)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "shows a planning" do
|
22
|
-
@planning.to_s.should == "Unestimated : 5 stories"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "A Planning for estimated and unestimated stories" do
|
27
|
-
before do
|
28
|
-
@document = Saga::Document.new
|
29
|
-
@document.stories[''] = [{}, {}, {}]
|
30
|
-
@document.stories['Member'] = [{}, {:estimate => [6, :hours], :stories => [{:estimate => [6, :hours]}]}, {}]
|
31
|
-
@planning = Saga::Planning.new(@document)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "shows a planning" do
|
35
|
-
@planning.to_s.should ==
|
36
|
-
"Unplanned : 12 (2 stories)\n"+
|
37
|
-
"------------------------------\n"+
|
38
|
-
"Total : 12 (2 stories)\n"+
|
39
|
-
"\n"+
|
40
|
-
"Unestimated : 5 stories"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "A simple Planning" do
|
45
|
-
before do
|
46
|
-
@document = Saga::Document.new
|
47
|
-
@document.stories[''] = [{:estimate => [12, :hours]}]
|
48
|
-
@planning = Saga::Planning.new(@document)
|
49
|
-
end
|
50
|
-
|
51
|
-
it "shows a planning" do
|
52
|
-
@planning.to_s.should ==
|
53
|
-
"Unplanned : 12 (1 story)\n"+
|
54
|
-
"----------------------------\n"+
|
55
|
-
"Total : 12 (1 story)"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "A complicated Planning" do
|
60
|
-
before do
|
61
|
-
@document = Saga::Document.new
|
62
|
-
@document.stories[''] = [{:estimate => [12, :hours], :iteration => 1}, {:estimate => [8, :hours], :stories => [{}, {}]}]
|
63
|
-
@document.stories['Developer'] = [{:estimate => [1, :weeks], :iteration => 2,:stories => [{:estimate => [2, :hours], :iteration => 2}, {:estimate => [1, :hours]}]}, {:estimate => [1, :hours], :iteration => 2}]
|
64
|
-
@document.stories['Writer'] = [{:estimate => [5, :hours], :iteration => 1}, {:estimate => [2, :hours], :iteration => 3}]
|
65
|
-
@planning = Saga::Planning.new(@document)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "shows a planning" do
|
69
|
-
@planning.to_s.should ==
|
70
|
-
"Unplanned : 9 (2 stories)\n"+
|
71
|
-
"Iteration 1 : 17 (2 stories)\n"+
|
72
|
-
"Iteration 2 : 43 (3 stories)\n"+
|
73
|
-
"Iteration 3 : 2 (1 story)\n"+
|
74
|
-
"------------------------------\n"+
|
75
|
-
"Total : 71 (8 stories)\n"+
|
76
|
-
"\n"+
|
77
|
-
"Unestimated : 2 stories"
|
78
|
-
end
|
79
|
-
end
|
data/test/saga_runner_spec.rb
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
require 'singleton'
|
3
|
-
|
4
|
-
module OutputHelper
|
5
|
-
class Collector
|
6
|
-
attr_reader :written
|
7
|
-
def initialize
|
8
|
-
@written = []
|
9
|
-
end
|
10
|
-
|
11
|
-
def write(string)
|
12
|
-
@written << string
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def collect_stdout(&block)
|
17
|
-
collector = Collector.new
|
18
|
-
stdout = $stdout
|
19
|
-
$stdout = collector
|
20
|
-
begin
|
21
|
-
block.call
|
22
|
-
ensure
|
23
|
-
$stdout = stdout
|
24
|
-
end
|
25
|
-
collector.written.join
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "A Runner" do
|
30
|
-
extend OutputHelper
|
31
|
-
|
32
|
-
it "shows a help text when invoked without a command and options" do
|
33
|
-
runner = Saga::Runner.new([])
|
34
|
-
collect_stdout do
|
35
|
-
runner.run
|
36
|
-
end.should == runner.parser.to_s
|
37
|
-
end
|
38
|
-
|
39
|
-
it "shows a help test when the -h option is used" do
|
40
|
-
runner = Saga::Runner.new(%w(-h))
|
41
|
-
runner.stubs(:exit)
|
42
|
-
collect_stdout do
|
43
|
-
runner.run
|
44
|
-
end.should == runner.parser.to_s*2 # Because we stub exit it runs twice ):
|
45
|
-
end
|
46
|
-
|
47
|
-
it "generates a requirements stub to can get started" do
|
48
|
-
Saga::Runner.stubs(:author).returns({:name => "Manfred Stienstra"})
|
49
|
-
runner = Saga::Runner.new(%w(new))
|
50
|
-
output = collect_stdout do
|
51
|
-
runner.run
|
52
|
-
end
|
53
|
-
output.should.include('Requirements Title')
|
54
|
-
output.should.include('- Manfred Stienstra')
|
55
|
-
end
|
56
|
-
|
57
|
-
it "knows information about the user currently logged in to the system" do
|
58
|
-
author = Saga::Runner.author
|
59
|
-
author[:name].should.not.be.nil
|
60
|
-
end
|
61
|
-
|
62
|
-
it "converts the provided filename" do
|
63
|
-
runner = Saga::Runner.new(%w(requirements.txt))
|
64
|
-
runner.expects(:convert).with(File.expand_path('requirements.txt'), {}).returns('output')
|
65
|
-
collect_stdout do
|
66
|
-
runner.run
|
67
|
-
end.should == "output\n"
|
68
|
-
end
|
69
|
-
|
70
|
-
it "converts the provided filename when the convert command is given" do
|
71
|
-
runner = Saga::Runner.new(%w(convert requirements.txt))
|
72
|
-
runner.expects(:convert).with(File.expand_path('requirements.txt'), {}).returns('output')
|
73
|
-
collect_stdout do
|
74
|
-
runner.run
|
75
|
-
end.should == "output\n"
|
76
|
-
end
|
77
|
-
|
78
|
-
it "converts the provided filename with an external template" do
|
79
|
-
Saga::Parser.stubs(:parse)
|
80
|
-
File.stubs(:read)
|
81
|
-
Saga::Formatter.expects(:format).with do |_, options|
|
82
|
-
options[:template].should == File.expand_path('path/to/a/template')
|
83
|
-
end
|
84
|
-
runner = Saga::Runner.new(%W(convert --template path/to/a/template requirements.txt))
|
85
|
-
collect_stdout { runner.run }
|
86
|
-
end
|
87
|
-
|
88
|
-
it "inspects the parsed document" do
|
89
|
-
runner = Saga::Runner.new(%w(inspect requirements.txt))
|
90
|
-
runner.expects(:write_parsed_document).with(File.expand_path('requirements.txt'))
|
91
|
-
runner.run
|
92
|
-
end
|
93
|
-
|
94
|
-
it "autofills the parsed document" do
|
95
|
-
runner = Saga::Runner.new(%w(autofill requirements.txt))
|
96
|
-
runner.expects(:autofill).with(File.expand_path('requirements.txt')).returns('output')
|
97
|
-
collect_stdout do
|
98
|
-
runner.run
|
99
|
-
end.should == "output\n"
|
100
|
-
end
|
101
|
-
|
102
|
-
it "shows an overview of the time planned in the different iterations" do
|
103
|
-
runner = Saga::Runner.new(%w(planning requirements.txt))
|
104
|
-
runner.expects(:planning).with(File.expand_path('requirements.txt')).returns('output')
|
105
|
-
collect_stdout do
|
106
|
-
runner.run
|
107
|
-
end.should == "output\n"
|
108
|
-
end
|
109
|
-
|
110
|
-
it "copies the default template to the specified path" do
|
111
|
-
begin
|
112
|
-
destination = "/tmp/saga-template-dir"
|
113
|
-
Saga::Runner.new(%W(template #{destination})).run
|
114
|
-
File.read(File.join(destination, 'helpers.rb')).should ==
|
115
|
-
File.read(File.join(Saga::Formatter.template_path, 'default/helpers.rb'))
|
116
|
-
File.read(File.join(destination, 'document.erb')).should ==
|
117
|
-
File.read(File.join(Saga::Formatter.template_path, 'default/document.erb'))
|
118
|
-
ensure
|
119
|
-
FileUtils.rm_rf(destination)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
it "complains when tryin to create a template at an existing path" do
|
124
|
-
begin
|
125
|
-
destination = "/tmp/saga-template-dir"
|
126
|
-
FileUtils.mkdir_p(destination)
|
127
|
-
runner = Saga::Runner.new(%W(template #{destination}))
|
128
|
-
collect_stdout do
|
129
|
-
runner.run
|
130
|
-
end.should == "The directory `#{destination}' already exists!\n"
|
131
|
-
File.should.not.exist File.join(destination, 'helpers.rb')
|
132
|
-
File.should.not.exist File.join(destination, 'document.erb')
|
133
|
-
ensure
|
134
|
-
FileUtils.rm_rf(destination)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
data/test/saga_spec.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
describe "Saga" do
|
4
|
-
before do
|
5
|
-
@document = Saga::Document.new
|
6
|
-
@document.title = 'Requirements API'
|
7
|
-
@document.authors = [
|
8
|
-
{:name => 'Thijs van der Vossen', :email => 'thijs@fngtps.com', :company => 'Fingertips', :website => 'http://www.fngtps.com'},
|
9
|
-
{:name => 'Manfred Stienstra', :email => 'manfred@fngtps.com', :company => 'Fingertips', :website => 'http://www.fngtps.com'}
|
10
|
-
]
|
11
|
-
@document.introduction = [
|
12
|
-
'A web service for interfacing with the service.', 'Exposes a public API to the application.'
|
13
|
-
]
|
14
|
-
@document.stories = ActiveSupport::OrderedHash.new
|
15
|
-
[
|
16
|
-
['General', [
|
17
|
-
{:description => 'As a consumer I would like to use TLS (SSL) so that my connection with the API is secure', :id => 4, :status => 'todo', :notes => 'Use a self-signed CA certificate to create the certificates.' }
|
18
|
-
]]
|
19
|
-
].each do |key, stories|
|
20
|
-
@document.stories[key] = stories
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
it "round-trips through the parser and formatter" do
|
25
|
-
document = @document
|
26
|
-
2.times do
|
27
|
-
saga = Saga::Formatter.saga_format(document)
|
28
|
-
document = Saga::Parser.parse(saga)
|
29
|
-
end
|
30
|
-
|
31
|
-
document.title.should == @document.title
|
32
|
-
document.authors.should == @document.authors
|
33
|
-
document.stories.should == @document.stories
|
34
|
-
end
|
35
|
-
end
|
data/test/saga_tokenizer_spec.rb
DELETED
@@ -1,141 +0,0 @@
|
|
1
|
-
require File.expand_path('../spec_helper', __FILE__)
|
2
|
-
|
3
|
-
module CasesHelper
|
4
|
-
def _parse_expected(line)
|
5
|
-
eval(line[3..-1])
|
6
|
-
end
|
7
|
-
|
8
|
-
def each_case(path)
|
9
|
-
filename = File.expand_path("../cases/#{path}.txt", __FILE__)
|
10
|
-
input = ''
|
11
|
-
File.readlines(filename).each do |line|
|
12
|
-
if line.start_with?('=>')
|
13
|
-
yield input, _parse_expected(line)
|
14
|
-
input = ''
|
15
|
-
else
|
16
|
-
input << "#{line}\n"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "Tokenizer" do
|
23
|
-
extend CasesHelper
|
24
|
-
|
25
|
-
it "tokenizes story attributes input" do
|
26
|
-
each_case('story_attributes') do |input, expected|
|
27
|
-
Saga::Tokenizer.tokenize_story_attributes(input).should == expected
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
it "tokenizes story input" do
|
32
|
-
each_case('story') do |input, expected|
|
33
|
-
Saga::Tokenizer.tokenize_story(input).should == expected
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it "tokenizes hard stories" do
|
38
|
-
Saga::Tokenizer.tokenize_story('As a member I would like the app to keep the information it got from Twitter up-to-date so that changes I make on Twitter get propagated to my listing.').should == {
|
39
|
-
:description => 'As a member I would like the app to keep the information it got from Twitter up-to-date so that changes I make on Twitter get propagated to my listing.'
|
40
|
-
}
|
41
|
-
end
|
42
|
-
|
43
|
-
it "tokenizes definition input" do
|
44
|
-
each_case('definition') do |input, expected|
|
45
|
-
Saga::Tokenizer.tokenize_definition(input).should == expected
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
it "tokenizes author input" do
|
50
|
-
each_case('author') do |input, expected|
|
51
|
-
Saga::Tokenizer.tokenize_author(input).should == expected
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "A Tokenizer" do
|
57
|
-
before do
|
58
|
-
@parser = stub('Parser')
|
59
|
-
@tokenizer = Saga::Tokenizer.new(@parser)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "sends a tokenized story to the parser" do
|
63
|
-
line = 'As a recorder I would like to use TLS (SSL) so that my connection with the storage API is secure and I can be sure of the API’s identity. - #4 todo'
|
64
|
-
story = Saga::Tokenizer.tokenize_story(line)
|
65
|
-
|
66
|
-
@parser.expects(:handle_story).with(story)
|
67
|
-
@tokenizer.process_line(line)
|
68
|
-
end
|
69
|
-
|
70
|
-
it "sends a tokenized note to the parser" do
|
71
|
-
line = ' Optionally support SSL'
|
72
|
-
notes = line.strip
|
73
|
-
|
74
|
-
@parser.expects(:handle_notes).with(notes)
|
75
|
-
@tokenizer.process_line(line)
|
76
|
-
end
|
77
|
-
|
78
|
-
it "doesn't mistake a story note with a semicolon as a definition" do
|
79
|
-
line = ' It would be nice if we could use http://www.braintreepaymentsolutions.com/'
|
80
|
-
@parser.expects(:handle_notes).with(line.strip)
|
81
|
-
@tokenizer.process_line(line)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "sends a nested tokenized story to the parser" do
|
85
|
-
line = '| As a recorder I would like to use TLS (SSL) so that my connection with the storage API is secure and I can be sure of the API’s identity. - #4 todo'
|
86
|
-
story = Saga::Tokenizer.tokenize_story(line[1..-1])
|
87
|
-
|
88
|
-
@parser.expects(:handle_nested_story).with(story)
|
89
|
-
@tokenizer.process_line(line)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "sends a nested tokenized note to the parser" do
|
93
|
-
line = '| Optionally support SSL'
|
94
|
-
notes = line[4..-1]
|
95
|
-
|
96
|
-
@parser.expects(:handle_notes).with(notes)
|
97
|
-
@tokenizer.process_line(line)
|
98
|
-
end
|
99
|
-
|
100
|
-
it "sends a tokenized author to the parser" do
|
101
|
-
line = '- Manfred Stienstra, manfred@fngtps.com'
|
102
|
-
author = Saga::Tokenizer.tokenize_author(line)
|
103
|
-
|
104
|
-
@parser.expects(:handle_author).with(author)
|
105
|
-
@tokenizer.process_line(line)
|
106
|
-
end
|
107
|
-
|
108
|
-
it "sends a tokenized definition to the parser" do
|
109
|
-
line = 'Author: Someone who writes'
|
110
|
-
definition = Saga::Tokenizer.tokenize_definition(line)
|
111
|
-
|
112
|
-
@parser.expects(:handle_definition).with(definition)
|
113
|
-
@tokenizer.process_line(line)
|
114
|
-
end
|
115
|
-
|
116
|
-
it "send a tokenize defintion to the parser (slighly more complex)" do
|
117
|
-
line = 'Search and retrieval: Stories related to selecting and retrieving recordings.'
|
118
|
-
definition = Saga::Tokenizer.tokenize_definition(line)
|
119
|
-
|
120
|
-
@parser.expects(:handle_definition).with(definition)
|
121
|
-
@tokenizer.process_line(line)
|
122
|
-
end
|
123
|
-
|
124
|
-
it "forwards plain strings to the parser" do
|
125
|
-
[
|
126
|
-
'Requirements User Application',
|
127
|
-
'USER STORIES',
|
128
|
-
''
|
129
|
-
].each do |line|
|
130
|
-
@parser.expects(:handle_string).with(line)
|
131
|
-
@tokenizer.process_line(line)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
it "processes lines from the input" do
|
136
|
-
input = File.read(File.expand_path("../cases/story.txt", __FILE__))
|
137
|
-
count = input.split("\n").length
|
138
|
-
@tokenizer.expects(:process_line).times(count)
|
139
|
-
@tokenizer.process(input)
|
140
|
-
end
|
141
|
-
end
|