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
@@ -0,0 +1,51 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+
6
+ module Ape
7
+ class AtomReporter < Reporter
8
+ def escape(text)
9
+ Escaper.escape(text)
10
+ end
11
+
12
+ def now
13
+ DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
14
+ end
15
+
16
+ def id
17
+ id = ''
18
+ 5.times { id += rand(1000000).to_s }
19
+ "tag:tbray.org,2005:#{id}"
20
+ end
21
+
22
+ def content(step, dialog = nil)
23
+ xml = "<div xmlns=\"http://www.w3.org/1999/xhtml\">"
24
+ lines = step.split("\n")
25
+ lines[0 .. -2].each do |line|
26
+ xml += "#{line} <br/>"
27
+ end
28
+ xml += escape(lines[-1]) if lines[-1]
29
+ if dialog && dialogs[dialog]
30
+ xml += '<div>'
31
+ xml += 'To server:'
32
+ dialogs[dialog].grep(/^>/).each do |crumb|
33
+ xml += show_message(crumb, :to)
34
+ end
35
+ xml += '</div>'
36
+ xml += '<div>'
37
+ xml += 'From server:'
38
+ dialogs[dialog].grep(/^</).each do |crumb|
39
+ xml += show_message(crumb, :from)
40
+ end
41
+ xml += '</div>'
42
+ end
43
+ xml += '</div>'
44
+ xml
45
+ end
46
+
47
+ def report(output = STDOUT)
48
+ output.puts evaluate_template("reporters/atom_template.eruby")
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <feed xmlns="http://www.w3.org/2005/Atom">
3
+ <id><%= id %></id>
4
+ <author><name>The ape</name></author>
5
+ <title>Atom Protocol Exerciser Report</title>
6
+ <updated><%= now %></updated>
7
+ <subtitle><%= errors.length == 1?"1 error":"#{errors.length} errors"%>, <%= warnings.length == 1?"1 warning":"#{warnings.length} warnings"%></subtitle>
8
+ <% steps.each do |step| %>
9
+ <% if step.kind_of?(Array) %>
10
+ <entry>
11
+ <id><%= id %></id>
12
+ <title type="html"><%= "INFO: #{step[0]}"%></title>
13
+ <updated><%= now %></updated>
14
+ <category term="info"/>
15
+ <content type="xhtml">
16
+ <% step[1..-1].each do |li| %>
17
+ <%= content(li[:message]) %>
18
+ <% end %>
19
+ </content>
20
+ </entry>
21
+ <% else %>
22
+ <% if step[:severity] == :debug %>
23
+ <% @dialog = step[:message] %>
24
+ <% else %>
25
+ <entry>
26
+ <id><%= id %></id>
27
+ <title type="html"><%= step[:severity].to_s.upcase%>: <%= step[:message] %></title>
28
+ <updated><%= now %></updated>
29
+ <category term="<%= step[:severity].to_s %>"/>
30
+ <content type="xhtml">
31
+ <%= content(step[:message], @dialog) %>
32
+ </content>
33
+ </entry>
34
+ <% @dialog = nil %>
35
+ <% end %>
36
+ <% end %>
37
+ <% end %>
38
+ </feed>
@@ -0,0 +1,53 @@
1
+ require 'erubis'
2
+ module Ape
3
+ class HtmlReporter < Reporter
4
+
5
+ def static_dir
6
+ dir = (server != true)?File.expand_path(File.dirname(__FILE__) + '/../../..') : ''
7
+ dir += "/web"
8
+ dir
9
+ end
10
+
11
+ def stylesheet
12
+ static_dir + '/ape.css'
13
+ end
14
+
15
+ def mark(mark)
16
+ span = "<span class=\"#{mark.to_s}\">"
17
+ case mark
18
+ when :success
19
+ span << '&#x2713;' << '</span>'
20
+ when :warning
21
+ span << '?' << '</span>'
22
+ when :error
23
+ span << '!' << '</span>'
24
+ when :info
25
+ span = "<img class=\"info\" src=\"#{static_dir + '/info.png'}\"/>"
26
+ end
27
+ end
28
+
29
+ def report_li(step, dialog = nil, marker = nil)
30
+ html = '<li>'
31
+ if marker
32
+ html += "#{mark(marker)} "
33
+ end
34
+ # preserve line-breaks in output
35
+ lines = step.split("\n")
36
+ lines[0 .. -2].each do |line|
37
+ html += "#{line} <br/>"
38
+ end
39
+ html += lines[-1] if lines[-1]
40
+ if dialog
41
+ html += "<a class=\"diaref\" href=\"#dia-#{@dianum}\">[Dialog]</a>"
42
+ @diarefs[dialog] = @dianum
43
+ @dianum += 1
44
+ end
45
+ html += '</li>'
46
+ html
47
+ end
48
+
49
+ def report(output = STDOUT)
50
+ output.puts evaluate_template("reporters/html_template.eruby")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,62 @@
1
+ <html>
2
+ <head>
3
+ <title>Atom Protocol Exerciser Report</title>
4
+ <link rel="stylesheet" type="text/css" href="<%= stylesheet %>" />
5
+ </head>
6
+ <body>
7
+ <h2>The Ape says:</h2>
8
+ <% if header %>
9
+ <p><%= header %></p>
10
+ <p>Summary: <%= errors.length == 1?"1 error":"#{errors.length} errors"%>,
11
+ <%= warnings.length == 1?"1 warning":"#{warnings.length} warnings"%>
12
+ </p>
13
+ <% end %>
14
+ <ol id="steps">
15
+ <% steps.each do |step| %>
16
+ <% if step.kind_of?(Array) %>
17
+ <li>
18
+ <p><%= mark(:info) %> <%= step[0] %></p>
19
+ <ul>
20
+ <% step[1..-1].each do |li| %>
21
+ <%= report_li(li[:message]) %>
22
+ <% end %>
23
+ </ul>
24
+ </li>
25
+ <% else %>
26
+ <% if step[:severity] == :debug %>
27
+ <% @dialog = step[:message] %>
28
+ <% else %>
29
+ <%= report_li(step[:message], @dialog, step[:severity])%>
30
+ <% @dialog = nil %>
31
+ <% end %>
32
+ <% end %>
33
+ <% end %>
34
+ </ol>
35
+
36
+ <% if footer %>
37
+ <p><%= footer %></p>
38
+ <% end %>
39
+
40
+ <% unless dialogs.empty? %>
41
+ <h2>Recorded client/server dialogs</h2>
42
+ <% diarefs.each do |k, v| %>
43
+ <% @dialog = @dialogs[k] %>
44
+ <h3 id="<%="dia-#{v}"%>"><%= k %></h3>
45
+ <div class="dialog">
46
+ <div class="dialab">
47
+ To server:
48
+ <% @dialog.grep(/^>/).each do |crumb| %>
49
+ <%= show_message(crumb, :to) %>
50
+ <% end %>
51
+ </div>
52
+ <div class="dialab">
53
+ From server:
54
+ <% @dialog.grep(/^</).each do |crumb| %>
55
+ <%= show_message(crumb, :from) %>
56
+ <% end %>
57
+ </div>
58
+ </div>
59
+ <% end %>
60
+ <% end %>
61
+ </body>
62
+ </html>
@@ -0,0 +1,37 @@
1
+ module Ape
2
+ class TextReporter < Reporter
3
+ def report(output = STDOUT)
4
+ if @header
5
+ output.puts @header
6
+ output.puts errors.length == 1?"1 error":"#{errors.length} errors"
7
+ output.puts warnings.length == 1?"1 warning":"#{warnings.length} warnings"
8
+ end
9
+ steps.each do |step|
10
+ if step.class == Crumbs
11
+ output.puts " Dialog:"
12
+ step.each { |crumb| output.puts " #{crumb}" }
13
+ else
14
+ line
15
+ if (step.kind_of?Array)
16
+ output.puts "INFO: #{step[0]}"
17
+ step[1..-1].each do |li|
18
+ lines = li[:message].split("\n")
19
+ lines[0..-2].each do |line|
20
+ output.puts("\t #{line} \n")
21
+ end
22
+ output.puts("\t #{lines[-1]}") if lines[-1]
23
+ end
24
+ else
25
+ case step[:severity]
26
+ when :warning, :error
27
+ output.puts "#{step[:severity].to_s.upcase}: #{step[:message]}"
28
+ else
29
+ output.puts step[:message]
30
+ end
31
+ end
32
+ end
33
+ output.puts @footer if @footer
34
+ end
35
+ end
36
+ end
37
+ end
@@ -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
  require 'date'
@@ -6,17 +6,23 @@ require 'base64'
6
6
  require 'erubis'
7
7
 
8
8
  module Ape
9
+ class SamplesContext < Erubis::Context
10
+ def now
11
+ DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
12
+ end
13
+ def id
14
+ id = ''
15
+ 5.times { id += rand(1000000).to_s }
16
+ "tag:tbray.org,2005:#{id}"
17
+ end
18
+ end
19
+
9
20
  class Samples
10
21
 
11
22
  @@service_schema = nil
12
23
  @@categories_schema = nil
13
24
  @@atom_schema = nil
14
- @@home = nil
15
25
 
16
- def Samples.home=(home)
17
- @@home = home
18
- end
19
-
20
26
  def Samples.foreign_child
21
27
  'subject'
22
28
  end
@@ -28,13 +34,7 @@ module Ape
28
34
  end
29
35
 
30
36
  def Samples.load_schema(file_name)
31
- schema = ""
32
- File.open(File.join(File.dirname(__FILE__), "/samples/#{file_name}_schema.txt")) do |file|
33
- while(line = file.gets)
34
- schema << line
35
- end
36
- end
37
- schema
37
+ IO.read(File.join(File.dirname(__FILE__), "/../../samples/#{file_name}_schema.txt"))
38
38
  end
39
39
 
40
40
  def Samples.service_RNC
@@ -52,15 +52,7 @@ module Ape
52
52
  @@atom_schema
53
53
  end
54
54
 
55
- #recipe from cap
56
- def Samples.home_directory
57
- ENV["HOME"] || (ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") ||
58
- "/"
59
- end
60
55
 
61
- def Samples.home
62
- @@home || ENV["APE_HOME"] || File.join(home_directory,".ape")
63
- end
64
56
 
65
57
  def Samples.make_id
66
58
  id = ''
@@ -69,57 +61,44 @@ module Ape
69
61
  end
70
62
 
71
63
  def Samples.entry_path(type)
72
- File.exist?(File.join(home, "/#{type}.eruby"))?
73
- File.join(home, "/#{type}.eruby") :
74
- File.join(File.dirname(__FILE__), "/samples/#{type}.eruby")
64
+ File.exist?(File.join(Ape.home, "/#{type}.eruby"))?
65
+ File.join(Ape.home, "/#{type}.eruby") :
66
+ File.join(File.dirname(__FILE__), "/../../samples/#{type}.eruby")
75
67
  end
76
68
 
77
69
  def Samples.load_template(type)
78
- entry_path = entry_path(type)
79
- input = File.read(entry_path)
80
- eruby = Erubis::Eruby.new(input)
70
+ Erubis::FastEruby.new(IO.read(entry_path(type)))
81
71
  end
82
72
 
83
73
 
84
74
  def Samples.mini_entry
85
- eruby = load_template('mini_entry')
86
-
87
- now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
88
- id = make_id
89
-
90
- eruby.result(binding())
75
+ load_template('mini_entry').evaluate(SamplesContext.new)
91
76
  end
92
77
 
93
78
  def Samples.basic_entry
94
- eruby = load_template('basic_entry')
79
+ eruby = load_template('basic_entry')
80
+
81
+ context = SamplesContext.new(
82
+ :title => Escaper.escape('From the <APE> (サル)'),
83
+ :subject => Names::DcNamespace
84
+ )
85
+ context[:summary] = "Summary from the &lt;b>&amp;lt;&amp;nbsp;APE&amp;nbsp;>&lt;/b> at #{context.now}"
95
86
 
96
- id = make_id
97
- now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
98
- title = Escaper.escape('From the <APE> (サル)')
99
- summary = "Summary from the &lt;b>&amp;lt;&amp;nbsp;APE&amp;nbsp;>&lt;/b> at #{now}"
100
- subject = Names::DcNamespace
101
-
102
- eruby.result(binding())
87
+ eruby.evaluate(context)
103
88
  end
104
89
 
105
90
  def Samples.unclean_xhtml_entry
106
- eruby = load_template('unclean_xhtml_entry')
107
-
108
- id = make_id
109
- now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
110
-
111
- eruby.result(binding())
91
+ load_template('unclean_xhtml_entry').evaluate(SamplesContext.new)
112
92
  end
113
93
 
114
94
  def Samples.cat_test_entry
115
- e = retitled_entry('Testing category posting')
95
+ retitled_entry('Testing category posting')
116
96
  end
117
97
 
118
98
  def Samples.retitled_entry(new_title, new_id = nil)
119
99
  e = basic_entry
120
- e.gsub!(/<title>.*<\/title>/, "<title>#{new_title}</title>")
121
- new_id = make_id unless new_id
122
- e.gsub(/<id>.*<\/id>/, "<id>#{new_id}</id>")
100
+ e.gsub!(/<title>.*<\/title>/, "<title>#{new_title}</title>")
101
+ e.gsub(/<id>.*<\/id>/, "<id>#{new_id}</id>") if new_id
123
102
  end
124
103
 
125
104
  def Samples.picture
@@ -4,18 +4,30 @@ require 'ape/handler'
4
4
  require 'ape/samples'
5
5
 
6
6
  module Ape
7
+
8
+ # Manages and initializes the Mongrel handler. See run for more details.
7
9
  class Server
10
+
11
+ # Starts the Mongrel handler with options given in +options+ and
12
+ # maps the <b>/</b>, <b>/ape</b> and <b>/atompub/go</b> URIs to handlers.
13
+ #
14
+ # ==== Options
15
+ # * :host - the IP address to bind to
16
+ # * :port - the port number to listen on
17
+ # * :directory - the ape home directory
8
18
  def self.run(options)
9
- Samples.home = options[:home]
19
+ Ape.home = options[:home]
10
20
 
11
21
  mongrel = Mongrel::Configurator.new(:host => options[:host], :port => options[:port]) do
12
22
  log "=> Booting mongrel"
13
23
  begin
14
24
  log "=> The ape starting on http://#{options[:host]}:#{options[:port]}"
15
25
  listener do
16
- redirect '/', '/ape/index.html'
17
- uri '/ape', :handler => Mongrel::DirHandler.new(File.dirname(__FILE__) + '/layout', true)
18
- uri '/atompub/go', :handler => Handler.new
26
+ redirect '/', '/web/index.html'
27
+ uri '/web', :handler =>
28
+ Mongrel::DirHandler.new(
29
+ File.expand_path(File.dirname(__FILE__) + '/../../web'), true)
30
+ uri '/atompub/go', :handler => Handler.new
19
31
  end
20
32
  rescue Errno::EADDRINUSE
21
33
  log "ERROR: Address (#{options[:host]}:#{options[:port]}) is already in use"
@@ -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
 
@@ -0,0 +1,67 @@
1
+ module Ape
2
+ module Util
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ include InstanceMethods
6
+ end
7
+
8
+ module InstanceMethods
9
+ =begin
10
+ Get a resource, optionally check its content-type
11
+ =end
12
+ def check_resource(uri, name, content_type = nil, report = true)
13
+ resource = Getter.new(uri, @authent)
14
+
15
+ # * Check the URI
16
+ if resource.last_error
17
+ reporter.error(self, "Unacceptable #{name} URI: " + resource.last_error, name) if report
18
+ return nil
19
+ end
20
+
21
+ # * Get it, make sure it has the right content-type
22
+ worked = resource.get(content_type)
23
+ reporter.save_dialog(name, resource)
24
+
25
+ reporter.security_warning(self) if (resource.security_warning)
26
+
27
+ if !worked
28
+ # oops, couldn't even get get it
29
+ reporter.error(self, "#{name} failed: " + resource.last_error, name) if report
30
+ return nil
31
+
32
+ elsif resource.last_error
33
+ # oops, media-type problem
34
+ reporter.error(self, "#{name}: #{resource.last_error}", name) if report
35
+
36
+ else
37
+ # resource fetched and is of right type
38
+ reporter.success(self, "#{name}: it exists and is served properly.", name) if report
39
+ end
40
+
41
+ return resource
42
+ end
43
+ end
44
+
45
+ module ClassMethods
46
+ =begin
47
+ Resolve extensions into the lib directory or into the ape home directory. These extensions could be validators,
48
+ resolvers or samples.
49
+ =end
50
+ def resolve_plugin(key, dir, suffix, drop_underlines = false)
51
+ [File.dirname(__FILE__), Ape.home].each do |path|
52
+ Dir[File.join(path, "#{dir}/*.rb")].each do |file|
53
+ require file
54
+ plugin_name = file.gsub(/(.+\/#{dir}\/)(.+)(_#{suffix}.rb)/, '\2')
55
+ plugin_name.gsub!(/_/, '') if drop_underlines
56
+ plugin_class = file.gsub(/(.+\/#{dir}\/)(.+)(.rb)/, '\2').gsub(/(^|_)(.)/) { $2.upcase }
57
+
58
+ if (key.to_s.strip.downcase.include?(plugin_name))
59
+ return eval("#{plugin_class}.new", binding, __FILE__, __LINE__)
60
+ end
61
+ end
62
+ end
63
+ return nil
64
+ end
65
+ end
66
+ end
67
+ end