saga 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ html
2
+ pkg
data/.kick ADDED
@@ -0,0 +1,14 @@
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{^test/(.+)_spec\.rb$}
11
+ file
12
+ end
13
+ end)
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Fingertips, Manfred Stienstra <manfred@fngtps.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Saga
2
+
3
+ Saga is a tool to convert the story format used at Fingertips to a readable document.
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+
4
+ desc "Run all specs by default"
5
+ task :default => [:spec]
6
+
7
+ desc "Run all specs"
8
+ task :spec do
9
+ Dir[File.dirname(__FILE__) + '/test/**/*_spec.rb'].each do |file|
10
+ load file
11
+ end
12
+ end
13
+
14
+ namespace :documentation do
15
+ Rake::RDocTask.new(:generate) do |rd|
16
+ rd.main = "README.rdoc"
17
+ rd.rdoc_files.include("README.rdoc", "LICENSE", "bin/**/*.rb", "lib/**/*.rb", "templates/**/*.rb")
18
+ rd.options << "--all" << "--charset" << "utf-8"
19
+ end
20
+ end
21
+
22
+ begin
23
+ require 'jeweler'
24
+ Jeweler::Tasks.new do |s|
25
+ s.name = "saga"
26
+ s.summary = s.description = "Saga is a tool to convert stories syntax to a nicely formatted document."
27
+ s.homepage = "http://fingertips.github.com"
28
+ s.email = "manfred@fngtps.com"
29
+ s.authors = ["Manfred Stienstra"]
30
+ end
31
+ rescue LoadError
32
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/saga ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.expand_path('../../lib', __FILE__))
4
+ require 'saga'
5
+ Saga.run(ARGV)
data/lib/saga.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Saga
2
+ autoload :Document, 'saga/document'
3
+ autoload :Formatter, 'saga/formatter'
4
+ autoload :Parser, 'saga/parser'
5
+ autoload :Runner, 'saga/runner'
6
+ autoload :Tokenizer, 'saga/tokenizer'
7
+
8
+ def self.run(argv)
9
+ runner = ::Saga::Runner.new(argv)
10
+ runner.run
11
+ runner
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Saga
2
+ class Document
3
+ attr_accessor :title, :introduction, :authors, :stories, :definitions
4
+
5
+ def initialize
6
+ @title = ''
7
+ @introduction = []
8
+ @authors = []
9
+ @stories = {}
10
+ @definitions = {}
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'erubis'
3
+
4
+ module Saga
5
+ class Formatter
6
+ TEMPLATE_PATH = File.expand_path('../../../templates', __FILE__)
7
+
8
+ def initialize(document)
9
+ @document = document
10
+ end
11
+
12
+ def format
13
+ helpers_file = File.join(self.class.template_path, 'default/helpers.rb')
14
+ load helpers_file
15
+ @document.extend(Helpers)
16
+ binding = @document.send(:binding)
17
+
18
+ template_file = File.join(self.class.template_path, 'default/document.erb')
19
+ template = Erubis::Eruby.new(File.read(template_file))
20
+ template.result(binding)
21
+ end
22
+
23
+ def self.format(document)
24
+ formatter = new(document)
25
+ formatter.format
26
+ end
27
+
28
+ def self.template_path
29
+ TEMPLATE_PATH
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,51 @@
1
+ module Saga
2
+ class Parser
3
+ attr_accessor :document
4
+
5
+ def initialize
6
+ @tokenizer = ::Saga::Tokenizer.new(self)
7
+ @document = ::Saga::Document.new
8
+ @current_section = :title
9
+ @current_header = ''
10
+ end
11
+
12
+ def parse(input)
13
+ @tokenizer.process(input)
14
+ @document
15
+ end
16
+
17
+ def handle_author(author)
18
+ @document.authors << author
19
+ end
20
+
21
+ def handle_story(story)
22
+ @document.stories[@current_header] ||= []
23
+ @document.stories[@current_header] << story
24
+ end
25
+
26
+ def handle_definition(definition)
27
+ @document.definitions[@current_header] ||= []
28
+ @document.definitions[@current_header] << definition
29
+ end
30
+
31
+ def handle_string(string)
32
+ return if string.strip == ''
33
+ return(@current_section = :body) if string.strip == 'USER STORIES'
34
+
35
+ case @current_section
36
+ when :title
37
+ @document.title = string.gsub(/^requirements/i, '').strip
38
+ @current_section = :introduction
39
+ when :introduction
40
+ @document.introduction << string
41
+ else
42
+ @current_header = string.strip
43
+ end
44
+ end
45
+
46
+ def self.parse(input)
47
+ parser = new
48
+ parser.parse(input)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ require 'optparse'
2
+
3
+ module Saga
4
+ class Runner
5
+ def initialize(argv)
6
+ @argv = argv
7
+ @options = {}
8
+ end
9
+
10
+ def parser
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: saga [command] [options] <file>"
13
+ opts.separator ""
14
+ opts.on("-h", "--help", "Show help") do
15
+ puts opts
16
+ exit
17
+ end
18
+ end
19
+ end
20
+
21
+ def run_command(command, options)
22
+ case command
23
+ when :run
24
+ else
25
+ filename = File.expand_path(command)
26
+ document = Saga::Parser.parse(File.read(filename))
27
+ puts Saga::Formatter.format(document)
28
+ end
29
+ end
30
+
31
+ def run
32
+ argv = @argv.dup
33
+ parser.parse!(argv)
34
+ if command = argv.shift
35
+ run_command(command, @options)
36
+ else
37
+ puts parser.to_s
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,71 @@
1
+ module Saga
2
+ class Tokenizer
3
+ def initialize(parser)
4
+ @parser = parser
5
+ end
6
+
7
+ def process_line(input)
8
+ if input[0,2].downcase == 'as'
9
+ @parser.handle_story(self.class.tokenize_story(input))
10
+ elsif input[0,1] == '-'
11
+ @parser.handle_author(self.class.tokenize_author(input))
12
+ elsif input =~ /^(\w|[\s-])+:/
13
+ @parser.handle_definition(self.class.tokenize_definition(input))
14
+ else
15
+ @parser.handle_string(input)
16
+ end
17
+ end
18
+
19
+ def process(input)
20
+ input.split("\n").each do |line|
21
+ process_line(line)
22
+ end
23
+ end
24
+
25
+ def self.tokenize_story_attributes(input)
26
+ return {} if input.nil?
27
+
28
+ attributes = {}
29
+ rest = []
30
+ parts = input.split(/\s/)
31
+
32
+ parts.each do |part|
33
+ if part.strip == ''
34
+ next
35
+ elsif match = /\#(\d+)/.match(part)
36
+ attributes[:id] = match[1].to_i
37
+ else
38
+ rest << part
39
+ end
40
+ end
41
+
42
+ attributes[:status] = rest.join(' ') unless rest.empty?
43
+ attributes
44
+ end
45
+
46
+ def self.tokenize_story(input)
47
+ description, attributes = input.split('-')
48
+ story = tokenize_story_attributes(attributes)
49
+ story[:description] = description.strip
50
+ story
51
+ end
52
+
53
+ def self.tokenize_definition(input)
54
+ if match = /^([^:]+)\s*:\s*(.+)\s*$/.match(input)
55
+ {:title => match[1], :definition => match[2]}
56
+ else
57
+ {}
58
+ end
59
+ end
60
+
61
+ def self.tokenize_author(input)
62
+ author = {}
63
+ parts = input[1..-1].split(',')
64
+ author[:name] = parts[0].strip if parts[0]
65
+ author[:email] = parts[1].strip if parts[1]
66
+ author[:company] = parts[2].strip if parts[2]
67
+ author[:website] = parts[3].strip if parts[3]
68
+ author
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
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 %> &middot; 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 &amp; development" /> </p>
25
+
26
+ <h1>Requirements <br /><%= title %></h1>
27
+
28
+ <% authors.each do |author| %>
29
+ <p class="author"><%= format_author(author) %></p>
30
+ <% end %>
31
+
32
+ <% unless stories.empty? %>
33
+ <h2>User stories</h2>
34
+ <% stories.each do |header, stories| %>
35
+ <% unless header.strip == '' %>
36
+ <h3><%= header %></h3>
37
+ <% end %>
38
+ <table class="review">
39
+ <tr>
40
+ <th></th>
41
+ <th title="id">#</th>
42
+ <th title="Estimate">e</th>
43
+ <th title="Iteration">i</th>
44
+ <th title="Status">s</th>
45
+ </tr>
46
+ <% stories.each do |story| %>
47
+ <tr class="<%= story[:status] %>" id="story<%= story[:id] %>">
48
+ <td class="story"><%= story[:description] %></td>
49
+ <td class="meta id"><%= story[:id] %></td>
50
+ <td class="meta estimate"><%= story[:estimate] %></td>
51
+ <td class="meta iteration"><%= story[:iteration] %></td>
52
+ <td class="meta status"><%= story[:status] %></td>
53
+ </tr>
54
+ <% end %>
55
+ </table>
56
+ <% end %>
57
+ <% end %>
58
+
59
+ <% definitions.each do |header, definitions| %>
60
+ <% unless header.strip == '' %>
61
+ <h2><%= format_header(header) %></h2>
62
+ <% end %>
63
+ <dl>
64
+ <% definitions.each do |definition| %>
65
+ <dt><%= definition[:title] %></dt>
66
+ <dd><%= definition[:definition] %></dd>
67
+ <% end %>
68
+ </dl>
69
+ <% end %>
70
+ </body>
71
+ </html>
@@ -0,0 +1,17 @@
1
+ module Helpers
2
+ def format_author(author)
3
+ parts = []
4
+ parts << author[:name] if author[:name]
5
+ parts << "<a href=\"mailto:#{author[:email]}\">#{author[:email]}</a>" if author[:email]
6
+ if author[:website] and author[:company]
7
+ parts << "<a href=\"#{author[:website]}\">#{author[:company]}</a>"
8
+ elsif author[:company]
9
+ parts << author[:company]
10
+ end
11
+ parts.join(', ')
12
+ end
13
+
14
+ def format_header(header)
15
+ "#{header[0,1].upcase}#{header[1..-1].downcase}"
16
+ end
17
+ end
@@ -0,0 +1,8 @@
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'}
@@ -0,0 +1,10 @@
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.'}
@@ -0,0 +1,4 @@
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' }
@@ -0,0 +1,10 @@
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'}
@@ -0,0 +1,10 @@
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
+ end
@@ -0,0 +1,13 @@
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
+ end
8
+
9
+ it "formats a saga document to HTML" do
10
+ html = Saga::Formatter.format(@document)
11
+ html.should.include('<h1>Requirements <br />Requirements API</h1>')
12
+ end
13
+ end
@@ -0,0 +1,112 @@
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_definition
33
+ parser.parse('Other: Stories that don’t fit anywhere else.')
34
+ end
35
+ end
36
+
37
+ describe "Parser" do
38
+ it "should initialize and parse" do
39
+ document = Saga::Parser.parse('')
40
+ document.should.be.kind_of?(Saga::Document)
41
+ end
42
+ end
43
+
44
+ describe "A Parser, concerning the handling of input" do
45
+ extend ParserHelper
46
+ before { @parser = nil }
47
+
48
+ it "interprets the first line of the document as a title" do
49
+ document = parse('Requirements API')
50
+ document.title.should == 'API'
51
+
52
+ document = parse('Record and Search')
53
+ document.title.should == 'Record and Search'
54
+ end
55
+
56
+ it "interprets authors" do
57
+ document = parse('- Manfred Stienstra')
58
+ document.authors.map { |author| author[:name] }.should == ['Manfred Stienstra']
59
+ end
60
+
61
+ it "interprets the introduction" do
62
+ parse_title
63
+ parse_introduction
64
+ parser.document.introduction.should == ['This document describes our API.']
65
+ end
66
+
67
+ it "interprets stories without a header as being a gobal story" do
68
+ parse_title
69
+ parse_introduction
70
+ parse_story_marker
71
+ parse_story
72
+
73
+ parser.document.stories.keys.should == ['']
74
+ parser.document.stories[''].length.should == 1
75
+ parser.document.stories[''].first[:id].should == 1
76
+ end
77
+
78
+ it "interprets stories with a header as being part of that section" do
79
+ parse_title
80
+ parse_introduction
81
+ parse_story_marker
82
+ parse_header
83
+ parse_story
84
+
85
+ parser.document.stories.keys.should == ['Storage']
86
+ parser.document.stories['Storage'].length.should == 1
87
+ parser.document.stories['Storage'].first[:id].should == 1
88
+ end
89
+
90
+ it "interprets definitions without a header as being a gobal definition" do
91
+ parse_title
92
+ parse_introduction
93
+ parse_story_marker
94
+ parse_definition
95
+
96
+ parser.document.definitions.keys.should == ['']
97
+ parser.document.definitions[''].length.should == 1
98
+ parser.document.definitions[''].first[:title].should == 'Other'
99
+ end
100
+
101
+ it "interprets definitions with a header as being part of that section" do
102
+ parse_title
103
+ parse_introduction
104
+ parse_story_marker
105
+ parse_header
106
+ parse_definition
107
+
108
+ parser.document.definitions.keys.should == ['Storage']
109
+ parser.document.definitions['Storage'].length.should == 1
110
+ parser.document.definitions['Storage'].first[:title].should == 'Other'
111
+ end
112
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe "A Runner" do
4
+ it "should show a help text when invoked without a command and options" do
5
+ @runner = Saga::Runner.new([])
6
+ @runner.expects(:puts).with(@runner.parser.to_s)
7
+ @runner.run
8
+ end
9
+
10
+ it "should show a help test when the -h option is used" do
11
+ @runner = Saga::Runner.new(%w(-h))
12
+ @runner.expects(:puts).at_least(1)
13
+ @runner.stubs(:exit)
14
+ @runner.run
15
+ end
16
+ end
@@ -0,0 +1,105 @@
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 = nil
11
+ File.readlines(filename).each do |line|
12
+ if input.nil?
13
+ input = line.strip
14
+ else
15
+ yield input, _parse_expected(line)
16
+ input = nil
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 definition input" do
38
+ each_case('definition') do |input, expected|
39
+ Saga::Tokenizer.tokenize_definition(input).should == expected
40
+ end
41
+ end
42
+
43
+ it "tokenizes author input" do
44
+ each_case('author') do |input, expected|
45
+ Saga::Tokenizer.tokenize_author(input).should == expected
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "A Tokenizer" do
51
+ before do
52
+ @parser = stub('Parser')
53
+ @tokenizer = Saga::Tokenizer.new(@parser)
54
+ end
55
+
56
+ it "sends a tokenized story to the parser" do
57
+ 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'
58
+ story = Saga::Tokenizer.tokenize_story(line)
59
+
60
+ @parser.expects(:handle_story).with(story)
61
+ @tokenizer.process_line(line)
62
+ end
63
+
64
+ it "sends a tokenized author to the parser" do
65
+ line = '- Manfred Stienstra, manfred@fngtps.com'
66
+ author = Saga::Tokenizer.tokenize_author(line)
67
+
68
+ @parser.expects(:handle_author).with(author)
69
+ @tokenizer.process_line(line)
70
+ end
71
+
72
+ it "sends a tokenized definition to the parser" do
73
+ line = 'Author: Someone who writes'
74
+ definition = Saga::Tokenizer.tokenize_definition(line)
75
+
76
+ @parser.expects(:handle_definition).with(definition)
77
+ @tokenizer.process_line(line)
78
+ end
79
+
80
+ it "send a tokenize defintion to the parser (slighly more complex)" do
81
+ line = 'Search and retrieval: Stories related to selecting and retrieving recordings.'
82
+ definition = Saga::Tokenizer.tokenize_definition(line)
83
+
84
+ @parser.expects(:handle_definition).with(definition)
85
+ @tokenizer.process_line(line)
86
+ end
87
+
88
+ it "forwards plain strings to the parser" do
89
+ [
90
+ 'Requirements User Application',
91
+ 'USER STORIES',
92
+ ''
93
+ ].each do |line|
94
+ @parser.expects(:handle_string).with(line)
95
+ @tokenizer.process_line(line)
96
+ end
97
+ end
98
+
99
+ it "processes lines from the input" do
100
+ input = File.read(File.expand_path("../cases/story.txt", __FILE__))
101
+ count = input.split("\n").length
102
+ @tokenizer.expects(:process_line).times(count)
103
+ @tokenizer.process(input)
104
+ end
105
+ end
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'rubygems'
3
+ rescue LoadError
4
+ end
5
+
6
+ require 'mocha-on-bacon'
7
+
8
+ Bacon.extend Bacon::TapOutput
9
+ $:.unshift(File.expand_path('../../lib', __FILE__))
10
+ require 'saga'
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: saga
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Manfred Stienstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-31 00:00:00 +02:00
13
+ default_executable: saga
14
+ dependencies: []
15
+
16
+ description: Saga is a tool to convert stories syntax to a nicely formatted document.
17
+ email: manfred@fngtps.com
18
+ executables:
19
+ - saga
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .gitignore
27
+ - .kick
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - bin/saga
33
+ - lib/saga.rb
34
+ - lib/saga/document.rb
35
+ - lib/saga/formatter.rb
36
+ - lib/saga/parser.rb
37
+ - lib/saga/runner.rb
38
+ - lib/saga/tokenizer.rb
39
+ - templates/default/document.erb
40
+ - templates/default/helpers.rb
41
+ - test/cases/author.txt
42
+ - test/cases/definition.txt
43
+ - test/cases/story.txt
44
+ - test/cases/story_attributes.txt
45
+ - test/saga_document_spec.rb
46
+ - test/saga_formatter_spec.rb
47
+ - test/saga_parser_spec.rb
48
+ - test/saga_runner_spec.rb
49
+ - test/saga_tokenizer_spec.rb
50
+ - test/spec_helper.rb
51
+ has_rdoc: true
52
+ homepage: http://fingertips.github.com
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options:
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.5
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Saga is a tool to convert stories syntax to a nicely formatted document.
79
+ test_files:
80
+ - test/saga_document_spec.rb
81
+ - test/saga_formatter_spec.rb
82
+ - test/saga_parser_spec.rb
83
+ - test/saga_runner_spec.rb
84
+ - test/saga_tokenizer_spec.rb
85
+ - test/spec_helper.rb