ape 1.0.0 → 1.5.0

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