saga 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,64 @@
1
1
  = Saga
2
2
 
3
- Saga is a tool to convert the story format used at Fingertips to a readable document.
3
+ Saga is a tool to convert the requirements format used at Fingertips to HTML.
4
+
5
+ The Saga document consists of three major parts: an introduction, the stories, and definitions. The document is usually a succinct and complete description of a piece of software. Anyone reading the document should get a fairly good idea of the application without further information.
6
+
7
+ === The introduction
8
+
9
+ The first line of the document is the title. You can start the line with ‘Requirements’, but this is not compulsory.
10
+
11
+ Requirements Saga
12
+
13
+ After the title follows a list of authors. It's encouraged to add all the authors, this way people know who to speak to for more information about the stories. Only the name of the author is compulsory.
14
+
15
+ - Manfred Stienstra, manfred@fngtps.com, Fingertips, http://www.fngtps.com
16
+
17
+ The final part of the introduction is a concise description of the project.
18
+
19
+ Saga is a tool to convert the requirements format used at Fingertips to HTML.
20
+
21
+ === The stories
22
+
23
+ The stories section starts with the USER STORIES header.
24
+
25
+ USER STORIES
26
+
27
+ After this follows a list of stories. You can choose to add section headers, but this is optional. For example:
28
+
29
+ As a writer I want to convert my description of the project to a nice format. - #1 todo
30
+
31
+ Workflow
32
+
33
+ As a writer I want an example so I don't have to remember all the details of the format. – #2 todo
34
+ Try to find author details for the system information and autofill that in the generated stub.
35
+ As a writer I want to debug the parse process so I can find out what I did wrong. - #3 todo
36
+
37
+
38
+ Stories consist of a description and some extra information. The number behind the hash is the unique number of the story. We use the id to point to a story without having to type the whole description in everyday conversation. The little text label at the end describes the status of the story.
39
+
40
+ === Definitions
41
+
42
+ The document ends with a list of definitions. Here we define words used throughout the document. We only define words if they need a strict definition or if they might be misunderstood by someone. Like with the stories sections are optional.
43
+
44
+ ROLES
45
+
46
+ Writer: Someone who was appointed the task of writing the stories.
47
+ Developer: A person developing the application.
48
+
49
+ DEFINITIONS
50
+
51
+ Project: The software project the developers are working on.
52
+
53
+ === Usage
54
+
55
+ Usage: saga [command]
56
+
57
+ Commands:
58
+ new - prints a blank stub
59
+ convert <filename> - convert the stories to HTML
60
+ inspect <filename> - print the internals of the document
61
+ autofill <filename> - adds an id to stories without one
62
+
63
+ Options:
64
+ -h, --help Show help
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -11,5 +11,35 @@ module Saga
11
11
  @stories = ActiveSupport::OrderedHash.new
12
12
  @definitions = ActiveSupport::OrderedHash.new
13
13
  end
14
+
15
+ def used_ids
16
+ @stories.values.inject([]) do |ids, stories|
17
+ ids.concat stories.map { |story| story[:id] }
18
+ ids
19
+ end.compact
20
+ end
21
+
22
+ def unused_ids(limit)
23
+ position = 1
24
+ used_ids = used_ids()
25
+ (1..limit).map do
26
+ while used_ids.include?(position) do position += 1 end
27
+ used_ids << position
28
+ position
29
+ end
30
+ end
31
+
32
+ def length
33
+ stories.inject(0) { |total, (_, stories)| total + stories.length }
34
+ end
35
+
36
+ def autofill_ids
37
+ unused_ids = unused_ids(length - used_ids.length)
38
+ stories.each do |_, stories|
39
+ stories.each do |story|
40
+ story[:id] ||= unused_ids.shift
41
+ end
42
+ end
43
+ end
14
44
  end
15
45
  end
@@ -15,6 +15,7 @@ module Saga
15
15
  opts.separator " new - prints a blank stub"
16
16
  opts.separator " convert <filename> - convert the stories to HTML"
17
17
  opts.separator " inspect <filename> - print the internals of the document"
18
+ opts.separator " autofill <filename> - adds an id to stories without one"
18
19
  opts.separator ""
19
20
  opts.separator "Options:"
20
21
  opts.on("-h", "--help", "Show help") do
@@ -31,6 +32,10 @@ module Saga
31
32
  Saga::Formatter.format(document, :template => 'saga')
32
33
  end
33
34
 
35
+ def convert(filename)
36
+ Saga::Formatter.format(Saga::Parser.parse(File.read(filename)))
37
+ end
38
+
34
39
  def write_parsed_document(filename)
35
40
  document = Saga::Parser.parse(File.read(filename))
36
41
  puts document.title
@@ -41,10 +46,13 @@ module Saga
41
46
  document.definitions.each { |header, definitions| puts header; definitions.each { |definition| p definition } }
42
47
  end
43
48
 
44
- def convert(filename)
45
- Saga::Formatter.format(Saga::Parser.parse(File.read(filename)))
49
+ def autofill(filename)
50
+ document = Saga::Parser.parse(File.read(filename))
51
+ document.autofill_ids
52
+ Saga::Formatter.format(document, :template => 'saga')
46
53
  end
47
54
 
55
+
48
56
  def run_command(command, options)
49
57
  case command
50
58
  when 'new'
@@ -53,6 +61,8 @@ module Saga
53
61
  puts convert(File.expand_path(@argv[1]))
54
62
  when 'inspect'
55
63
  write_parsed_document(File.expand_path(@argv[1]))
64
+ when 'autofill'
65
+ puts autofill(File.expand_path(@argv[1]))
56
66
  else
57
67
  puts convert(File.expand_path(command))
58
68
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{saga}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Manfred Stienstra"]
12
- s.date = %q{2010-03-31}
12
+ s.date = %q{2010-04-02}
13
13
  s.default_executable = %q{saga}
14
14
  s.description = %q{Saga is a tool to convert stories syntax to a nicely formatted document.}
15
15
  s.email = %q{manfred@fngtps.com}
@@ -4,4 +4,19 @@ Requirements <%= title %>
4
4
  - <%= format_author(author) %>
5
5
  <% end %>
6
6
 
7
- USER STORIES
7
+ USER STORIES
8
+ <% stories.each do |header, stories| %>
9
+
10
+ <%= "#{header}\n" if header %>
11
+ <% stories.each do |story| %>
12
+ <%= format_story(story) %>
13
+ <% end %>
14
+ <% end %>
15
+
16
+ <% definitions.each do |header, definitions| %>
17
+
18
+ <%= "#{header}\n" if header %>
19
+ <% definitions.each do |definition| %>
20
+ <%= format_definition(definition) %>
21
+ <% end %>
22
+ <% end %>
@@ -4,4 +4,18 @@ module Helpers
4
4
  author[key]
5
5
  end.compact.join(', ')
6
6
  end
7
+
8
+ def format_story(story)
9
+ story_attributes = []
10
+ story_attributes << "##{story[:id]}" if story[:id]
11
+ story_attributes << "##{story[:status]}" if story[:status]
12
+
13
+ parts = [[story[:description], story_attributes.join(' ')].join(' - ')]
14
+ parts << " #{story[:notes]}" if story[:notes]
15
+ parts.join("\n\n")
16
+ end
17
+
18
+ def format_definition(definition)
19
+ [definition[:title], definition[:definition]].join(': ')
20
+ end
7
21
  end
@@ -25,4 +25,61 @@ describe "A Document" do
25
25
  end
26
26
  document.definitions.keys.should == sections
27
27
  end
28
+
29
+ it "should return a list of used IDs" do
30
+ document = Saga::Document.new
31
+ document.used_ids.should == []
32
+
33
+ document.stories[''] = []
34
+ document.stories[''] << { :id => 2 }
35
+ document.used_ids.should == [2]
36
+
37
+ document.stories['Non-functional'] = []
38
+ document.stories['Non-functional'] << { :id => 12 }
39
+ document.used_ids.should == [2, 12]
40
+
41
+ document.stories['Non-functional'] << { :id => 3 }
42
+ document.used_ids.should == [2, 12, 3]
43
+ end
44
+
45
+ it "should return a list of unused IDs" do
46
+ document = Saga::Document.new
47
+ document.unused_ids(0).should == []
48
+ document.unused_ids(3).should == [1,2,3]
49
+ document.unused_ids(4).should == [1,2,3,4]
50
+
51
+ document.stories[''] = []
52
+ document.stories[''] << { :id => 2 }
53
+ document.stories[''] << { :id => 4 }
54
+ document.stories[''] << { :id => 5 }
55
+ document.stories[''] << { :id => 6 }
56
+ document.stories[''] << { :id => 100 }
57
+
58
+ document.unused_ids(0).should == []
59
+ document.unused_ids(1).should == [1]
60
+ document.unused_ids(2).should == [1,3]
61
+ document.unused_ids(3).should == [1,3,7]
62
+ document.unused_ids(4).should == [1,3,7,8]
63
+ end
64
+
65
+ it "should autofills ids" do
66
+ document = Saga::Document.new
67
+ document.autofill_ids
68
+
69
+ document.stories[''] = []
70
+ document.stories[''] << { :description => 'First story'}
71
+ document.stories[''] << { :description => 'Second story'}
72
+
73
+ document.stories['Non-functional'] = []
74
+ document.stories['Non-functional'] << { :id => 1, :description => 'Third story' }
75
+
76
+ document.stories['Developer'] = []
77
+ document.stories['Developer'] << { :description => 'Fourth story' }
78
+ document.stories['Developer'] << { :id => 3, :description => 'Fifth story' }
79
+
80
+ document.autofill_ids
81
+ document.stories[''].map { |story| story[:id] }.should == [2, 4]
82
+ document.stories['Non-functional'].map { |story| story[:id] }.should == [1]
83
+ document.stories['Developer'].map { |story| story[:id] }.should == [5, 3]
84
+ end
28
85
  end
@@ -80,4 +80,12 @@ describe "A Runner" do
80
80
  runner.expects(:write_parsed_document).with(File.expand_path('requirements.txt'))
81
81
  runner.run
82
82
  end
83
+
84
+ it "autofills the parsed document" do
85
+ runner = Saga::Runner.new(%w(autofill requirements.txt))
86
+ runner.expects(:autofill).with(File.expand_path('requirements.txt')).returns('output')
87
+ collect_stdout do
88
+ runner.run
89
+ end.should == "output\n"
90
+ end
83
91
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saga
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manfred Stienstra
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-31 00:00:00 +02:00
12
+ date: 2010-04-02 00:00:00 +02:00
13
13
  default_executable: saga
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency