ape 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +14 -6
- data/bin/ape_server +1 -1
- data/lib/ape.rb +49 -36
- data/lib/ape/atomURI.rb +2 -2
- data/lib/ape/coll_element.rb +67 -0
- data/lib/ape/collection.rb +30 -33
- data/lib/ape/entry.rb +22 -10
- data/lib/ape/feed.rb +1 -1
- data/lib/ape/invoker.rb +17 -0
- data/lib/ape/invokers/getter.rb +18 -2
- data/lib/ape/invokers/poster.rb +6 -7
- data/lib/ape/names.rb +2 -1
- data/lib/ape/reporter.rb +1 -1
- data/lib/ape/samples.rb +2 -2
- data/lib/ape/server.rb +1 -1
- data/lib/ape/service.rb +20 -3
- data/lib/ape/util.rb +11 -7
- data/lib/ape/validator.rb +1 -1
- data/lib/ape/validators/entry_posts_validator.rb +2 -3
- data/lib/ape/validators/media_linkage_validator.rb +2 -2
- data/lib/ape/validators/media_posts_validator.rb +1 -2
- data/lib/ape/validators/sanitization_validator.rb +1 -1
- data/lib/ape/validators/service_document_validator.rb +14 -24
- data/lib/ape/version.rb +4 -4
- data/test/test_helper.rb +24 -1
- data/test/unit/ape_test.rb +8 -24
- data/test/unit/coll_element_test.rb +56 -0
- data/test/unit/collection_test.rb +43 -0
- data/test/unit/invoker_test.rb +6 -0
- data/test/unit/samples_test.rb +1 -1
- data/test/unit/service_test.rb +47 -0
- data/web/index.html +1 -1
- metadata +59 -51
data/lib/ape/invokers/getter.rb
CHANGED
@@ -57,6 +57,10 @@ module Ape
|
|
57
57
|
end
|
58
58
|
return false
|
59
59
|
end
|
60
|
+
|
61
|
+
def code
|
62
|
+
@response.code
|
63
|
+
end
|
60
64
|
|
61
65
|
def getBody contentType
|
62
66
|
|
@@ -66,15 +70,27 @@ module Ape
|
|
66
70
|
if @contentType =~ /^([^;]*);/
|
67
71
|
@contentType = $1
|
68
72
|
end
|
69
|
-
|
73
|
+
|
70
74
|
if contentType != @contentType
|
71
|
-
@
|
75
|
+
@response = Net::HTTPGone.new('1.1', '409',
|
76
|
+
"Content-type must be '#{contentType}', not '#{@contentType}'")
|
77
|
+
@body = ""
|
72
78
|
end
|
73
79
|
end
|
74
80
|
|
75
81
|
@body = @response.body
|
76
82
|
return true
|
77
83
|
end
|
84
|
+
|
85
|
+
# Handy wrapper with optional media-type checking; return the Response object
|
86
|
+
def Getter.retrieve(uri, content_type=nil, authent=nil)
|
87
|
+
|
88
|
+
getter = Getter.new(uri, authent)
|
89
|
+
raise(Exception, "Bad URI value #{uri}") if getter.last_error
|
90
|
+
|
91
|
+
getter.get content_type
|
92
|
+
return getter.response
|
93
|
+
end
|
78
94
|
end
|
79
95
|
end
|
80
96
|
|
data/lib/ape/invokers/poster.rb
CHANGED
@@ -8,12 +8,11 @@ module Ape
|
|
8
8
|
|
9
9
|
def initialize(uriString, authent)
|
10
10
|
super uriString, authent
|
11
|
-
@headers = {}
|
12
11
|
@entry = nil
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
@
|
14
|
+
def authoritative?
|
15
|
+
@response['Location'] == @response['Content-Location']
|
17
16
|
end
|
18
17
|
|
19
18
|
def post(contentType, body, req = nil)
|
@@ -35,16 +34,16 @@ module Ape
|
|
35
34
|
return false
|
36
35
|
end
|
37
36
|
|
38
|
-
|
39
|
-
|
37
|
+
# XXX how can this happen?
|
38
|
+
if (!(@response['Content-type'] =~ %r{^application/atom\+xml(;type=entry)?}))
|
40
39
|
return true
|
41
40
|
end
|
42
41
|
|
43
42
|
begin
|
44
|
-
@entry = Entry.new(@response.body)
|
43
|
+
@entry = Entry.new(:text => @response.body, :uri => @response['Location'])
|
45
44
|
return true
|
46
45
|
rescue ArgumentError
|
47
|
-
@last_error =
|
46
|
+
@last_error = $!.to_s
|
48
47
|
return false
|
49
48
|
end
|
50
49
|
end
|
data/lib/ape/names.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 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
|
@@ -16,6 +16,7 @@ module Ape
|
|
16
16
|
|
17
17
|
AtomMediaType = 'application/atom+xml' unless defined?(AtomMediaType)
|
18
18
|
AtomEntryMediaType = 'application/atom+xml;type=entry' unless defined?(AtomEntryMediaType)
|
19
|
+
AtomFeedMediaType = 'application/atom+xml;type=feed' unless defined?(AtomFeedMediaType)
|
19
20
|
AppMediaType = 'application/atomsvc+xml' unless defined?(AppMediaType)
|
20
21
|
|
21
22
|
end
|
data/lib/ape/reporter.rb
CHANGED
@@ -23,7 +23,7 @@ module Ape
|
|
23
23
|
|
24
24
|
def self.supported_outputs
|
25
25
|
Dir[File.join(File.dirname(__FILE__), 'reporters/*.rb'),
|
26
|
-
File.join(Ape.home, 'reporters/*.rb')].map { |file|
|
26
|
+
File.join(::Ape.home, 'reporters/*.rb')].map { |file|
|
27
27
|
file.gsub(/(.+\/reporters\/)(.+)(_reporter.rb)/, '\2').gsub(/_/, '')
|
28
28
|
}.sort.join(", ").downcase
|
29
29
|
end
|
data/lib/ape/samples.rb
CHANGED
@@ -61,8 +61,8 @@ module Ape
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def Samples.entry_path(type)
|
64
|
-
File.exist?(File.join(Ape.home, "/#{type}.eruby"))?
|
65
|
-
File.join(Ape.home, "/#{type}.eruby") :
|
64
|
+
File.exist?(File.join(::Ape.home, "/#{type}.eruby"))?
|
65
|
+
File.join(::Ape.home, "/#{type}.eruby") :
|
66
66
|
File.join(File.dirname(__FILE__), "/../../samples/#{type}.eruby")
|
67
67
|
end
|
68
68
|
|
data/lib/ape/server.rb
CHANGED
@@ -16,7 +16,7 @@ module Ape
|
|
16
16
|
# * :port - the port number to listen on
|
17
17
|
# * :directory - the ape home directory
|
18
18
|
def self.run(options)
|
19
|
-
Ape.home = options[:home]
|
19
|
+
::Ape.home = options[:home]
|
20
20
|
|
21
21
|
mongrel = Mongrel::Configurator.new(:host => options[:host], :port => options[:port]) do
|
22
22
|
log "=> Booting mongrel"
|
data/lib/ape/service.rb
CHANGED
@@ -4,9 +4,26 @@ require 'rexml/xpath'
|
|
4
4
|
|
5
5
|
module Ape
|
6
6
|
class Service
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
require File.dirname(__FILE__) + '/util.rb'
|
8
|
+
include Ape::Util::InstanceMethods
|
9
|
+
|
10
|
+
attr_accessor :service, :reporter
|
11
|
+
|
12
|
+
def initialize(opts = {}) #uri = nil, authent = nil)
|
13
|
+
@authent = opts[:authent]
|
14
|
+
@reporter = opts[:reporter]
|
15
|
+
if opts[:uri]
|
16
|
+
@uri = opts[:uri]
|
17
|
+
resource = check_resource(@uri, 'Service document', Names::AppMediaType, @reporter)
|
18
|
+
raise StandardError, "Service document not found at: #{@uri}" unless resource
|
19
|
+
|
20
|
+
@service = REXML::Document.new(resource.body, { :raw => nil })
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def collections(uri = @uri)
|
25
|
+
nodes = REXML::XPath.match(@service, '//app:collection', Names::XmlNamespaces)
|
26
|
+
nodes.collect { |n| CollElement.new(n, uri) }
|
10
27
|
end
|
11
28
|
end
|
12
29
|
end
|
data/lib/ape/util.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
module Ape
|
2
2
|
module Util
|
3
|
-
def self.included(base)
|
4
|
-
base.extend ClassMethods
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
5
|
include InstanceMethods
|
6
6
|
end
|
7
|
+
|
8
|
+
def self.extended(base)
|
9
|
+
base.extend InstanceMethods
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
7
12
|
|
8
13
|
module InstanceMethods
|
9
14
|
=begin
|
@@ -20,9 +25,9 @@ module Ape
|
|
20
25
|
|
21
26
|
# * Get it, make sure it has the right content-type
|
22
27
|
worked = resource.get(content_type)
|
23
|
-
reporter.save_dialog(name, resource)
|
28
|
+
reporter.save_dialog(name, resource) if report
|
24
29
|
|
25
|
-
reporter.security_warning(self) if (resource.security_warning)
|
30
|
+
reporter.security_warning(self) if (resource.security_warning && report)
|
26
31
|
|
27
32
|
if !worked
|
28
33
|
# oops, couldn't even get get it
|
@@ -32,7 +37,6 @@ module Ape
|
|
32
37
|
elsif resource.last_error
|
33
38
|
# oops, media-type problem
|
34
39
|
reporter.error(self, "#{name}: #{resource.last_error}", name) if report
|
35
|
-
|
36
40
|
else
|
37
41
|
# resource fetched and is of right type
|
38
42
|
reporter.success(self, "#{name}: it exists and is served properly.", name) if report
|
@@ -48,7 +52,7 @@ module Ape
|
|
48
52
|
resolvers or samples.
|
49
53
|
=end
|
50
54
|
def resolve_plugin(key, dir, suffix, drop_underlines = false)
|
51
|
-
[File.dirname(__FILE__), Ape.home].each do |path|
|
55
|
+
[File.dirname(__FILE__), ::Ape.home].each do |path|
|
52
56
|
Dir[File.join(path, "#{dir}/*.rb")].each do |file|
|
53
57
|
require file
|
54
58
|
plugin_name = file.gsub(/(.+\/#{dir}\/)(.+)(_#{suffix}.rb)/, '\2')
|
@@ -64,4 +68,4 @@ resolvers or samples.
|
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
67
|
-
end
|
71
|
+
end
|
data/lib/ape/validator.rb
CHANGED
@@ -11,7 +11,7 @@ module Ape
|
|
11
11
|
|
12
12
|
def self.custom_validators(reporter, authent)
|
13
13
|
validators = []
|
14
|
-
Dir[Ape.home + '/validators/*.rb'].each do |v|
|
14
|
+
Dir[::Ape.home + '/validators/*.rb'].each do |v|
|
15
15
|
require v
|
16
16
|
class_name = v.gsub(/(.+\/validators\/)(.+)(.rb)/, '\2').gsub(/(^|_)(.)/) { $2.upcase }
|
17
17
|
validator = eval("#{class_name}.new", binding, __FILE__, __LINE__)
|
@@ -29,7 +29,7 @@ module Ape
|
|
29
29
|
return
|
30
30
|
end
|
31
31
|
|
32
|
-
my_entry = Entry.new(Samples.basic_entry)
|
32
|
+
my_entry = Entry.new(:text => Samples.basic_entry)
|
33
33
|
|
34
34
|
# ask it to use this in the URI
|
35
35
|
slug_num = rand(100000)
|
@@ -71,7 +71,7 @@ module Ape
|
|
71
71
|
reporter.info(self, "Examining the new entry as retrieved using Location header in POST response:")
|
72
72
|
|
73
73
|
begin
|
74
|
-
new_entry = Entry.new(new_entry.body, location)
|
74
|
+
new_entry = Entry.new(:text => new_entry.body, :uri => location)
|
75
75
|
rescue REXML::ParseException
|
76
76
|
prob = $!.to_s.gsub(/\n/, '<br/>')
|
77
77
|
reporter.error(self, "New entry is not well-formed: #{prob}")
|
@@ -99,7 +99,6 @@ module Ape
|
|
99
99
|
# * fetch the feed again and check that version
|
100
100
|
from_feed = find_entry(collection_uri, "entry collection", entry_id)
|
101
101
|
if from_feed.class == String
|
102
|
-
reporter.success(self, "About to check #{collection_uri}")
|
103
102
|
Feed.read(collection_uri, "Can't find entry in collection", reporter)
|
104
103
|
reporter.error(self, "New entry didn't show up in the collections feed.")
|
105
104
|
return
|
@@ -30,7 +30,7 @@ module Ape
|
|
30
30
|
return
|
31
31
|
end
|
32
32
|
|
33
|
-
ids = entries.map { |e| e.child_content('id
|
33
|
+
ids = entries.map { |e| e.child_content('id') }
|
34
34
|
|
35
35
|
# let's update one of them; have to fetch it first to get the ETag
|
36
36
|
two_media = entries[1].link('edit-media')
|
@@ -75,4 +75,4 @@ module Ape
|
|
75
75
|
reporter.success(self, "Entries correctly ordered after update of multi-post.")
|
76
76
|
end
|
77
77
|
end
|
78
|
-
end
|
78
|
+
end
|
@@ -25,7 +25,6 @@ module Ape
|
|
25
25
|
slug_re = %r{apix.?#{slug_num}}
|
26
26
|
poster.set_header('Slug', slug)
|
27
27
|
|
28
|
-
#poster.set_header('Slug', slug)
|
29
28
|
worked = poster.post('image/jpeg', Samples.picture)
|
30
29
|
reporter.save_dialog(name, poster)
|
31
30
|
if !worked
|
@@ -50,7 +49,7 @@ module Ape
|
|
50
49
|
|
51
50
|
# * See if the <content src= is there and usable
|
52
51
|
begin
|
53
|
-
media_link_entry = Entry.new(media_link_entry.body, mle_uri)
|
52
|
+
media_link_entry = Entry.new(:text => media_link_entry.body, :uri => mle_uri)
|
54
53
|
rescue REXML::ParseException
|
55
54
|
prob = $!.to_s.gsub(/\n/, '<br/>')
|
56
55
|
reporter.error(self, "Media link entry is not well-formed: #{prob}")
|
@@ -22,7 +22,7 @@ module Ape
|
|
22
22
|
return unless entry
|
23
23
|
|
24
24
|
begin
|
25
|
-
entry = Entry.new(entry.body, location)
|
25
|
+
entry = Entry.new(:text => entry.body, :uri => location)
|
26
26
|
rescue REXML::ParseException
|
27
27
|
prob = $!.to_s.gsub(/\n/, '<br/>')
|
28
28
|
reporter.error(self, "New entry is not well-formed: #{prob}")
|
@@ -6,30 +6,23 @@ module Ape
|
|
6
6
|
|
7
7
|
def validate(opts = {})
|
8
8
|
init_service_document(opts[:uri])
|
9
|
-
raise ValidationError, "service document not found in: #{opts[uri]}" unless @service
|
10
9
|
init_service_collections(opts[:uri])
|
11
|
-
raise ValidationError unless @entry_collections && @media_collections
|
12
10
|
end
|
13
11
|
|
14
12
|
def init_service_document(uri)
|
15
13
|
reporter.info(self, "TESTING: Service document and collections.")
|
16
|
-
|
17
|
-
service = check_resource(uri, name, Names::AppMediaType)
|
18
|
-
return unless service
|
19
|
-
|
20
|
-
# * XML-parse the service doc
|
21
|
-
text = service.body
|
14
|
+
|
22
15
|
begin
|
23
|
-
@
|
24
|
-
rescue
|
16
|
+
@service_document = Service.new(:uri => uri, :reporter => reporter )
|
17
|
+
rescue Exception
|
25
18
|
prob = $!.to_s.gsub(/\n/, '<br/>')
|
26
|
-
reporter.error(self, "Service document not
|
19
|
+
reporter.error(self, "Service document not usable: #{prob}")
|
27
20
|
return
|
28
21
|
end
|
29
22
|
|
30
23
|
# RNC-validate the service doc
|
31
24
|
Validator.instance(:schema, @reporter).validate(:schema => Samples.service_RNC,
|
32
|
-
:title => 'Service doc', :doc => @service)
|
25
|
+
:title => 'Service doc', :doc => @service_document.service)
|
33
26
|
end
|
34
27
|
|
35
28
|
def init_service_collections(uri)
|
@@ -37,23 +30,20 @@ module Ape
|
|
37
30
|
# the requested_* arguments are the requested collection titles; if
|
38
31
|
# provided, try to match them, otherwise just pick the first listed
|
39
32
|
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
reporter.error(self, "Couldn't read collections from service doc: #{$!}")
|
44
|
-
return
|
45
|
-
end
|
46
|
-
|
47
|
-
if @service_collections.length > 0
|
33
|
+
collections = @service_document.collections
|
34
|
+
if collections.length > 0
|
35
|
+
|
48
36
|
reporter.start_list(self, "Found these collections")
|
49
|
-
|
37
|
+
collections.each do |collection|
|
50
38
|
reporter.list_item("'#{collection.title}' " +
|
51
|
-
|
39
|
+
"accepts #{collection.accept.join(', ')}")
|
52
40
|
|
53
|
-
if
|
41
|
+
if collection.accept?(Names::AtomEntryMediaType)
|
54
42
|
@entry_collections ||= []
|
55
43
|
@entry_collections << collection
|
56
|
-
|
44
|
+
end
|
45
|
+
|
46
|
+
if collection.accept?('image/jpeg')
|
57
47
|
@media_collections ||= []
|
58
48
|
@media_collections << collection
|
59
49
|
end
|
data/lib/ape/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -15,7 +15,7 @@ module Writer
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
Ape::Invoker.send(:include, Writer)
|
18
|
-
|
18
|
+
|
19
19
|
module ApeAccessors
|
20
20
|
def service=(service)
|
21
21
|
@service = service
|
@@ -47,3 +47,26 @@ class ValidatorMock < Ape::Validator
|
|
47
47
|
deterministic
|
48
48
|
requires_presence_of :entry_collection
|
49
49
|
end
|
50
|
+
|
51
|
+
def collection(title, accept = ['application/atom+xml;type=entry'])
|
52
|
+
collection = "<collection href=\"http://localhost\">" +
|
53
|
+
"<atom:title>#{title}</atom:title>"
|
54
|
+
accept.each do |a|
|
55
|
+
collection += "<accept>#{a}</accept>"
|
56
|
+
end
|
57
|
+
collection += "</collection>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def service(&block)
|
61
|
+
service = "<service xmlns=\"http://www.w3.org/2007/app\"" +
|
62
|
+
" xmlns:atom=\"http://www.w3.org/2005/Atom\"><workspace>"
|
63
|
+
service += yield
|
64
|
+
service += "</workspace></service>"
|
65
|
+
REXML::Document.new(service)
|
66
|
+
end
|
67
|
+
|
68
|
+
def collections(&block)
|
69
|
+
s = service(&block)
|
70
|
+
nodes = REXML::XPath.match(s, '//app:collection', Ape::Names::XmlNamespaces)
|
71
|
+
nodes.collect { |n| Ape::CollElement.new(n) }
|
72
|
+
end
|
data/test/unit/ape_test.rb
CHANGED
@@ -10,26 +10,26 @@ class ApeTest < Test::Unit::TestCase
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_check_manifest_raises_validation_error
|
13
|
-
@ape.
|
13
|
+
@ape.media_collections = collections { collection('Attachments', ['image/jpg']) }
|
14
14
|
assert_raise(Ape::ValidationError) {
|
15
|
-
|
15
|
+
@ape.check_manifest(ValidatorMock.new)
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_check_manifest_assert_first_entry_collection
|
20
|
-
@ape.entry_collections =
|
20
|
+
@ape.entry_collections = collections { collection('Posts') }
|
21
21
|
variables = @ape.check_manifest(ValidatorMock.new)
|
22
22
|
assert_equal('Posts', variables[:entry_collection].title)
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_check_manifest_assert_first_media_collection
|
26
|
-
@ape.media_collections =
|
26
|
+
@ape.media_collections = collections { collection('Attachments', ['image/jpg']) }
|
27
27
|
variables = stub_manifest([:media_collection])
|
28
28
|
assert_equal('Attachments', variables[:media_collection].title)
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_check_manifest_assert_select_last_collection
|
32
|
-
@ape.entry_collections =
|
32
|
+
@ape.entry_collections = collections {
|
33
33
|
collection('Posts') +
|
34
34
|
collection('Comments')
|
35
35
|
}
|
@@ -39,7 +39,7 @@ class ApeTest < Test::Unit::TestCase
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_select_first_collection_by_type
|
42
|
-
@ape.entry_collections =
|
42
|
+
@ape.entry_collections = collections {
|
43
43
|
collection('Posts') +
|
44
44
|
collection('Attachments', ['image/jpg']) +
|
45
45
|
collection('Attachments2', ['image/png'])
|
@@ -49,7 +49,7 @@ class ApeTest < Test::Unit::TestCase
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def test_select_first_collection_by_title
|
52
|
-
@ape.entry_collections =
|
52
|
+
@ape.entry_collections = collections {
|
53
53
|
collection('Posts') +
|
54
54
|
collection('Attachments', ['image/jpg'])
|
55
55
|
}
|
@@ -58,7 +58,7 @@ class ApeTest < Test::Unit::TestCase
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def test_select_by_name_raises_validation_error
|
61
|
-
@ape.entry_collections =
|
61
|
+
@ape.entry_collections = collections {
|
62
62
|
collection('Posts') +
|
63
63
|
collection('Attachments', ['image/jpg'])
|
64
64
|
}
|
@@ -73,20 +73,4 @@ class ApeTest < Test::Unit::TestCase
|
|
73
73
|
@ape.check_manifest(validator)
|
74
74
|
end
|
75
75
|
|
76
|
-
def collection(title, accept = ['application/atom+xml;type=entry'])
|
77
|
-
collection = "<collection href=\"http://localhost\">" +
|
78
|
-
"<atom:title>#{title}</atom:title>"
|
79
|
-
accept.each do |a|
|
80
|
-
collection += "<accept>#{a}</accept>"
|
81
|
-
end
|
82
|
-
collection += "</collection>"
|
83
|
-
end
|
84
|
-
|
85
|
-
def service(&block)
|
86
|
-
service = "<service xmlns=\"http://www.w3.org/2007/app\"" +
|
87
|
-
" xmlns:atom=\"http://www.w3.org/2005/Atom\"><workspace>"
|
88
|
-
service += yield
|
89
|
-
service += "</workspace></service>"
|
90
|
-
Ape::Service.collections(REXML::Document.new(service), nil)
|
91
|
-
end
|
92
76
|
end
|