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
@@ -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