atom-tools 0.9.0 → 2.0.5
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/COPYING +3 -3
- data/README +9 -49
- data/Rakefile +65 -62
- data/bin/atom-cp +159 -0
- data/bin/atom-grep +78 -0
- data/bin/atom-post +73 -0
- data/bin/atom-purge +82 -0
- data/doc/classes/Atom/AttrEl.html +163 -0
- data/doc/classes/Atom/Author.html +2 -19
- data/doc/classes/Atom/AutodiscoveryFailure.html +111 -0
- data/doc/classes/Atom/Categories.html +168 -0
- data/doc/classes/Atom/Category.html +10 -1
- data/doc/classes/Atom/Collection.html +194 -91
- data/doc/classes/Atom/Content.html +42 -1
- data/doc/classes/Atom/Contributor.html +2 -8
- data/doc/classes/Atom/Control.html +126 -0
- data/doc/classes/Atom/Converters.html +571 -0
- data/doc/classes/Atom/DigestAuth.html +285 -0
- data/doc/classes/Atom/Element.html +673 -106
- data/doc/classes/Atom/FileCache.html +287 -0
- data/doc/classes/Atom/HTTP.html +211 -95
- data/doc/classes/Atom/HTTPResponse.html +190 -0
- data/doc/classes/Atom/HasCategories.html +187 -0
- data/doc/classes/Atom/HasLinks.html +168 -0
- data/doc/classes/Atom/Link.html +80 -3
- data/doc/classes/Atom/NilCache.html +202 -0
- data/doc/classes/Atom/ParseError.html +111 -0
- data/doc/classes/Atom/Parsers.html +311 -0
- data/doc/classes/Atom/Person.html +130 -0
- data/doc/classes/Atom/Rights.html +113 -0
- data/doc/classes/Atom/Service.html +298 -0
- data/doc/{files/lib/atom/yaml_rb.html → classes/Atom/Source.html} +26 -24
- data/doc/classes/Atom/Subtitle.html +113 -0
- data/doc/classes/Atom/Summary.html +113 -0
- data/doc/classes/Atom/Text.html +196 -53
- data/doc/classes/Atom/Title.html +113 -0
- data/doc/classes/Atom/Tools.html +525 -0
- data/doc/classes/Atom/Workspace.html +121 -0
- data/doc/classes/Object.html +204 -0
- data/doc/created.rid +1 -1
- data/doc/files/README.html +9 -49
- data/doc/files/lib/atom/cache_rb.html +302 -0
- data/doc/files/lib/atom/collection_rb.html +1 -2
- data/doc/files/lib/atom/element_rb.html +2 -1
- data/doc/files/lib/atom/entry_rb.html +2 -2
- data/doc/files/lib/atom/feed_rb.html +1 -2
- data/doc/files/lib/atom/http_rb.html +6 -1
- data/doc/files/lib/atom/{xml_rb.html → service_rb.html} +8 -6
- data/doc/files/lib/atom/text_rb.html +2 -1
- data/doc/files/lib/atom/{app_rb.html → tools_rb.html} +4 -6
- data/doc/fr_class_index.html +23 -3
- data/doc/fr_file_index.html +3 -3
- data/doc/fr_method_index.html +111 -36
- data/lib/atom/cache.rb +178 -0
- data/lib/atom/collection.rb +90 -40
- data/lib/atom/element.rb +569 -161
- data/lib/atom/entry.rb +63 -89
- data/lib/atom/feed.rb +65 -67
- data/lib/atom/http.rb +421 -49
- data/lib/atom/service.rb +106 -0
- data/lib/atom/text.rb +138 -61
- data/lib/atom/tools.rb +163 -0
- data/spec/entry_spec.rb +364 -0
- data/spec/ext_spec.rb +42 -0
- data/spec/feed_spec.rb +39 -0
- data/spec/fixtures/contacts-feed.xml +34 -0
- data/spec/fixtures/entry-w-ext.xml +15 -0
- data/spec/fixtures/entry-w-xml.xml +24 -0
- data/spec/fixtures/entry.xml +42 -0
- data/spec/fixtures/feed-w-ext.xml +33 -0
- data/spec/fixtures/service-w-xhtml-ns.xml +21 -0
- data/spec/fixtures/service.xml +36 -0
- data/spec/service_spec.rb +108 -0
- data/spec/spec_helper.rb +9 -0
- data/test/conformance/order.rb +11 -10
- data/test/conformance/title.rb +9 -9
- data/test/conformance/updated.rb +2 -1
- data/test/runtests.rb +0 -0
- data/test/test_constructs.rb +78 -8
- data/test/test_feed.rb +35 -29
- data/test/test_general.rb +3 -30
- data/test/test_http.rb +248 -34
- data/test/test_protocol.rb +143 -24
- data/test/test_xml.rb +145 -53
- metadata +116 -62
- data/bin/atom-client.rb +0 -246
- data/bin/atom-server.rb~ +0 -71
- data/doc/classes/Atom/App.html +0 -217
- data/doc/classes/Atom/Entry.html +0 -365
- data/doc/classes/Atom/Feed.html +0 -585
- data/lib/atom/app.rb +0 -87
- data/lib/atom/xml.rb +0 -200
- data/lib/atom/yaml.rb +0 -101
data/lib/atom/app.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require "uri"
|
|
2
|
-
|
|
3
|
-
require "atom/http"
|
|
4
|
-
require "atom/collection"
|
|
5
|
-
|
|
6
|
-
module Atom
|
|
7
|
-
PP_NS = "http://purl.org/atom/app#"
|
|
8
|
-
|
|
9
|
-
class WrongNamespace < RuntimeError #:nodoc:
|
|
10
|
-
end
|
|
11
|
-
class WrongMimetype < RuntimeError # :nodoc:
|
|
12
|
-
end
|
|
13
|
-
class WrongResponse < RuntimeError # :nodoc:
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Atom::App represents an Atom Publishing Protocol introspection
|
|
17
|
-
# document.
|
|
18
|
-
class App
|
|
19
|
-
# collections referred to by the introspection document
|
|
20
|
-
attr_reader :collections
|
|
21
|
-
|
|
22
|
-
# retrieves and parses an Atom introspection document.
|
|
23
|
-
def initialize(introspection_url, http = Atom::HTTP.new)
|
|
24
|
-
i_url = URI.parse(introspection_url)
|
|
25
|
-
|
|
26
|
-
rxml = nil
|
|
27
|
-
|
|
28
|
-
res = http.get(i_url)
|
|
29
|
-
|
|
30
|
-
unless res.code == "200" # redirects, &c.
|
|
31
|
-
raise WrongResponse, "introspection document responded with unexpected code #{res.code}"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
unless res.content_type == "application/atomserv+xml"
|
|
35
|
-
raise WrongMimetype, "this isn't an introspection document!"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
rxml = REXML::Document.new(res.body)
|
|
39
|
-
|
|
40
|
-
unless rxml.root.namespace == PP_NS
|
|
41
|
-
raise WrongNamespace, "this isn't an introspection document!"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# TODO: expose workspaces
|
|
45
|
-
colls = REXML::XPath.match( rxml,
|
|
46
|
-
"/app:service/app:workspace/app:collection",
|
|
47
|
-
{"app" => Atom::PP_NS} )
|
|
48
|
-
|
|
49
|
-
@collections = []
|
|
50
|
-
|
|
51
|
-
colls.each do |collection|
|
|
52
|
-
# absolutize relative URLs
|
|
53
|
-
url = i_url + URI.parse(collection.attributes["href"])
|
|
54
|
-
|
|
55
|
-
coll = Atom::Collection.new(url, http)
|
|
56
|
-
|
|
57
|
-
# XXX this is a Text Construct, and should be parsed as such
|
|
58
|
-
coll.title = REXML::XPath.first( collection,
|
|
59
|
-
"./atom:title",
|
|
60
|
-
{"app" => Atom::PP_NS,
|
|
61
|
-
"atom" => Atom::NS } ).text
|
|
62
|
-
|
|
63
|
-
accepts = REXML::XPath.first( collection,
|
|
64
|
-
"./app:accept",
|
|
65
|
-
{"app" => Atom::PP_NS} )
|
|
66
|
-
coll.accepts = (accepts ? accepts.text : "entry")
|
|
67
|
-
|
|
68
|
-
@collections << coll
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
class Entry
|
|
74
|
-
# the @href of an entry's link[@rel="edit"]
|
|
75
|
-
def edit_url
|
|
76
|
-
begin
|
|
77
|
-
edit_link = self.links.find do |link|
|
|
78
|
-
link["rel"] == "edit"
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
edit_link["href"]
|
|
82
|
-
rescue
|
|
83
|
-
nil
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
data/lib/atom/xml.rb
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
require "atom/entry"
|
|
2
|
-
require "atom/feed"
|
|
3
|
-
require "uri"
|
|
4
|
-
|
|
5
|
-
# Two big, interrelated problems:
|
|
6
|
-
#
|
|
7
|
-
# * I probably shouldn't be playing around in REXML's namespace
|
|
8
|
-
# * REXML isn't a great parser, and other options would be nice
|
|
9
|
-
#
|
|
10
|
-
# This shouldn't be hard to do, it's just tedious and non-critical
|
|
11
|
-
module REXML # :nodoc: all
|
|
12
|
-
class Document
|
|
13
|
-
def to_atom_entry base = ""
|
|
14
|
-
self.root.to_atom_entry base
|
|
15
|
-
end
|
|
16
|
-
def to_atom_feed base = ""
|
|
17
|
-
self.root.to_atom_feed base
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
class Element
|
|
21
|
-
def get_atom_element name
|
|
22
|
-
XPath.first(self, "./atom:#{name}", { "atom" => Atom::NS })
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def each_atom_element name
|
|
26
|
-
XPath.each(self, "./atom:#{name}", { "atom" => Atom::NS }) do |elem|
|
|
27
|
-
yield elem
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def get_extensions
|
|
32
|
-
# XXX also look for attributes
|
|
33
|
-
children.find_all { |child| child.respond_to? :namespace and child.namespace != Atom::NS }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# get the text content of a descendant element in the Atom namespace
|
|
37
|
-
def get_atom_text name
|
|
38
|
-
elem = get_atom_element name
|
|
39
|
-
if elem
|
|
40
|
-
elem.text
|
|
41
|
-
else
|
|
42
|
-
nil
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# a workaround for the odd way in which REXML handles namespaces
|
|
47
|
-
# returns the value of the attribute +name+ that's in the same namespace as this element
|
|
48
|
-
def ns_attr name
|
|
49
|
-
if not self.prefix.empty?
|
|
50
|
-
attr = self.prefix + ":" + name
|
|
51
|
-
else
|
|
52
|
-
attr = name
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
self.attributes[attr]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def fill_text_construct(entry, name)
|
|
59
|
-
text = get_atom_element(name)
|
|
60
|
-
if text
|
|
61
|
-
type = text.ns_attr("type")
|
|
62
|
-
src = text.ns_attr("src")
|
|
63
|
-
|
|
64
|
-
if src and name == :content
|
|
65
|
-
# the only content is out of line
|
|
66
|
-
entry.send( "#{name}=".to_sym, "")
|
|
67
|
-
entry.send(name.to_sym)["src"] = src
|
|
68
|
-
elsif type == "xhtml"
|
|
69
|
-
div = XPath.first(text, "./xhtml:div", { "xhtml" => XHTML::NS })
|
|
70
|
-
unless div
|
|
71
|
-
raise "Refusing to parse type='xhtml' with no <div/> wrapper"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# content is the serialized content of the <div> wrapper
|
|
75
|
-
entry.send( "#{name}=".to_sym, div )
|
|
76
|
-
else
|
|
77
|
-
raw = text.text
|
|
78
|
-
entry.send( "#{name}=", raw )
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
if text.attributes["xml:base"]
|
|
82
|
-
entry.send(name.to_sym).base = text.attributes["xml:base"]
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
if type and type != "text"
|
|
86
|
-
entry.send(name.to_sym)["type"] = type
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def fill_elem_element(top, kind)
|
|
92
|
-
each_atom_element(kind) do |elem|
|
|
93
|
-
person = top.send("#{kind}s".to_sym).new
|
|
94
|
-
|
|
95
|
-
["name", "uri", "email"].each do |name|
|
|
96
|
-
person.send("#{name}=".to_sym, elem.get_atom_text(name))
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def fill_attr_element(top, array, kind)
|
|
102
|
-
each_atom_element(kind) do |elem|
|
|
103
|
-
thing = array.new
|
|
104
|
-
|
|
105
|
-
thing.class.attrs.each do |name,req|
|
|
106
|
-
value = elem.ns_attr name.to_s
|
|
107
|
-
if value and name == :href
|
|
108
|
-
thing[name.to_s] = (URI.parse(top.base) + value).to_s
|
|
109
|
-
elsif value
|
|
110
|
-
thing[name.to_s] = value
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# 'base' is the URI that you fetched this document from.
|
|
117
|
-
def to_atom_entry base = ""
|
|
118
|
-
unless self.name == "entry" and self.namespace == Atom::NS
|
|
119
|
-
raise TypeError, "this isn't an atom:entry! (name: #{self.name}, ns: #{self.namespace})"
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
entry = Atom::Entry.new
|
|
123
|
-
|
|
124
|
-
entry.base = if attributes["xml:base"]
|
|
125
|
-
(URI.parse(base) + attributes["xml:base"]).to_s
|
|
126
|
-
else
|
|
127
|
-
# go with the URL we were passed in
|
|
128
|
-
base
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Text constructs
|
|
132
|
-
entry.class.elements.find_all { |n,k,r| k.ancestors.member? Atom::Text }.each do |n,k,r|
|
|
133
|
-
fill_text_construct(entry, n)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
["id", "published", "updated"].each do |name|
|
|
137
|
-
entry.send("#{name}=".to_sym, get_atom_text(name))
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
["author", "contributor"].each do |type|
|
|
141
|
-
fill_elem_element(entry, type)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
{"link" => entry.links, "category" => entry.categories}.each do |k,v|
|
|
145
|
-
fill_attr_element(entry, v, k)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# extension elements
|
|
149
|
-
get_extensions.each do |elem|
|
|
150
|
-
entry.extensions << elem.dup # otherwise they get removed from the doc
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
entry
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# 'base' is the URI that you fetched this document from.
|
|
157
|
-
def to_atom_feed base = ""
|
|
158
|
-
unless self.name == "feed" and self.namespace == Atom::NS
|
|
159
|
-
raise TypeError, "this isn't an atom:feed! (name: #{self.name}, ns: #{self.namespace})"
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
feed = Atom::Feed.new
|
|
163
|
-
|
|
164
|
-
feed.base = if attributes["xml:base"]
|
|
165
|
-
(URI.parse(base) + attributes["xml:base"]).to_s
|
|
166
|
-
else
|
|
167
|
-
# go with the URL we were passed in
|
|
168
|
-
base
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Text constructs
|
|
172
|
-
feed.class.elements.find_all { |n,k,r| k.ancestors.member? Atom::Text }.each do |n,k,r|
|
|
173
|
-
fill_text_construct(feed, n)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
["id", "updated", "generator", "icon", "logo"].each do |name|
|
|
177
|
-
feed.send("#{name}=".to_sym, get_atom_text(name))
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
["author", "contributor"].each do |type|
|
|
181
|
-
fill_elem_element(feed, type)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
{"link" => feed.links, "category" => feed.categories}.each do |k,v|
|
|
185
|
-
fill_attr_element(feed, v, k)
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
each_atom_element("entry") do |elem|
|
|
189
|
-
feed << elem.to_atom_entry(feed.base)
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
get_extensions.each do |elem|
|
|
193
|
-
# have to duplicate them, or they'll get removed from the doc
|
|
194
|
-
feed.extensions << elem.dup
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
feed
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
data/lib/atom/yaml.rb
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
require "atom/entry"
|
|
2
|
-
require "yaml"
|
|
3
|
-
|
|
4
|
-
module Atom
|
|
5
|
-
class Time
|
|
6
|
-
def taguri; nil end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
class Element < Hash
|
|
10
|
-
def taguri # :nodoc:
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def to_yaml_properties # :nodoc:
|
|
14
|
-
self.class.elements.find_all do |n,k,r|
|
|
15
|
-
v = get(n)
|
|
16
|
-
v and not (v.respond_to? :empty? and v.empty?)
|
|
17
|
-
end.map { |n,k,r| "@#{n}" }
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def to_yaml( opts = {} ) # :nodoc:
|
|
21
|
-
YAML::quick_emit( object_id, opts ) do |out|
|
|
22
|
-
out.map( taguri, to_yaml_style ) do |map|
|
|
23
|
-
self.to_yaml_properties.each do |m|
|
|
24
|
-
map.add( m[1..-1], instance_variable_get( m ) )
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
class AttrEl < Atom::Element
|
|
32
|
-
def to_yaml( opts = {} )
|
|
33
|
-
YAML::quick_emit( object_id, opts ) do |out|
|
|
34
|
-
out.map( nil, to_yaml_style ) do |map|
|
|
35
|
-
self.class.attrs.each do |n,r|
|
|
36
|
-
map.add( n.to_s, self[n.to_s] ) if self[n.to_s]
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
class Text < Atom::Element
|
|
44
|
-
def taguri # :nodoc:
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def to_yaml( opts = {} ) # :nodoc:
|
|
48
|
-
YAML::quick_emit( object_id, opts ) do |out|
|
|
49
|
-
out.scalar(taguri, to_s, to_yaml_style)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
def to_yaml_style # :nodoc:
|
|
53
|
-
if @content.match("\n")
|
|
54
|
-
:fold
|
|
55
|
-
else
|
|
56
|
-
:plain
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
class Entry
|
|
62
|
-
def to_yaml_type # :nodoc:
|
|
63
|
-
'!necronomicorp.com,2006/entry'
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# parses an Atom::Entry from YAML
|
|
67
|
-
def self.from_yaml yaml
|
|
68
|
-
hash = if yaml.kind_of?(Hash); yaml else YAML.load(yaml); end
|
|
69
|
-
|
|
70
|
-
entry = Atom::Entry.new
|
|
71
|
-
|
|
72
|
-
entry.title = hash["title"]
|
|
73
|
-
entry.summary = hash["summary"]
|
|
74
|
-
|
|
75
|
-
elem_constructs = {"authors" => entry.authors, "contributors" => entry.contributors, "links" => entry.links, "categories" => entry.categories}
|
|
76
|
-
|
|
77
|
-
elem_constructs.each do |type,ary|
|
|
78
|
-
hash[type] ||= []
|
|
79
|
-
|
|
80
|
-
hash[type].each do |yelem|
|
|
81
|
-
elem = ary.new
|
|
82
|
-
|
|
83
|
-
elem.class.attrs.each do |attrb,req|
|
|
84
|
-
elem[attrb.to_s] = yelem[attrb.to_s]
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
elem.class.elements.each do |name,kind,req|
|
|
88
|
-
elem.send("#{name}=".to_sym, yelem[name.to_s])
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# this adds more categories, and could cause conflicts
|
|
94
|
-
entry.tag_with hash["tags"]
|
|
95
|
-
entry.content = hash["content"]
|
|
96
|
-
entry.content["type"] = hash["type"] if hash["type"]
|
|
97
|
-
|
|
98
|
-
entry
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|