ape 1.0.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|