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/entry.rb
CHANGED
|
@@ -3,8 +3,37 @@ require "rexml/document"
|
|
|
3
3
|
require "atom/element"
|
|
4
4
|
require "atom/text"
|
|
5
5
|
|
|
6
|
+
require 'atom/feed'
|
|
7
|
+
|
|
6
8
|
module Atom
|
|
7
|
-
|
|
9
|
+
# this is just a forward declaration since atom/entry includes atom/feed and vice-versa.
|
|
10
|
+
class Feed < Atom::Element # :nodoc:
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Source < Atom::Feed
|
|
14
|
+
is_atom_element :source
|
|
15
|
+
|
|
16
|
+
# TODO: this shouldn't be necessary, but on_init doesn't get inherited the
|
|
17
|
+
# way I would like it to.
|
|
18
|
+
@on_init = Atom::Feed.instance_variable_get '@on_init'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Control < Atom::Element
|
|
22
|
+
attr_accessor :draft
|
|
23
|
+
|
|
24
|
+
is_element PP_NS, :control
|
|
25
|
+
|
|
26
|
+
on_parse [PP_NS, 'draft'] do |e,x|
|
|
27
|
+
e.set(:draft, x.text == 'yes')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
on_build do |e,x|
|
|
31
|
+
unless (v = e.get(:draft)).nil?
|
|
32
|
+
el = e.append_elem(x, ['app', PP_NS], 'draft')
|
|
33
|
+
el.text = (v ? 'yes' : 'no')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
8
37
|
|
|
9
38
|
# An individual entry in a feed. As an Atom::Element, it can be
|
|
10
39
|
# manipulated using accessors for each of its child elements. You
|
|
@@ -26,110 +55,55 @@ module Atom
|
|
|
26
55
|
# each of which is an Array of its respective type and can be used
|
|
27
56
|
# thusly:
|
|
28
57
|
#
|
|
29
|
-
# author = entry.authors.new
|
|
30
|
-
#
|
|
58
|
+
# author = entry.authors.new :name => "Captain Kangaroo", :email => "kanga@example.net"
|
|
59
|
+
#
|
|
31
60
|
class Entry < Atom::Element
|
|
61
|
+
is_atom_element :entry
|
|
62
|
+
|
|
32
63
|
# the master list of standard children and the types they map to
|
|
33
|
-
|
|
34
|
-
element :title, Atom::Text, true
|
|
35
|
-
element :content, Atom::Content, true
|
|
36
|
-
|
|
37
|
-
element :rights, Atom::Text
|
|
38
|
-
# element :source, Atom::Feed # complicated, eg. serialization
|
|
39
|
-
|
|
40
|
-
element :authors, Atom::Multiple(Atom::Author)
|
|
41
|
-
element :contributors, Atom::Multiple(Atom::Contributor)
|
|
42
|
-
|
|
43
|
-
element :categories, Atom::Multiple(Atom::Category)
|
|
44
|
-
element :links, Atom::Multiple(Atom::Link)
|
|
45
|
-
|
|
46
|
-
element :published, Atom::Time
|
|
47
|
-
element :updated, Atom::Time, true
|
|
48
|
-
|
|
49
|
-
element :summary, Atom::Text
|
|
50
|
-
|
|
51
|
-
def initialize # :nodoc:
|
|
52
|
-
super "entry"
|
|
53
|
-
|
|
54
|
-
# XXX I don't think I've ever actually used this
|
|
55
|
-
yield self if block_given?
|
|
56
|
-
end
|
|
64
|
+
atom_string :id
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
xml.to_atom_entry(base)
|
|
62
|
-
else
|
|
63
|
-
REXML::Document.new(xml.to_s).to_atom_entry(base)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
+
atom_element :title, Atom::Title
|
|
67
|
+
atom_element :summary, Atom::Summary
|
|
68
|
+
atom_element :content, Atom::Content
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
"#<Atom::Entry id:'#{self.id}'>"
|
|
69
|
-
end
|
|
70
|
+
atom_element :rights, Atom::Rights
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
def updated!
|
|
73
|
-
self.updated = Time.now
|
|
74
|
-
end
|
|
72
|
+
atom_element :source, Atom::Source
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
atom_time :published
|
|
75
|
+
atom_time :updated
|
|
76
|
+
time ['app', PP_NS], :edited
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
end
|
|
83
|
-
end
|
|
78
|
+
atom_elements :author, :authors, Atom::Author
|
|
79
|
+
atom_elements :contributor, :contributors, Atom::Contributor
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
=begin
|
|
87
|
-
# tests the entry's validity
|
|
88
|
-
def valid?
|
|
89
|
-
self.class.required.each do |element|
|
|
90
|
-
unless instance_variable_get "@#{element}"
|
|
91
|
-
return [ false, "required element atom:#{element} missing" ]
|
|
92
|
-
end
|
|
93
|
-
end
|
|
81
|
+
element ['app', PP_NS], :control, Atom::Control
|
|
94
82
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
end
|
|
83
|
+
include HasCategories
|
|
84
|
+
include HasLinks
|
|
98
85
|
|
|
99
|
-
|
|
100
|
-
link["rel"] == "alternate"
|
|
101
|
-
end
|
|
86
|
+
atom_link :edit_url, :rel => 'edit'
|
|
102
87
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
def inspect # :nodoc:
|
|
89
|
+
"#<Atom::Entry id:'#{self.id}'>"
|
|
90
|
+
end
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
x["type"] == link["type"] and
|
|
111
|
-
x["hreflang"] == link["hreflang"]
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
return [ false, 'more than one atom:link with a rel attribute value of "alternate" that has the same combination of type and hreflang attribute values.' ]
|
|
115
|
-
end
|
|
116
|
-
end
|
|
92
|
+
def draft
|
|
93
|
+
control and control.draft
|
|
94
|
+
end
|
|
117
95
|
|
|
118
|
-
|
|
96
|
+
alias :draft? :draft
|
|
119
97
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
98
|
+
def draft!
|
|
99
|
+
self.draft = true
|
|
100
|
+
end
|
|
123
101
|
|
|
124
|
-
|
|
125
|
-
|
|
102
|
+
def draft= is_draft
|
|
103
|
+
unless control
|
|
104
|
+
instance_variable_set '@control', Atom::Control.new
|
|
126
105
|
end
|
|
127
|
-
|
|
128
|
-
true
|
|
106
|
+
control.draft = is_draft
|
|
129
107
|
end
|
|
130
|
-
=end
|
|
131
108
|
end
|
|
132
109
|
end
|
|
133
|
-
|
|
134
|
-
# this is here solely so that you don't have to require it
|
|
135
|
-
require "atom/xml"
|
data/lib/atom/feed.rb
CHANGED
|
@@ -5,8 +5,10 @@ require "atom/entry"
|
|
|
5
5
|
require "atom/http"
|
|
6
6
|
|
|
7
7
|
module Atom
|
|
8
|
-
|
|
8
|
+
# this is just a forward declaration since atom/entry includes atom/feed and vice-versa.
|
|
9
|
+
class Entry < Atom::Element # :nodoc:
|
|
9
10
|
end
|
|
11
|
+
|
|
10
12
|
class FeedGone < RuntimeError # :nodoc:
|
|
11
13
|
end
|
|
12
14
|
|
|
@@ -23,44 +25,47 @@ module Atom
|
|
|
23
25
|
# generator:: the agent used to generate a feed
|
|
24
26
|
# icon:: an IRI identifying an icon which visually identifies a feed (1:1 aspect ratio, looks OK small)
|
|
25
27
|
# logo:: an IRI identifying an image which visually identifies a feed (2:1 aspect ratio)
|
|
26
|
-
# rights:: rights held in and over a feed (Atom::Text)
|
|
28
|
+
# rights:: rights held in and over a feed (Atom::Text)
|
|
27
29
|
#
|
|
28
|
-
# There are also +links+, +categories+, +authors+, +contributors+
|
|
30
|
+
# There are also +links+, +categories+, +authors+, +contributors+
|
|
29
31
|
# and +entries+, each of which is an Array of its respective type and
|
|
30
32
|
# can be used thusly:
|
|
31
33
|
#
|
|
32
34
|
# entry = feed.entries.new
|
|
33
35
|
# entry.title = "blah blah blah"
|
|
36
|
+
#
|
|
34
37
|
class Feed < Atom::Element
|
|
38
|
+
is_atom_element :feed
|
|
39
|
+
|
|
35
40
|
attr_reader :uri
|
|
36
41
|
|
|
37
42
|
# the Atom::Feed pointed to by link[@rel='previous']
|
|
38
43
|
attr_reader :prev
|
|
39
44
|
# the Atom::Feed pointed to by link[@rel='next']
|
|
40
45
|
attr_reader :next
|
|
41
|
-
|
|
46
|
+
|
|
42
47
|
# conditional get information from the last fetch
|
|
43
48
|
attr_reader :etag, :last_modified
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
atom_string :id
|
|
51
|
+
atom_element :title, Atom::Title
|
|
52
|
+
atom_element :subtitle, Atom::Subtitle
|
|
53
|
+
|
|
54
|
+
atom_time :updated
|
|
55
|
+
|
|
56
|
+
include HasLinks
|
|
57
|
+
include HasCategories
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
atom_elements :author, :authors, Atom::Author
|
|
60
|
+
atom_elements :contributor, :contributors, Atom::Contributor
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
atom_string :generator # XXX with uri and version attributes!
|
|
63
|
+
atom_string :icon
|
|
64
|
+
atom_string :logo
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
element :icon, String
|
|
59
|
-
element :logo, String
|
|
66
|
+
atom_element :rights, Atom::Rights
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
element :entries, Atom::Multiple(Atom::Entry)
|
|
68
|
+
atom_elements :entry, :entries, Atom::Entry
|
|
64
69
|
|
|
65
70
|
include Enumerable
|
|
66
71
|
|
|
@@ -68,15 +73,6 @@ module Atom
|
|
|
68
73
|
"<#{@uri} entries: #{entries.length} title='#{title}'>"
|
|
69
74
|
end
|
|
70
75
|
|
|
71
|
-
# parses XML fetched from +base+ into an Atom::Feed
|
|
72
|
-
def self.parse xml, base = ""
|
|
73
|
-
if xml.respond_to? :to_atom_entry
|
|
74
|
-
xml.to_atom_feed(base)
|
|
75
|
-
else
|
|
76
|
-
REXML::Document.new(xml.to_s).to_atom_feed(base)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
76
|
# Create a new Feed that can be found at feed_uri and retrieved
|
|
81
77
|
# using an Atom::HTTP object http
|
|
82
78
|
def initialize feed_uri = nil, http = Atom::HTTP.new
|
|
@@ -88,7 +84,7 @@ module Atom
|
|
|
88
84
|
self.base = feed_uri
|
|
89
85
|
end
|
|
90
86
|
|
|
91
|
-
super
|
|
87
|
+
super()
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
# iterates over a feed's entries
|
|
@@ -96,11 +92,15 @@ module Atom
|
|
|
96
92
|
@entries.each &block
|
|
97
93
|
end
|
|
98
94
|
|
|
95
|
+
def empty?
|
|
96
|
+
@entries.empty?
|
|
97
|
+
end
|
|
98
|
+
|
|
99
99
|
# gets everything in the logical feed (could be a lot of stuff)
|
|
100
|
-
# (see
|
|
100
|
+
# (see RFC 5005)
|
|
101
101
|
def get_everything!
|
|
102
102
|
self.update!
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
prev = @prev
|
|
105
105
|
while prev
|
|
106
106
|
prev.update!
|
|
@@ -128,18 +128,22 @@ module Atom
|
|
|
128
128
|
end
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
# like #merge, but in place
|
|
131
|
+
# like #merge, but in place
|
|
132
132
|
def merge! other_feed
|
|
133
|
-
[:id, :title, :subtitle, :updated, :rights].each
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
[:id, :title, :subtitle, :updated, :rights, :logo, :icon].each do |p|
|
|
134
|
+
if (v = other_feed.get(p))
|
|
135
|
+
set p, v
|
|
136
|
+
end
|
|
137
|
+
end
|
|
136
138
|
|
|
137
139
|
[:links, :categories, :authors, :contributors].each do |p|
|
|
138
|
-
other_feed.
|
|
139
|
-
|
|
140
|
+
other_feed.get(p).each do |e|
|
|
141
|
+
get(p) << e
|
|
140
142
|
end
|
|
141
143
|
end
|
|
142
144
|
|
|
145
|
+
@extensions = other_feed.extensions
|
|
146
|
+
|
|
143
147
|
merge_entries! other_feed
|
|
144
148
|
end
|
|
145
149
|
|
|
@@ -149,22 +153,20 @@ module Atom
|
|
|
149
153
|
feed = self.clone
|
|
150
154
|
|
|
151
155
|
feed.merge! other_feed
|
|
152
|
-
|
|
156
|
+
|
|
153
157
|
feed
|
|
154
158
|
end
|
|
155
159
|
|
|
156
160
|
# fetches this feed's URL, parses the result and #merge!s
|
|
157
161
|
# changes, new entries, &c.
|
|
162
|
+
#
|
|
163
|
+
# (note that this is different from Atom::Entry#updated!
|
|
158
164
|
def update!
|
|
159
165
|
raise(RuntimeError, "can't fetch without a uri.") unless @uri
|
|
160
|
-
|
|
161
|
-
headers = {}
|
|
162
|
-
headers["If-None-Match"] = @etag if @etag
|
|
163
|
-
headers["If-Modified-Since"] = @last_modified if @last_modified
|
|
164
166
|
|
|
165
|
-
res = @http.get(@uri,
|
|
167
|
+
res = @http.get(@uri, "Accept" => "application/atom+xml")
|
|
166
168
|
|
|
167
|
-
if res
|
|
169
|
+
if @etag and res['etag'] == @etag
|
|
168
170
|
# we're already all up to date
|
|
169
171
|
return self
|
|
170
172
|
elsif res.code == "410"
|
|
@@ -172,44 +174,43 @@ module Atom
|
|
|
172
174
|
elsif res.code != "200"
|
|
173
175
|
raise Atom::HTTPException, "Unexpected HTTP response code: #{res.code}"
|
|
174
176
|
end
|
|
175
|
-
|
|
176
|
-
unless res.content_type.match(/^application\/atom\+xml/)
|
|
177
|
-
raise Atom::HTTPException, "Unexpected HTTP response Content-Type: #{res.content_type} (wanted application/atom+xml)"
|
|
178
|
-
end
|
|
179
177
|
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
# we'll be forgiving about feed content types.
|
|
179
|
+
res.validate_content_type(["application/atom+xml",
|
|
180
|
+
"application/xml",
|
|
181
|
+
"text/xml"])
|
|
182
|
+
|
|
183
|
+
@etag = res["ETag"] if res["ETag"]
|
|
182
184
|
|
|
183
185
|
xml = res.body
|
|
184
186
|
|
|
185
187
|
coll = REXML::Document.new(xml)
|
|
186
188
|
|
|
187
|
-
|
|
189
|
+
update_el = REXML::XPath.first(coll, "/atom:feed/atom:updated", { "atom" => Atom::NS } )
|
|
188
190
|
|
|
189
|
-
# the feed hasn't been updated, don't
|
|
190
|
-
if self.updated and self.updated >=
|
|
191
|
+
# the feed hasn't been updated, don't do anything.
|
|
192
|
+
if update_el and self.updated and self.updated >= Time.parse(update_el.text)
|
|
191
193
|
return self
|
|
192
194
|
end
|
|
193
195
|
|
|
194
|
-
coll =
|
|
196
|
+
coll = self.class.parse(coll.root, self.base.to_s)
|
|
195
197
|
merge! coll
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
abs_uri = @uri + link["href"]
|
|
200
|
-
@next = Feed.new(abs_uri.to_s, @http)
|
|
198
|
+
|
|
199
|
+
if abs_uri = next_link
|
|
200
|
+
@next = self.class.new(abs_uri.to_s, @http)
|
|
201
201
|
end
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
abs_uri = @uri + link["href"]
|
|
206
|
-
@prev = Feed.new(abs_uri.to_s, @http)
|
|
203
|
+
if abs_uri = previous_link
|
|
204
|
+
@prev = self.class.new(abs_uri.to_s, @http)
|
|
207
205
|
end
|
|
208
206
|
|
|
209
207
|
self
|
|
210
208
|
end
|
|
211
209
|
|
|
212
|
-
|
|
210
|
+
atom_link :previous_link, :rel => 'previous'
|
|
211
|
+
atom_link :next_link, :rel => 'next'
|
|
212
|
+
|
|
213
|
+
# adds an entry to this feed. if this feed already contains an
|
|
213
214
|
# entry with the same id, the newest one is used.
|
|
214
215
|
def << entry
|
|
215
216
|
existing = entries.find do |e|
|
|
@@ -224,6 +225,3 @@ module Atom
|
|
|
224
225
|
end
|
|
225
226
|
end
|
|
226
227
|
end
|
|
227
|
-
|
|
228
|
-
# this is here solely so you don't have to require it
|
|
229
|
-
require "atom/xml"
|