ape 1.5.1 → 1.6.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.
- 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
|