ape 1.0.0 → 1.5.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/LICENSE +1 -1
- data/README +22 -8
- data/Rakefile +66 -0
- data/bin/ape_server +3 -3
- data/lib/ape.rb +131 -937
- data/lib/ape/atomURI.rb +1 -1
- data/lib/ape/authent.rb +11 -17
- data/lib/ape/categories.rb +8 -7
- data/lib/ape/collection.rb +3 -7
- data/lib/ape/crumbs.rb +1 -1
- data/lib/ape/entry.rb +1 -1
- data/lib/ape/escaper.rb +1 -1
- data/lib/ape/feed.rb +26 -14
- data/lib/ape/handler.rb +8 -3
- data/lib/ape/html.rb +1 -1
- data/lib/ape/invoker.rb +1 -1
- data/lib/ape/invokers/deleter.rb +1 -1
- data/lib/ape/invokers/getter.rb +1 -1
- data/lib/ape/invokers/poster.rb +1 -1
- data/lib/ape/invokers/putter.rb +1 -1
- data/lib/ape/names.rb +1 -1
- data/lib/ape/print_writer.rb +4 -6
- data/lib/ape/reporter.rb +156 -0
- data/lib/ape/reporters/atom_reporter.rb +51 -0
- data/lib/ape/reporters/atom_template.eruby +38 -0
- data/lib/ape/reporters/html_reporter.rb +53 -0
- data/lib/ape/reporters/html_template.eruby +62 -0
- data/lib/ape/reporters/text_reporter.rb +37 -0
- data/lib/ape/samples.rb +30 -51
- data/lib/ape/server.rb +16 -4
- data/lib/ape/service.rb +1 -1
- data/lib/ape/util.rb +67 -0
- data/lib/ape/validator.rb +85 -57
- data/lib/ape/validator_dsl.rb +40 -0
- data/lib/ape/validators/entry_posts_validator.rb +226 -0
- data/lib/ape/validators/media_linkage_validator.rb +78 -0
- data/lib/ape/validators/media_posts_validator.rb +104 -0
- data/lib/ape/validators/sanitization_validator.rb +57 -0
- data/lib/ape/validators/schema_validator.rb +57 -0
- data/lib/ape/validators/service_document_validator.rb +64 -0
- data/lib/ape/validators/sorting_validator.rb +87 -0
- data/lib/ape/version.rb +1 -1
- data/{lib/ape/samples → samples}/atom_schema.txt +0 -0
- data/{lib/ape/samples → samples}/basic_entry.eruby +4 -4
- data/{lib/ape/samples → samples}/categories_schema.txt +0 -0
- data/{lib/ape/samples → samples}/mini_entry.eruby +0 -0
- data/{lib/ape/samples → samples}/service_schema.txt +0 -0
- data/{lib/ape/samples → samples}/unclean_xhtml_entry.eruby +0 -0
- data/test/test_helper.rb +33 -1
- data/test/unit/ape_test.rb +92 -0
- data/test/unit/authent_test.rb +2 -2
- data/test/unit/reporter_test.rb +102 -0
- data/test/unit/samples_test.rb +2 -2
- data/test/unit/validators_test.rb +50 -0
- data/{lib/ape/layout → web}/ape.css +5 -1
- data/{lib/ape/layout → web}/ape_logo.png +0 -0
- data/{lib/ape/layout → web}/index.html +2 -5
- data/{lib/ape/layout → web}/info.png +0 -0
- metadata +108 -56
- data/CHANGELOG +0 -1
- data/Manifest +0 -45
- data/ape.gemspec +0 -57
- data/scripts/go.rb +0 -29
data/lib/ape/atomURI.rb
CHANGED
data/lib/ape/authent.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
|
2
2
|
# Use is subject to license terms - see file "LICENSE"
|
3
3
|
|
4
4
|
module Ape
|
5
5
|
class AuthenticationError < StandardError ; end
|
6
6
|
|
7
|
-
class Authent
|
7
|
+
class Authent
|
8
|
+
require File.dirname(__FILE__) + '/util.rb'
|
9
|
+
include Ape::Util
|
10
|
+
|
8
11
|
def initialize(username, password, scheme=nil)
|
9
12
|
@username = username
|
10
|
-
@password = password
|
11
|
-
@auth_plugin = nil
|
13
|
+
@password = password
|
12
14
|
end
|
13
15
|
|
14
16
|
def add_to(req, authentication = nil)
|
@@ -17,7 +19,7 @@ module Ape
|
|
17
19
|
if authentication.strip.downcase.include? 'basic'
|
18
20
|
req.basic_auth @username, @password
|
19
21
|
else
|
20
|
-
@auth_plugin
|
22
|
+
@auth_plugin ||= load_plugin(authentication)
|
21
23
|
@auth_plugin.add_credentials(req, authentication, @username, @password)
|
22
24
|
end
|
23
25
|
else
|
@@ -25,18 +27,10 @@ module Ape
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
plugin_class = file.gsub(/(.+\/auth\/)(.+)(.rb)/, '\2').gsub(/(^|_)(.)/) { $2.upcase }
|
32
|
-
|
33
|
-
if (authentication.strip.downcase.include?(plugin_name))
|
34
|
-
return eval("#{plugin_class}.new", binding, __FILE__, __LINE__)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
raise AuthenticationError, "Unknown authentication method: #{authentication}"
|
30
|
+
def load_plugin(authentication)
|
31
|
+
plugin = Authent.resolve_plugin(authentication, 'auth', 'credentials', true)
|
32
|
+
plugin || (raise AuthenticationError, "Unknown authentication method: #{authentication}")
|
38
33
|
end
|
39
34
|
end
|
35
|
+
Dir[File.dirname(__FILE__) + '/auth/*.rb'].each { |l| require l }
|
40
36
|
end
|
41
|
-
|
42
|
-
Dir[File.dirname(__FILE__) + '/auth/*.rb'].each { |l| require l }
|
data/lib/ape/categories.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
|
1
2
|
# Copyright © 2006 Sun Microsystems, Inc. All rights reserved
|
2
3
|
# Use is subject to license terms - see file "LICENSE"
|
3
4
|
|
@@ -8,7 +9,7 @@ module Ape
|
|
8
9
|
class Categories
|
9
10
|
attr_reader :fixed
|
10
11
|
|
11
|
-
def Categories.from_collection(collection, authent,
|
12
|
+
def Categories.from_collection(collection, authent, reporter = nil)
|
12
13
|
|
13
14
|
# "catses" because if cats is short for categories, then catses
|
14
15
|
# suggests multiple <app:categories> elements
|
@@ -18,17 +19,17 @@ module Ape
|
|
18
19
|
if cats.attribute(:href)
|
19
20
|
getter = Getter.new(cats.attribute(:href).value, authent)
|
20
21
|
if getter.last_error # wonky URI
|
21
|
-
|
22
|
+
reporter.error(self, getter.last_error) if reporter
|
22
23
|
nil
|
23
24
|
end
|
24
25
|
|
25
26
|
if !getter.get('application/atomcat+xml')
|
26
|
-
|
27
|
-
"'#{cats.attribute(:href).value}': getter.last_error" if
|
27
|
+
reporter.error(self, "Can't fetch categories doc " +
|
28
|
+
"'#{cats.attribute(:href).value}': getter.last_error") if reporter
|
28
29
|
nil
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
+
reporter.warning(self, getter.last_error) if reporter && getter.last_error
|
32
33
|
REXML::Document.new(getter.body).root
|
33
34
|
else
|
34
35
|
# no href attribute
|
@@ -46,7 +47,7 @@ module Ape
|
|
46
47
|
# at least one with fixed="no", also add a syntho-cat that we make up.
|
47
48
|
# Return the list of categories that we added.
|
48
49
|
#
|
49
|
-
def Categories.add_cats(entry, collection, authent,
|
50
|
+
def Categories.add_cats(entry, collection, authent, reporter = nil)
|
50
51
|
|
51
52
|
added = []
|
52
53
|
c = from_collection(collection, authent)
|
@@ -68,7 +69,7 @@ module Ape
|
|
68
69
|
# for each <app:category> take the first one whose attribute "term" is not empty
|
69
70
|
cat_list.each do |cat|
|
70
71
|
if cat.attributes['term'].empty?
|
71
|
-
|
72
|
+
reporter.warning(self, 'A mangled category is present in your categories list') if reporter
|
72
73
|
else
|
73
74
|
scheme = cat.attributes['scheme']
|
74
75
|
if !scheme
|
data/lib/ape/collection.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
|
2
2
|
# Use is subject to license terms - see file "LICENSE"
|
3
3
|
require 'rexml/xpath'
|
4
4
|
|
@@ -12,9 +12,7 @@ module Ape
|
|
12
12
|
@title = REXML::XPath.first(input, './atom:title', Names::XmlNamespaces)
|
13
13
|
|
14
14
|
# sigh, RNC validation *should* take care of this
|
15
|
-
unless @title
|
16
|
-
raise(SyntaxError, "Collection is missing required 'atom:title'")
|
17
|
-
end
|
15
|
+
raise(SyntaxError, "Collection is missing required 'atom:title'") unless @title
|
18
16
|
@title = @title.texts.join
|
19
17
|
|
20
18
|
if doc_uri
|
@@ -30,9 +28,7 @@ module Ape
|
|
30
28
|
@accept << a.texts.join
|
31
29
|
end
|
32
30
|
|
33
|
-
if @accept.empty?
|
34
|
-
@accept = [ Names::AtomEntryMediaType ]
|
35
|
-
end
|
31
|
+
@accept = [ Names::AtomEntryMediaType ] if @accept.empty?
|
36
32
|
end
|
37
33
|
|
38
34
|
def to_s
|
data/lib/ape/crumbs.rb
CHANGED
data/lib/ape/entry.rb
CHANGED
data/lib/ape/escaper.rb
CHANGED
data/lib/ape/feed.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
|
2
2
|
# Use is subject to license terms - see file "LICENSE"
|
3
|
-
|
3
|
+
require File.dirname(__FILE__) + '/util.rb'
|
4
4
|
require 'rexml/document'
|
5
5
|
require 'rexml/xpath'
|
6
6
|
|
7
7
|
module Ape
|
8
8
|
class Feed
|
9
|
+
extend Ape::Util
|
10
|
+
|
11
|
+
def Feed.reporter
|
12
|
+
@@reporter
|
13
|
+
end
|
14
|
+
|
15
|
+
def Feed.reporter=(reporter)
|
16
|
+
@@reporter = reporter
|
17
|
+
end
|
18
|
+
|
9
19
|
# Load up a collection feed from its URI. Return an array of <entry> objects.
|
10
20
|
# follow <link rel="next" /> pointers as required to get the whole
|
11
21
|
# collection
|
12
|
-
def Feed.read(uri, name,
|
13
|
-
|
22
|
+
def Feed.read(uri, name, reporter, report = true)
|
23
|
+
Feed.reporter = reporter
|
14
24
|
entries = []
|
15
25
|
uris = []
|
16
26
|
next_page = uri
|
@@ -20,11 +30,13 @@ module Ape
|
|
20
30
|
|
21
31
|
label = "Page #{page_num} of #{name}"
|
22
32
|
uris << next_page
|
23
|
-
page =
|
33
|
+
page = check_resource(next_page, label, Names::AtomMediaType, report)
|
24
34
|
break unless page
|
25
35
|
|
26
36
|
# * Validate it
|
27
|
-
Validator.validate(Samples.atom_RNC,
|
37
|
+
Validator.instance(:schema, reporter).validate(:schema => Samples.atom_RNC,
|
38
|
+
:title => label, :doc => page) if report
|
39
|
+
#Validator.validate(Samples.atom_RNC, page.body, label, ape.reporter) if report
|
28
40
|
|
29
41
|
# XML-parse the feed
|
30
42
|
error = "not well-formed"
|
@@ -36,19 +48,19 @@ module Ape
|
|
36
48
|
feed = nil
|
37
49
|
end
|
38
50
|
if feed == nil
|
39
|
-
|
51
|
+
reporter.error(self, "Can't parse #{label} at #{next_page}, Parser said: #{$!}") if report
|
40
52
|
break
|
41
53
|
end
|
42
54
|
|
43
55
|
feed = feed.root
|
44
56
|
if feed == nil
|
45
|
-
|
57
|
+
reporter.warning(self, "#{label} is empty.")
|
46
58
|
break
|
47
59
|
end
|
48
60
|
|
49
61
|
page_entries = REXML::XPath.match(feed, "./atom:entry", Names::XmlNamespaces)
|
50
62
|
if page_entries.empty? && report
|
51
|
-
|
63
|
+
reporter.info(self, "#{label} has no entries.")
|
52
64
|
end
|
53
65
|
|
54
66
|
entries += page_entries.map { |e| Entry.new(e, next_page)}
|
@@ -59,7 +71,7 @@ module Ape
|
|
59
71
|
base = AtomURI.new(next_page)
|
60
72
|
next_link = base.absolutize(next_link, feed)
|
61
73
|
if uris.index(next_link)
|
62
|
-
|
74
|
+
reporter.error(self, "Collection contains circular 'next' linkage: #{next_link}") if report
|
63
75
|
break
|
64
76
|
end
|
65
77
|
page_num += 1
|
@@ -68,7 +80,7 @@ module Ape
|
|
68
80
|
end
|
69
81
|
|
70
82
|
if report && next_page
|
71
|
-
|
83
|
+
reporter.warning(self, "Stopped reading collection after #{page_num} pages.")
|
72
84
|
end
|
73
85
|
|
74
86
|
# all done unless we're error-checking
|
@@ -98,17 +110,17 @@ module Ape
|
|
98
110
|
end
|
99
111
|
if error
|
100
112
|
title = e.child_content "title"
|
101
|
-
|
113
|
+
reporter.error(self, "In entry with title '#{title}', #{error}.")
|
102
114
|
clean = false
|
103
115
|
end
|
104
116
|
end
|
105
117
|
end
|
106
118
|
if with_app_date < entries.size
|
107
|
-
|
119
|
+
reporter.error(self, "#{entries.size - with_app_date} of #{entries.size} entries in #{name} lack app:edited elements.")
|
108
120
|
clean = false
|
109
121
|
end
|
110
122
|
|
111
|
-
|
123
|
+
reporter.success(self, "#{name} has correct app:edited value order.") if clean
|
112
124
|
|
113
125
|
entries
|
114
126
|
end
|
data/lib/ape/handler.rb
CHANGED
@@ -3,7 +3,14 @@ require 'mongrel'
|
|
3
3
|
require 'ape'
|
4
4
|
|
5
5
|
module Ape
|
6
|
+
|
7
|
+
# Implements the APE application handler for processing
|
8
|
+
# and responding to requests. See process for more detail.
|
6
9
|
class Handler < Mongrel::HttpHandler
|
10
|
+
|
11
|
+
# Called by Mongrel with Mongrel::HttpRequest and
|
12
|
+
# Mongrel::HttpResponse objects. Creates an Ape
|
13
|
+
# instance for the request and responds with its report.
|
7
14
|
def process(request, response)
|
8
15
|
cgi = Mongrel::CGIWrapper.new(request, response)
|
9
16
|
|
@@ -11,8 +18,6 @@ module Ape
|
|
11
18
|
user = cgi['username'].strip
|
12
19
|
pass = cgi['password'].strip
|
13
20
|
|
14
|
-
# invoke_ape uri, user, pass, request, response
|
15
|
-
|
16
21
|
if uri.empty?
|
17
22
|
response.start(200, true) do |header, body|
|
18
23
|
header['Content-Type'] = 'text/plain'
|
@@ -22,7 +27,7 @@ module Ape
|
|
22
27
|
end
|
23
28
|
|
24
29
|
format = request.params['HTTP_ACCEPT'] == 'text/plain' ? 'text' : 'html'
|
25
|
-
ape = Ape.new({ :crumbs => true, :output => format })
|
30
|
+
ape = Ape.new({ :crumbs => true, :output => format, :server => true })
|
26
31
|
(user && pass) ? ape.check(uri, user, pass) : ape.check(uri)
|
27
32
|
|
28
33
|
response.start(200, true) do |header, body|
|
data/lib/ape/html.rb
CHANGED
data/lib/ape/invoker.rb
CHANGED
data/lib/ape/invokers/deleter.rb
CHANGED
data/lib/ape/invokers/getter.rb
CHANGED
data/lib/ape/invokers/poster.rb
CHANGED
data/lib/ape/invokers/putter.rb
CHANGED
data/lib/ape/names.rb
CHANGED
data/lib/ape/print_writer.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
|
4
|
-
# this is a wrapper for the weird derived-from-PrintWriter class that comes
|
5
|
-
# out of HttpResponse.getWriter
|
6
|
-
|
1
|
+
# Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
|
2
|
+
# See the included LICENSE[link:/files/LICENSE.html] file for details.
|
7
3
|
module Ape
|
4
|
+
# this is a wrapper for the weird derived-from-PrintWriter class that comes
|
5
|
+
# out of HttpResponse.getWriter
|
8
6
|
class Printwriter
|
9
7
|
def initialize(java_writer)
|
10
8
|
@w = java_writer
|
data/lib/ape/reporter.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
module Ape
|
2
|
+
require 'erubis'
|
3
|
+
class Reporter < Erubis::Context
|
4
|
+
require File.dirname(__FILE__) + '/util.rb'
|
5
|
+
include Ape::Util
|
6
|
+
|
7
|
+
attr_accessor :header, :footer, :debug, :dialogs, :diarefs, :dianum, :server
|
8
|
+
|
9
|
+
def steps
|
10
|
+
@steps ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.instance(key, opts = {})
|
14
|
+
reporter = resolve_plugin(key, 'reporters', 'reporter')
|
15
|
+
raise StandardError, "Unknown reporter: #{key}, outputs supported: #{supported_outputs}" unless reporter
|
16
|
+
reporter.debug = opts[:debug] || false
|
17
|
+
reporter.server = opts[:server] || false
|
18
|
+
reporter.dialogs = {}
|
19
|
+
reporter.diarefs = {}
|
20
|
+
reporter.dianum = 1
|
21
|
+
reporter
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.supported_outputs
|
25
|
+
Dir[File.join(File.dirname(__FILE__), 'reporters/*.rb'),
|
26
|
+
File.join(Ape.home, 'reporters/*.rb')].map { |file|
|
27
|
+
file.gsub(/(.+\/reporters\/)(.+)(_reporter.rb)/, '\2').gsub(/_/, '')
|
28
|
+
}.sort.join(", ").downcase
|
29
|
+
end
|
30
|
+
=begin
|
31
|
+
This method saves the messages that the validators send
|
32
|
+
=== Parameters
|
33
|
+
* args[0] must be allways a reference to the validator.
|
34
|
+
* args[1] should be the severity of the message, but it could be an array if several steps are recorded at once.
|
35
|
+
* args[2] is the message to show.
|
36
|
+
* args[3] is the message group key if it exits
|
37
|
+
=end
|
38
|
+
def add(*args)
|
39
|
+
if (args.length == 2 && args[1].kind_of?(Array))
|
40
|
+
steps << args[1]
|
41
|
+
elsif (args.length == 3)
|
42
|
+
steps << {:severity => args[1], :message => args[2]}
|
43
|
+
else
|
44
|
+
steps << {:severity => :debug, :message => args[3]}
|
45
|
+
show_crumbs(args[3]) if debug
|
46
|
+
steps << {:severity => args[1], :message => args[2], :key => args[3]}
|
47
|
+
end
|
48
|
+
puts "#{steps[-1][:severity].to_s.upcase}: #{steps[-1][:message]}" if debug && !steps[-1].kind_of?(Array)
|
49
|
+
end
|
50
|
+
|
51
|
+
def security_warning(validator)
|
52
|
+
unless (@sec_warning_writed)
|
53
|
+
warning(validator, "Sending authentication information over an open channel is not a good security practice.", name)
|
54
|
+
@sec_warning_writed = true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def warning(validator, message, crumb_key=nil)
|
59
|
+
unless crumb_key
|
60
|
+
add(validator, :warning, message)
|
61
|
+
else
|
62
|
+
add(validator, :warning, message, crumb_key)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def error(validator, message, crumb_key=nil)
|
67
|
+
unless crumb_key
|
68
|
+
add(validator, :error, message)
|
69
|
+
else
|
70
|
+
add(validator, :error, message, crumb_key)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def success(validator, message, crumb_key=nil)
|
75
|
+
unless crumb_key
|
76
|
+
add(validator, :success, message)
|
77
|
+
else
|
78
|
+
add(validator, :success, message, crumb_key)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def info(validator, message)
|
83
|
+
add(validator, :info, message)
|
84
|
+
end
|
85
|
+
|
86
|
+
def start_list(validator, message)
|
87
|
+
add(validator, [ message + ":" ])
|
88
|
+
end
|
89
|
+
|
90
|
+
def list_item(message)
|
91
|
+
steps[-1] << {:severity => :debug, :message => message}
|
92
|
+
end
|
93
|
+
|
94
|
+
def line(output=STDOUT)
|
95
|
+
printf(output, "%2d. ", @lnum ||= 1)
|
96
|
+
@lnum += 1
|
97
|
+
end
|
98
|
+
|
99
|
+
def save_dialog(name, actor)
|
100
|
+
dialogs[name] = actor.crumbs
|
101
|
+
end
|
102
|
+
|
103
|
+
def show_crumbs(key)
|
104
|
+
dialogs[key].each do |d|
|
105
|
+
puts "Dialog: #{d}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def show_message(crumb, tf)
|
110
|
+
message = crumb[1 .. -1]
|
111
|
+
message.gsub!(/^\s*"/, '')
|
112
|
+
message.gsub!(/"\s*$/, '')
|
113
|
+
message.gsub!(/\\"/, '"')
|
114
|
+
message = Escaper.escape message
|
115
|
+
message.gsub!(/(\\r\\n|\\n|\\r)/, "\n<br/>")
|
116
|
+
message.gsub!(/\\t/, '    ')
|
117
|
+
"<div class=\"#{tf.to_s}\">#{message}</div>"
|
118
|
+
end
|
119
|
+
|
120
|
+
def successes
|
121
|
+
select(:success)
|
122
|
+
end
|
123
|
+
|
124
|
+
def infos
|
125
|
+
select(:info)
|
126
|
+
end
|
127
|
+
|
128
|
+
def warnings
|
129
|
+
select(:warning)
|
130
|
+
end
|
131
|
+
|
132
|
+
def errors
|
133
|
+
select(:error)
|
134
|
+
end
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
def select(option)
|
139
|
+
steps.select { |step|
|
140
|
+
unless step.kind_of?(Array)
|
141
|
+
step.values.include?(option)
|
142
|
+
else
|
143
|
+
!step[1..-1].select {|dialog| dialog.values.include?(option)}.empty?
|
144
|
+
end
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def evaluate_template(name)
|
149
|
+
template = Erubis::FastEruby.new(IO.read(
|
150
|
+
File.expand_path(File.join(File.dirname(__FILE__), name))))
|
151
|
+
template.evaluate(self)
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
Dir[File.dirname(__FILE__) + '/reporters/*.rb'].each { |l| require l }
|
156
|
+
end
|