BuildMaster 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +26 -0
- data/lib/buildmaster.rb +2 -1
- data/lib/buildmaster/build_number_file.rb +27 -0
- data/lib/buildmaster/buildnumber +1 -0
- data/lib/buildmaster/file_processor.rb +114 -25
- data/lib/buildmaster/java_manifest.rb +4 -0
- data/lib/buildmaster/{site.rb → site/site.rb} +11 -46
- data/lib/buildmaster/site/template/buildmaster.css +340 -0
- data/lib/buildmaster/site/template/buildmaster_template.xml +130 -0
- data/lib/buildmaster/site/template_builder.rb +276 -0
- data/lib/buildmaster/site_server.rb +33 -0
- data/lib/buildmaster/site_spec.rb +74 -22
- data/lib/buildmaster/site_tester.rb +14 -2
- data/lib/buildmaster/source_content.rb +11 -0
- data/lib/buildmaster/source_file_handler.rb +7 -2
- data/lib/buildmaster/svn_driver.rb +23 -11
- data/lib/buildmaster/template_exception.rb +8 -0
- data/lib/buildmaster/template_runner.rb +19 -86
- data/lib/buildmaster/templatelets.rb +23 -0
- data/lib/buildmaster/templatelets/attribute.rb +16 -0
- data/lib/buildmaster/templatelets/each.rb +43 -0
- data/lib/buildmaster/templatelets/href.rb +39 -0
- data/lib/buildmaster/templatelets/include.rb +30 -0
- data/lib/buildmaster/templatelets/link.rb +41 -0
- data/lib/buildmaster/templatelets/text.rb +14 -0
- data/lib/buildmaster/templatelets/when.rb +24 -0
- data/lib/buildmaster/tree_to_object.rb +76 -0
- data/lib/buildmaster/xtemplate.rb +10 -11
- data/test/buildmaster/manifest.mf +1 -1
- data/test/buildmaster/{content → site/content}/index.html +0 -0
- data/test/buildmaster/site/content/markdown.markdown +0 -0
- data/test/buildmaster/site/content/textile.textile +0 -0
- data/test/buildmaster/{tc_site.rb → site/tc_site.rb} +1 -1
- data/test/buildmaster/site/tc_template_builder.rb +128 -0
- data/test/buildmaster/tc_build_number_file.rb +31 -0
- data/test/buildmaster/tc_file_processor.rb +81 -14
- data/test/buildmaster/tc_site_spec.rb +26 -42
- data/test/buildmaster/tc_source_file_handler.rb +55 -0
- data/test/buildmaster/tc_template_runner.rb +42 -1
- data/test/buildmaster/tc_tree_to_object.rb +120 -0
- data/test/buildmaster/tc_xtemplate.rb +13 -233
- data/test/buildmaster/templatelets/common_templatelet_test.rb +27 -0
- data/test/buildmaster/templatelets/tc_attribute.rb +57 -0
- data/test/buildmaster/templatelets/tc_each.rb +69 -0
- data/test/buildmaster/templatelets/tc_href.rb +48 -0
- data/test/buildmaster/templatelets/tc_include.rb +34 -0
- data/test/buildmaster/templatelets/tc_link.rb +70 -0
- data/test/buildmaster/templatelets/tc_text.rb +36 -0
- data/test/buildmaster/templatelets/tc_when.rb +59 -0
- data/test/buildmaster/ts_site.rb +4 -0
- data/test/buildmaster/ts_templatelets.rb +10 -0
- data/test/tmp/output/index.html +8 -0
- data/test/tmp/output/markdown.html +8 -0
- data/test/tmp/output/textile.html +8 -0
- data/test/ts_buildmaster.rb +15 -3
- metadata +102 -53
@@ -0,0 +1,14 @@
|
|
1
|
+
module BuildMaster
|
2
|
+
class Text
|
3
|
+
def initialize(properties)
|
4
|
+
@properties = properties
|
5
|
+
end
|
6
|
+
|
7
|
+
def process(target, template, source)
|
8
|
+
property = template.attribute_value!('property')
|
9
|
+
value = @properties[property]
|
10
|
+
raise TemplateException, "property value for '#{property}' not found" unless value
|
11
|
+
target.add(REXML::Text.new(value))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..')
|
2
|
+
|
3
|
+
require 'template_runner'
|
4
|
+
|
5
|
+
module BuildMaster
|
6
|
+
class When
|
7
|
+
def initialize(site_spec, expression_evaluator)
|
8
|
+
@site_spec = site_spec
|
9
|
+
@evaluator = expression_evaluator
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(target, template, source)
|
13
|
+
eval = template.attribute_value!('test')
|
14
|
+
if (not @evaluator.respond_to?(eval))
|
15
|
+
raise TemplateException, "#{@evaluator.class} cannot evaluate expression #{eval}"
|
16
|
+
end
|
17
|
+
if (@evaluator.send(eval, source.path))
|
18
|
+
runner = TemplateRunner.new(target, template, source)
|
19
|
+
runner.templatelets = @site_spec.load_templatelets
|
20
|
+
runner.process
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module BuildMaster
|
4
|
+
|
5
|
+
class PropertyMatchError < StandardError
|
6
|
+
|
7
|
+
attr_reader :property, :type, :target
|
8
|
+
|
9
|
+
def initialize(property, type, target)
|
10
|
+
@property = property
|
11
|
+
@type = type
|
12
|
+
@target = target
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TreeToObject
|
17
|
+
def initialize(tree, object)
|
18
|
+
@tree = tree
|
19
|
+
@object = object
|
20
|
+
end
|
21
|
+
|
22
|
+
def TreeToObject.from_yaml(content, object)
|
23
|
+
tree = {}
|
24
|
+
if (content.chomp!)
|
25
|
+
tree = YAML.load(content)
|
26
|
+
if (not tree.kind_of?(Hash))
|
27
|
+
raise "Format error for content:\n#{content}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return TreeToObject.new(tree, object).convert
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def convert
|
35
|
+
@tree.each_pair do |key, value|
|
36
|
+
if (value.kind_of?(Array))
|
37
|
+
convert_array(key, value)
|
38
|
+
elsif (value.kind_of?(Hash))
|
39
|
+
convert_hash(key, value)
|
40
|
+
else
|
41
|
+
convert_property(key, value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
return @object
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def convert_hash(field, hash)
|
49
|
+
return TreeToObject.new(hash, property("#{field}", field, 'sub property')).convert
|
50
|
+
end
|
51
|
+
|
52
|
+
def convert_array(field, array)
|
53
|
+
array.each do |item|
|
54
|
+
TreeToObject.new(item, property("add_to_#{field}", field, 'array')).convert
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def convert_property(field, value)
|
59
|
+
method = "#{field}="
|
60
|
+
check_property(method, field, 'string')
|
61
|
+
@object.send("#{field}=", value)
|
62
|
+
end
|
63
|
+
|
64
|
+
def property(method, field, type)
|
65
|
+
check_property(method, field, type)
|
66
|
+
@object.send(method)
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_property(method, field, type)
|
70
|
+
if not @object.respond_to? method
|
71
|
+
message = "#{field} does not exist as #{type} in #{@object}"
|
72
|
+
raise PropertyMatchError.new(field, type, @object), message, caller
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -4,25 +4,24 @@ require 'rexml/document'
|
|
4
4
|
require 'template_runner'
|
5
5
|
|
6
6
|
module BuildMaster
|
7
|
-
class TemplateException < Exception
|
8
|
-
end
|
9
|
-
|
10
7
|
class XTemplate
|
11
|
-
def initialize(template_file)
|
8
|
+
def initialize(template_file, templatelets = Hash.new)
|
12
9
|
@template = REXML::Document.new(template_file)
|
10
|
+
@templatelets = templatelets
|
13
11
|
end
|
14
12
|
|
15
|
-
def process(
|
16
|
-
|
17
|
-
output_xml = process_xml(content_xml, &evaluator)
|
13
|
+
def process(source, &evaluator)
|
14
|
+
output_xml = process_xml(source, &evaluator)
|
18
15
|
return output_xml
|
19
16
|
end
|
20
17
|
|
21
18
|
private
|
22
|
-
def process_xml(
|
23
|
-
|
24
|
-
TemplateRunner.new(
|
25
|
-
|
19
|
+
def process_xml(source, &evaluator)
|
20
|
+
output_xml = REXML::Document.new
|
21
|
+
runner = TemplateRunner.new(output_xml, @template, source, &evaluator)
|
22
|
+
runner.templatelets = @templatelets
|
23
|
+
runner.process
|
24
|
+
return output_xml
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,128 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..', 'lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rexml/xpath'
|
5
|
+
require 'buildmaster/site/template_builder'
|
6
|
+
require 'buildmaster/tree_to_object'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module BuildMaster
|
10
|
+
class TemplateBuilderTest < Test::Unit::TestCase
|
11
|
+
def test_should_generate_template_document
|
12
|
+
builder = TemplateBuilder.new
|
13
|
+
document = builder.generate
|
14
|
+
assert_equal('include', REXML::XPath.first(document, '/html/head/title/*').name)
|
15
|
+
assert_css_path(document, 'buildmaster.css')
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_update_header_with_title_and_css
|
19
|
+
builder = TemplateBuilder.new
|
20
|
+
builder.title_header = 'Title Header - '
|
21
|
+
builder.css_path = 'mycss.css'
|
22
|
+
document = builder.generate
|
23
|
+
first_child = REXML::XPath.first(document, '/html/head/title').children[0]
|
24
|
+
assert_equal(REXML::Text, first_child.class)
|
25
|
+
assert_equal('Title Header - ', first_child.value)
|
26
|
+
assert_css_path(document, 'mycss.css')
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_have_logo_defaults
|
30
|
+
builder = TemplateBuilder.new
|
31
|
+
assert_equal(nil, builder.logo.path)
|
32
|
+
assert_equal('index.html', builder.logo.link)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_should_generate_logo_and_link
|
36
|
+
builder = TemplateBuilder.new
|
37
|
+
builder.logo.path = 'gif/logo.gif'
|
38
|
+
builder.logo.link = 'main.html'
|
39
|
+
document = builder.generate
|
40
|
+
header = assert_first(document, '/html/body/div[@class="header"]')
|
41
|
+
anchor_href = assert_first(header, 'a/template:href')
|
42
|
+
assert_equal('main.html', anchor_href.attributes['url'])
|
43
|
+
img_href = assert_first(header, 'a/img/template:href')
|
44
|
+
assert_equal('gif/logo.gif', img_href.attributes['url'])
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_build_left_menu
|
48
|
+
builder = TemplateBuilder.new
|
49
|
+
builder.left_bottom_logo.path='http://www.example.com/logo.gif'
|
50
|
+
group = builder.menu_group('Software')
|
51
|
+
group.menu_item('Download', 'download.html')
|
52
|
+
group.menu_item('License', 'license.html')
|
53
|
+
group.more='more.html'
|
54
|
+
group = builder.menu_group('Documentation', 'doc/index.html')
|
55
|
+
group.menu_item('Getting Started', 'doc/getting-started')
|
56
|
+
document = builder.generate
|
57
|
+
groups = REXML::XPath.match(document, '/html/body/div[@class="left"]/div[@class="MenuGroup"]')
|
58
|
+
assert_equal(2, groups.size)
|
59
|
+
first_group = groups[0]
|
60
|
+
header = assert_first(first_group, 'h1')
|
61
|
+
assert_equal('Software', header.text)
|
62
|
+
items = REXML::XPath.match(first_group, 'ul/li')
|
63
|
+
assert_equal(3, items.size)
|
64
|
+
first_item = items[0]
|
65
|
+
anchor = assert_first(first_item, 'template:link')
|
66
|
+
assert_equal('download.html', anchor.attributes['href'])
|
67
|
+
assert_equal('Download', anchor.text)
|
68
|
+
more = items[2]
|
69
|
+
assert_equal('More', more.attributes['class'])
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_should_have_releases_info
|
73
|
+
builder = TemplateBuilder.new
|
74
|
+
releases = builder.releases
|
75
|
+
releases.stable_version = '0.6'
|
76
|
+
releases.pre_release_version = '0.7'
|
77
|
+
releases.snap_shot_version = 'n/a'
|
78
|
+
releases.download_link = 'download.html'
|
79
|
+
releases.versioning_link = 'versioning.html'
|
80
|
+
builder.generate
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_should_read_from_yaml
|
84
|
+
content = <<CONTENT
|
85
|
+
title_header: BuilderMaster -
|
86
|
+
logo:
|
87
|
+
path: logo.gif
|
88
|
+
link: index.html
|
89
|
+
menu_groups:
|
90
|
+
- title: Software
|
91
|
+
menu_items:
|
92
|
+
- title: Download
|
93
|
+
link: download.html
|
94
|
+
- title: Source Repository
|
95
|
+
link: http://rubyforge.org/scm/?group_id=1680
|
96
|
+
- title: Project License
|
97
|
+
link: license.html
|
98
|
+
- title: Documentation...
|
99
|
+
link: document.html
|
100
|
+
menu_items:
|
101
|
+
- title: Getting Started
|
102
|
+
link: getting-started.html
|
103
|
+
- title: Project Release
|
104
|
+
link: release-project.html
|
105
|
+
- title: Site Building
|
106
|
+
link: build-site.html
|
107
|
+
more: doc/index.html
|
108
|
+
CONTENT
|
109
|
+
builder = TreeToObject.from_yaml(content, TemplateBuilder.new)
|
110
|
+
assert_equal('BuilderMaster -', builder.title_header)
|
111
|
+
assert_equal('logo.gif', builder.logo.path)
|
112
|
+
assert_equal('index.html', builder.logo.link)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def assert_css_path(document, expected)
|
117
|
+
href = REXML::XPath.first(document, '/html/head/link/*')
|
118
|
+
assert_equal('href', href.name)
|
119
|
+
assert_equal(expected, href.attributes['url'])
|
120
|
+
end
|
121
|
+
|
122
|
+
def assert_first(xml_model, xpath)
|
123
|
+
first = REXML::XPath.first(xml_model, xpath)
|
124
|
+
assert_not_nil(first)
|
125
|
+
return first
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
require 'buildmaster/build_number_file'
|
6
|
+
|
7
|
+
module BuildMaster
|
8
|
+
class BuildNumberFileTest < Test::Unit::TestCase
|
9
|
+
def test_load_file
|
10
|
+
path = tmp_file(2)
|
11
|
+
build_number = BuildNumberFile.new(path)
|
12
|
+
assert_equal(2, build_number.number)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_increase_build
|
16
|
+
path = tmp_file(3)
|
17
|
+
build_number = BuildNumberFile.new(path)
|
18
|
+
build_number.increase_build
|
19
|
+
assert_equal(4, build_number.number)
|
20
|
+
reloaded = BuildNumberFile.new(path)
|
21
|
+
assert_equal(4, reloaded.number)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def tmp_file(build_number)
|
26
|
+
path = File.join(File.dirname(__FILE__), '..', '..', 'tmp', 'buildnumber')
|
27
|
+
File.open(path, "w") {|file| file.puts build_number}
|
28
|
+
return path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,32 +2,99 @@ $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib")
|
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'rexml/document'
|
5
|
+
require 'pathname'
|
5
6
|
require 'buildmaster/file_processor'
|
6
7
|
require 'buildmaster/xtemplate'
|
7
8
|
|
8
9
|
module BuildMaster
|
9
10
|
class FileProcessorTest < Test::Unit::TestCase
|
10
|
-
def
|
11
|
-
|
11
|
+
def test_should_know_content_and_target
|
12
|
+
template = XTemplate.new(<<CONTENT
|
12
13
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
13
14
|
xmlns:template="http://buildmaster.rubyforge.org/xtemplate/1.0">
|
14
|
-
<p>
|
15
|
-
<template:attribute name="class" eval="method_one"/>
|
16
|
-
</p>
|
17
15
|
</html>
|
18
16
|
CONTENT
|
19
17
|
)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
site_spec = SiteSpec.new
|
19
|
+
current_dir = File.dirname(__FILE__)
|
20
|
+
site_spec.content_dir = File.join(current_dir, "content")
|
21
|
+
site_spec.output_dir = File.join(current_dir, "output")
|
22
|
+
processor = FileProcessor.new(template, File.join(current_dir, 'content', 'index.html'), site_spec)
|
23
|
+
assert_equal(File.join(current_dir, 'content', 'index.html'), processor.path_to_content_file)
|
24
|
+
assert_equal(true, processor.is_html?)
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def test_should_know_html_target
|
28
|
+
template = XTemplate.new(<<CONTENT
|
29
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
30
|
+
xmlns:template="http://buildmaster.rubyforge.org/xtemplate/1.0">
|
31
|
+
</html>
|
32
|
+
CONTENT
|
33
|
+
)
|
34
|
+
site_spec = SiteSpec.new
|
35
|
+
current_dir = File.dirname(__FILE__)
|
36
|
+
site_spec.content_dir = File.join(current_dir, "content")
|
37
|
+
site_spec.output_dir = File.join(current_dir, "output")
|
38
|
+
processor = FileProcessor.new(template, File.join(current_dir, 'content', 'index.gif'), site_spec)
|
39
|
+
assert_equal(File.join(current_dir, 'content', 'index.gif'), processor.path_to_content_file)
|
40
|
+
assert_equal(false, processor.is_html?)
|
41
|
+
assert_equal(nil, processor.generate_document)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_load_from_target
|
45
|
+
template_content = <<CONTENT
|
46
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
47
|
+
xmlns:template="http://buildmaster.rubyforge.org/xtemplate/1.0">
|
48
|
+
</html>
|
49
|
+
CONTENT
|
50
|
+
site_spec = SiteSpec.new
|
51
|
+
current_dir = File.dirname(__FILE__)
|
52
|
+
site_spec.template = template_content
|
53
|
+
site_spec.content_dir = File.join(current_dir, 'site', 'content')
|
54
|
+
site_spec.output_dir = File.join(current_dir, "output")
|
55
|
+
processor = FileProcessor.for_request_path('/index.html', site_spec)
|
56
|
+
assert_equal(File.join(site_spec.content_dir, 'index.html'), processor.path_to_content_file)
|
57
|
+
processor = FileProcessor.for_request_path('/index.gif', site_spec)
|
58
|
+
assert_equal(File.join(site_spec.content_dir, 'index.gif'), processor.path_to_content_file)
|
59
|
+
processor = FileProcessor.for_request_path('/textile.html', site_spec)
|
60
|
+
assert_equal(File.join(site_spec.content_dir, 'textile.textile'), processor.path_to_content_file)
|
61
|
+
processor = FileProcessor.for_request_path('/markdown.html', site_spec)
|
62
|
+
assert_equal(File.join(site_spec.content_dir, 'markdown.markdown'), processor.path_to_content_file)
|
63
|
+
end
|
64
|
+
|
65
|
+
def output_dir
|
66
|
+
return 'tmp'
|
67
|
+
end
|
68
|
+
|
69
|
+
def relative_to_root(path)
|
70
|
+
return Pathname.new('tmp')
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_mark_down_support
|
74
|
+
template_content = <<CONTENT
|
75
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
76
|
+
xmlns:template="http://buildmaster.rubyforge.org/xtemplate/1.0">
|
77
|
+
<head><title><template:include elements="/html/head/title/text()"/></title></head>
|
78
|
+
<body>
|
79
|
+
<template:include elements="/html/body/*"/>
|
80
|
+
</body>
|
81
|
+
</html>
|
82
|
+
CONTENT
|
83
|
+
hash = {'include' => Include.new(self)}
|
84
|
+
template = XTemplate.new(template_content, hash)
|
85
|
+
|
86
|
+
processor = FileProcessor.new(template, "content_path", self)
|
87
|
+
markdown_content = <<CONTENT
|
88
|
+
--------------------------------------------
|
89
|
+
Title Here
|
90
|
+
--------------------------------------------
|
91
|
+
Header
|
92
|
+
=====================
|
93
|
+
CONTENT
|
94
|
+
document = processor.process_markdown(markdown_content)
|
95
|
+
document = REXML::Document.new(document.to_s)
|
96
|
+
assert_equal('Title Here', REXML::XPath.first(document, '/html/head/title').text)
|
97
|
+
assert_equal('Header', REXML::XPath.first(document, '/html/body/h1').text)
|
31
98
|
end
|
32
99
|
|
33
100
|
end
|