docbert 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/docbert.rb +9 -0
- data/lib/docbert/converters/gherkin.rb +69 -0
- data/lib/docbert/converters/step_definitions.rb +52 -0
- data/lib/docbert/converters/step_definitions/parameterized_content.rb +38 -0
- data/lib/docbert/parser.rb +66 -0
- data/lib/docbert/parser/definition_node.rb +31 -0
- data/lib/docbert/parser/example_node.rb +83 -0
- data/lib/docbert/parser/parameterized_content.rb +43 -0
- data/lib/docbert/version.rb +3 -0
- metadata +102 -0
data/lib/docbert.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
|
3
|
+
require 'docbert/parser'
|
4
|
+
require 'docbert/parser/example_node'
|
5
|
+
require 'docbert/parser/definition_node'
|
6
|
+
require 'docbert/parser/parameterized_content'
|
7
|
+
require 'docbert/converters/gherkin'
|
8
|
+
require 'docbert/converters/step_definitions'
|
9
|
+
require 'docbert/converters/step_definitions/parameterized_content'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'kramdown/parser/kramdown'
|
2
|
+
|
3
|
+
module Docbert
|
4
|
+
module Converters
|
5
|
+
class Gherkin < Kramdown::Converter::Base
|
6
|
+
Kramdown::Converter::Gherkin = self
|
7
|
+
|
8
|
+
def convert(root)
|
9
|
+
root.
|
10
|
+
children.
|
11
|
+
map { |el| send("convert_#{el.type}", el) }.
|
12
|
+
compact.
|
13
|
+
join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert_example(el)
|
19
|
+
convert_example_generic('Scenario', el)
|
20
|
+
end
|
21
|
+
|
22
|
+
def convert_example_background(el)
|
23
|
+
_, body = el.children
|
24
|
+
|
25
|
+
" Background:\n" << convert_example_body(body, " ")
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_example_body(body, indent)
|
29
|
+
body.
|
30
|
+
value.
|
31
|
+
split(/\n/).
|
32
|
+
map { |step| "#{indent}#{step.strip}\n" }.
|
33
|
+
join
|
34
|
+
end
|
35
|
+
|
36
|
+
def convert_example_generic(type, el)
|
37
|
+
title, body = el.children
|
38
|
+
|
39
|
+
convert_example_title(title, " ", type) +
|
40
|
+
convert_example_body(body, " ")
|
41
|
+
end
|
42
|
+
|
43
|
+
def convert_example_title(title, indent, type)
|
44
|
+
"#{indent}#{type}: #{title.value}\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
def convert_example_outline(el)
|
48
|
+
convert_example_generic('Scenario Outline', el)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Convert a level 1 header to a feature title.
|
52
|
+
#
|
53
|
+
# Current limitations:
|
54
|
+
# - Expects one and only one level 1 header to exist
|
55
|
+
# - Doesn't allow additional markup to be specified in the header
|
56
|
+
def convert_header(el)
|
57
|
+
if el.options[:level] == 1
|
58
|
+
feature_title = el.children.first.value
|
59
|
+
|
60
|
+
"Feature: #{feature_title}\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(name, *args)
|
65
|
+
super if name.to_s !~ /^convert_/
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'kramdown/parser/kramdown'
|
2
|
+
|
3
|
+
module Docbert
|
4
|
+
module Converters
|
5
|
+
class StepDefinitions < Kramdown::Converter::Base
|
6
|
+
Kramdown::Converter::StepDefinitions = self
|
7
|
+
|
8
|
+
def convert(root)
|
9
|
+
root.
|
10
|
+
children.
|
11
|
+
map { |el| send("convert_#{el.type}", el) }.
|
12
|
+
compact.
|
13
|
+
join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert_example_definition(el)
|
19
|
+
title_el, body_el = el.children
|
20
|
+
|
21
|
+
title = convert_example_definition_title(title_el)
|
22
|
+
par_list = if title.params.empty?
|
23
|
+
''
|
24
|
+
else
|
25
|
+
'|' << title.params.join(', ') << '|'
|
26
|
+
end
|
27
|
+
body = convert_example_definition_body(body_el)
|
28
|
+
|
29
|
+
<<-STEPD
|
30
|
+
Given(/^#{title}$/) do #{par_list}
|
31
|
+
steps <<-EOS
|
32
|
+
#{body}
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
|
36
|
+
STEPD
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_example_definition_body(el)
|
40
|
+
ParameterizedContent.new(el) { |p| '#{' + p + '}' }
|
41
|
+
end
|
42
|
+
|
43
|
+
def convert_example_definition_title(el)
|
44
|
+
ParameterizedContent.new(el) { |p| '(.+)' }
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(name, *args)
|
48
|
+
super if name.to_s !~ /^convert_/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Docbert
|
2
|
+
module Converters
|
3
|
+
class StepDefinitions
|
4
|
+
class ParameterizedContent
|
5
|
+
def initialize(el, &block)
|
6
|
+
self.el = el
|
7
|
+
self.block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def params
|
11
|
+
@params ||= el.
|
12
|
+
children.
|
13
|
+
select { |e| param?(e) }.
|
14
|
+
map { |e| param(e) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@s ||= el.
|
19
|
+
children.
|
20
|
+
map { |e| param?(e) ? block.(param(e)) : e.value }.
|
21
|
+
join
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_accessor :el, :block
|
27
|
+
|
28
|
+
def param?(el)
|
29
|
+
el.type == :example_definition_param
|
30
|
+
end
|
31
|
+
|
32
|
+
def param(el)
|
33
|
+
el.value.gsub(' ', '_')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'kramdown/parser/kramdown'
|
2
|
+
|
3
|
+
module Docbert
|
4
|
+
class Parser < Kramdown::Parser::Kramdown
|
5
|
+
EXAMPLE_START = /#{BLANK_LINE}?^\s*Example:/
|
6
|
+
EXAMPLE_BACKGROUND_START = /#{BLANK_LINE}?^\s*Background:/
|
7
|
+
EXAMPLE_DEFINITION_START = /#{BLANK_LINE}?^\s*Definition:/
|
8
|
+
EXAMPLE_OUTLINE_START = /#{BLANK_LINE}?^\s*Example Outline:/
|
9
|
+
|
10
|
+
def initialize(source, options)
|
11
|
+
super
|
12
|
+
|
13
|
+
register_example_background_parser
|
14
|
+
register_example_definition_parser
|
15
|
+
register_example_outline_parser
|
16
|
+
register_example_parser
|
17
|
+
end
|
18
|
+
|
19
|
+
public :new_block_el
|
20
|
+
|
21
|
+
def new_el(type, value)
|
22
|
+
Element.new(type, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse_example
|
28
|
+
@tree.children << ExampleNode.as_example(self, @src).to_element
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_example_background
|
32
|
+
@tree.children << ExampleNode.as_background(self, @src).to_element
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_example_definition
|
36
|
+
@tree.children << ExampleNode.as_definition(self, @src).to_element
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_example_outline
|
40
|
+
@tree.children << ExampleNode.as_example_outline(self, @src).to_element
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_example_background_parser
|
44
|
+
@block_parsers.unshift(:example_background)
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_example_definition_parser
|
48
|
+
@block_parsers.unshift(:example_definition)
|
49
|
+
end
|
50
|
+
|
51
|
+
def register_example_parser
|
52
|
+
@block_parsers.unshift(:example)
|
53
|
+
end
|
54
|
+
|
55
|
+
def register_example_outline_parser
|
56
|
+
@block_parsers.unshift(:example_outline)
|
57
|
+
end
|
58
|
+
|
59
|
+
Kramdown::Parser::Docbert = self
|
60
|
+
|
61
|
+
define_parser :example, /#{EXAMPLE_START}/
|
62
|
+
define_parser :example_background, /#{EXAMPLE_BACKGROUND_START}/
|
63
|
+
define_parser :example_definition, /#{EXAMPLE_DEFINITION_START}/
|
64
|
+
define_parser :example_outline, /#{EXAMPLE_OUTLINE_START}/
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Docbert
|
2
|
+
class Parser
|
3
|
+
class DefinitionNode < ExampleNode
|
4
|
+
def initialize(element_factory, fragment)
|
5
|
+
super :example_definition,
|
6
|
+
EXAMPLE_DEFINITION_START,
|
7
|
+
EXAMPLE_KEYWORDS,
|
8
|
+
element_factory,
|
9
|
+
fragment
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def parse_title(frag)
|
15
|
+
super.tap { |el|
|
16
|
+
content = ParameterizedContent.new(element_factory, el.value)
|
17
|
+
|
18
|
+
el.children.concat content.to_elements
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_body(frag)
|
23
|
+
super.tap { |el|
|
24
|
+
content = ParameterizedContent.new(element_factory, el.value)
|
25
|
+
|
26
|
+
el.children.concat content.to_elements
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Docbert
|
2
|
+
class Parser
|
3
|
+
class ExampleNode
|
4
|
+
BLANK_LINE = Kramdown::Parser::Kramdown::BLANK_LINE
|
5
|
+
|
6
|
+
EXAMPLE_KEYWORDS = 'Given|When|Then|And|But|\\||"""'
|
7
|
+
EXAMPLE_OUTLINE_KEYWORDS = "#{EXAMPLE_KEYWORDS}|Examples:"
|
8
|
+
|
9
|
+
def self.as_background(element_factory, fragment)
|
10
|
+
new(:example_background,
|
11
|
+
EXAMPLE_BACKGROUND_START,
|
12
|
+
EXAMPLE_KEYWORDS,
|
13
|
+
element_factory,
|
14
|
+
fragment)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.as_definition(element_factory, fragment)
|
18
|
+
DefinitionNode.new(element_factory, fragment)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.as_example(element_factory, fragment)
|
22
|
+
new(:example, EXAMPLE_START, EXAMPLE_KEYWORDS, element_factory, fragment)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.as_example_outline(element_factory, fragment)
|
26
|
+
new(:example_outline,
|
27
|
+
EXAMPLE_OUTLINE_START,
|
28
|
+
EXAMPLE_OUTLINE_KEYWORDS,
|
29
|
+
element_factory,
|
30
|
+
fragment)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(type, start_re, keywords, element_factory, fragment)
|
34
|
+
self.type = type
|
35
|
+
self.start_re = start_re
|
36
|
+
self.body_re = /(?:\s*(?:#{keywords}).+?\n|#{BLANK_LINE})+/m
|
37
|
+
self.element_factory = element_factory
|
38
|
+
self.fragment = fragment
|
39
|
+
|
40
|
+
parse
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_element
|
44
|
+
element
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_accessor :type, :start_re, :body_re, :element_factory, :fragment, :element
|
50
|
+
|
51
|
+
# Parse an example block.
|
52
|
+
#
|
53
|
+
# Currently only creates 2 nodes: one for the title and another for the
|
54
|
+
# example body.
|
55
|
+
def parse
|
56
|
+
skip_start fragment
|
57
|
+
|
58
|
+
self.element = parse_root(fragment)
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_body(frag)
|
62
|
+
body = frag.scan(body_re).gsub(/^ +/, '').strip
|
63
|
+
|
64
|
+
element_factory.new_el(:example_body, body)
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_root(frag)
|
68
|
+
element_factory.new_block_el(type).
|
69
|
+
tap { |el| el.children << parse_title(frag) << parse_body(frag) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_title(frag)
|
73
|
+
title = frag.scan(/.*?\n/).strip
|
74
|
+
|
75
|
+
element_factory.new_el(:example_title, title)
|
76
|
+
end
|
77
|
+
|
78
|
+
def skip_start(frag)
|
79
|
+
frag.skip start_re
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Docbert
|
2
|
+
class Parser
|
3
|
+
class ParameterizedContent
|
4
|
+
WORDS = /\w(?:\w+| )*\w/
|
5
|
+
|
6
|
+
def initialize(element_factory, fragment)
|
7
|
+
self.element_factory = element_factory
|
8
|
+
self.scanner = StringScanner.new(fragment)
|
9
|
+
self.elements = build_elements
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_elements
|
13
|
+
elements
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_accessor :element_factory, :scanner, :elements
|
19
|
+
|
20
|
+
def build_element
|
21
|
+
build_text || build_param
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_elements(elements = [])
|
25
|
+
return elements if scanner.eos?
|
26
|
+
|
27
|
+
build_elements(elements << build_element)
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_param
|
31
|
+
scanner.skip(/</) and name = scanner.scan(WORDS) and scanner.skip(/>/)
|
32
|
+
|
33
|
+
element_factory.new_el(:example_definition_param, name) if name
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_text
|
37
|
+
if text = scanner.scan(/[^<]+/)
|
38
|
+
element_factory.new_el(:text, text)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: docbert
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Leal
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
none: false
|
21
|
+
name: kramdown
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
requirement: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 1.0.0
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
none: false
|
37
|
+
name: cucumber
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
none: false
|
53
|
+
name: rspec-expectations
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
none: false
|
62
|
+
description: See https://github.com/mojotech/docbert/features/index.feature.md
|
63
|
+
email:
|
64
|
+
- dleal@mojotech.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- lib/docbert.rb
|
70
|
+
- lib/docbert/converters/gherkin.rb
|
71
|
+
- lib/docbert/converters/step_definitions.rb
|
72
|
+
- lib/docbert/converters/step_definitions/parameterized_content.rb
|
73
|
+
- lib/docbert/parser.rb
|
74
|
+
- lib/docbert/parser/definition_node.rb
|
75
|
+
- lib/docbert/parser/example_node.rb
|
76
|
+
- lib/docbert/parser/parameterized_content.rb
|
77
|
+
- lib/docbert/version.rb
|
78
|
+
homepage: https://github.com/mojotech/docbert
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
none: false
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
none: false
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project: ! '[none]'
|
98
|
+
rubygems_version: 1.8.23
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: A tool to help write literate cucumber
|
102
|
+
test_files: []
|