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.
Files changed (63) hide show
  1. data/LICENSE +1 -1
  2. data/README +22 -8
  3. data/Rakefile +66 -0
  4. data/bin/ape_server +3 -3
  5. data/lib/ape.rb +131 -937
  6. data/lib/ape/atomURI.rb +1 -1
  7. data/lib/ape/authent.rb +11 -17
  8. data/lib/ape/categories.rb +8 -7
  9. data/lib/ape/collection.rb +3 -7
  10. data/lib/ape/crumbs.rb +1 -1
  11. data/lib/ape/entry.rb +1 -1
  12. data/lib/ape/escaper.rb +1 -1
  13. data/lib/ape/feed.rb +26 -14
  14. data/lib/ape/handler.rb +8 -3
  15. data/lib/ape/html.rb +1 -1
  16. data/lib/ape/invoker.rb +1 -1
  17. data/lib/ape/invokers/deleter.rb +1 -1
  18. data/lib/ape/invokers/getter.rb +1 -1
  19. data/lib/ape/invokers/poster.rb +1 -1
  20. data/lib/ape/invokers/putter.rb +1 -1
  21. data/lib/ape/names.rb +1 -1
  22. data/lib/ape/print_writer.rb +4 -6
  23. data/lib/ape/reporter.rb +156 -0
  24. data/lib/ape/reporters/atom_reporter.rb +51 -0
  25. data/lib/ape/reporters/atom_template.eruby +38 -0
  26. data/lib/ape/reporters/html_reporter.rb +53 -0
  27. data/lib/ape/reporters/html_template.eruby +62 -0
  28. data/lib/ape/reporters/text_reporter.rb +37 -0
  29. data/lib/ape/samples.rb +30 -51
  30. data/lib/ape/server.rb +16 -4
  31. data/lib/ape/service.rb +1 -1
  32. data/lib/ape/util.rb +67 -0
  33. data/lib/ape/validator.rb +85 -57
  34. data/lib/ape/validator_dsl.rb +40 -0
  35. data/lib/ape/validators/entry_posts_validator.rb +226 -0
  36. data/lib/ape/validators/media_linkage_validator.rb +78 -0
  37. data/lib/ape/validators/media_posts_validator.rb +104 -0
  38. data/lib/ape/validators/sanitization_validator.rb +57 -0
  39. data/lib/ape/validators/schema_validator.rb +57 -0
  40. data/lib/ape/validators/service_document_validator.rb +64 -0
  41. data/lib/ape/validators/sorting_validator.rb +87 -0
  42. data/lib/ape/version.rb +1 -1
  43. data/{lib/ape/samples → samples}/atom_schema.txt +0 -0
  44. data/{lib/ape/samples → samples}/basic_entry.eruby +4 -4
  45. data/{lib/ape/samples → samples}/categories_schema.txt +0 -0
  46. data/{lib/ape/samples → samples}/mini_entry.eruby +0 -0
  47. data/{lib/ape/samples → samples}/service_schema.txt +0 -0
  48. data/{lib/ape/samples → samples}/unclean_xhtml_entry.eruby +0 -0
  49. data/test/test_helper.rb +33 -1
  50. data/test/unit/ape_test.rb +92 -0
  51. data/test/unit/authent_test.rb +2 -2
  52. data/test/unit/reporter_test.rb +102 -0
  53. data/test/unit/samples_test.rb +2 -2
  54. data/test/unit/validators_test.rb +50 -0
  55. data/{lib/ape/layout → web}/ape.css +5 -1
  56. data/{lib/ape/layout → web}/ape_logo.png +0 -0
  57. data/{lib/ape/layout → web}/index.html +2 -5
  58. data/{lib/ape/layout → web}/info.png +0 -0
  59. metadata +108 -56
  60. data/CHANGELOG +0 -1
  61. data/Manifest +0 -45
  62. data/ape.gemspec +0 -57
  63. data/scripts/go.rb +0 -29
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'uri'
4
4
 
@@ -1,14 +1,16 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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 = resolve_plugin(authentication) unless @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 resolve_plugin(authentication)
29
- Dir.glob(File.join(File.dirname(__FILE__), 'auth/*.rb')).each do |file|
30
- plugin_name = file.gsub(/(.+\/auth\/)(.+)(_credentials.rb)/, '\2').gsub(/_/, '')
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 }
@@ -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, ape=nil)
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
- ape.error getter.last_error if ape
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
- ape.error "Can't fetch categories doc " +
27
- "'#{cats.attribute(:href).value}': getter.last_error" if ape
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
- ape.warning(getter.last_error) if ape && getter.last_error
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, ape=nil)
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
- ape.warning 'A mangled category is present in your categories list' if ape
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
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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
  # All the methods (getter/poster/putter/deleter) use the set_debug_output method
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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
  require 'rexml/document'
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  module Ape
4
4
  class Escaper
@@ -1,16 +1,26 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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, ape, report=true)
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 = ape.check_resource(next_page, label, Names::AtomMediaType, report)
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, page.body, label, ape) if report
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
- ape.error "Can't parse #{label} at #{next_page}, Parser said: #{$!}" if report
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
- ape.warning "#{label} is empty."
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
- ape.info "#{label} has no entries."
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
- ape.error "Collection contains circular 'next' linkage: #{next_link}" if report
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
- ape.warning "Stopped reading collection after #{page_num} pages."
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
- ape.error "In entry with title '#{title}', #{error}."
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
- ape.error "#{entries.size - with_app_date} of #{entries.size} entries in #{name} lack app:edited elements."
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
- ape.good "#{name} has correct app:edited value order." if clean
123
+ reporter.success(self, "#{name} has correct app:edited value order.") if clean
112
124
 
113
125
  entries
114
126
  end
@@ -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|
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'net/https'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'net/http'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'net/http'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'net/http'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
1
+ # Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved
2
2
  # Use is subject to license terms - see file "LICENSE"
3
3
  require 'net/http'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
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
@@ -1,10 +1,8 @@
1
- # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
- # Use is subject to license terms - see file "LICENSE"
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
@@ -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/, '&#xa0;&#xa0;&#xa0;&#xa0;')
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