atom-tools 0.9.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/bin/atom-client.rb +19 -23
- data/lib/atom/element.rb +1 -1
- data/lib/atom/entry.rb +5 -1
- data/lib/atom/feed.rb +10 -2
- data/lib/atom/http.rb +1 -1
- data/lib/atom/service.rb +20 -14
- data/lib/atom/text.rb +16 -9
- data/lib/atom/xml.rb +7 -3
- data/test/test_protocol.rb +9 -9
- data/test/test_xml.rb +1 -1
- metadata +5 -5
data/Rakefile
CHANGED
data/bin/atom-client.rb
CHANGED
@@ -32,6 +32,7 @@ require "atom/service"
|
|
32
32
|
require "atom/http"
|
33
33
|
|
34
34
|
require "rubygems"
|
35
|
+
require "bluecloth"
|
35
36
|
|
36
37
|
require "time"
|
37
38
|
|
@@ -62,26 +63,34 @@ class String
|
|
62
63
|
end
|
63
64
|
|
64
65
|
class Atom::Entry
|
66
|
+
def prepare_for_output
|
67
|
+
filter_hook
|
68
|
+
|
69
|
+
updated!
|
70
|
+
end
|
71
|
+
|
72
|
+
def filter_hook
|
73
|
+
# so much for actual text content...
|
74
|
+
if @content and @content["type"] == "text"
|
75
|
+
self.content = BlueCloth.new( @content.to_s ).to_html
|
76
|
+
@content["type"] = "xhtml"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
65
80
|
def edit
|
66
81
|
yaml = YAML.load(self.to_yaml)
|
67
82
|
|
68
|
-
#
|
83
|
+
# human readability
|
69
84
|
yaml.delete "id"
|
70
85
|
|
71
86
|
if yaml["links"]
|
72
|
-
yaml["links"].find_all { |l| ["
|
87
|
+
yaml["links"].find_all { |l| l["rel"] == "alternate" or l["rel"] == "edit" }.each { |l| yaml["links"].delete(l) }
|
73
88
|
yaml.delete("links") if yaml["links"].empty?
|
74
89
|
end
|
75
|
-
|
76
|
-
if self.content
|
77
|
-
yaml["content"] = $text_filter_before.call(self.content.html)
|
78
|
-
end
|
79
|
-
|
90
|
+
|
80
91
|
new_yaml, entry = write_entry(yaml.to_yaml)
|
81
92
|
|
82
|
-
# restore deleted bits
|
83
93
|
entry.id = self.id
|
84
|
-
self.links.each { |l| entry.links << l }
|
85
94
|
|
86
95
|
[new_yaml["slug"], entry]
|
87
96
|
end
|
@@ -136,12 +145,7 @@ def write_entry(editstring = "")
|
|
136
145
|
|
137
146
|
entry = Atom::Entry.from_yaml yaml
|
138
147
|
|
139
|
-
|
140
|
-
entry.content, type = $text_filter_after.call(yaml["content"])
|
141
|
-
entry.content["type"] = (type || "text")
|
142
|
-
end
|
143
|
-
|
144
|
-
entry.updated!
|
148
|
+
entry.prepare_for_output
|
145
149
|
|
146
150
|
# XXX disabled until the APP WG can decide what a valid entry is
|
147
151
|
=begin
|
@@ -183,14 +187,6 @@ url = if options[:url]
|
|
183
187
|
options[:url]
|
184
188
|
else
|
185
189
|
yaml = YAML.load(File.read("#{ENV["HOME"]}/.atom-client"))
|
186
|
-
|
187
|
-
# leave it the way it came in
|
188
|
-
i = Proc.new { |x| x }
|
189
|
-
$text_filter_before = eval(yaml["text_filter_before"])
|
190
|
-
$text_filter_before ||= i
|
191
|
-
$text_filter_after = eval(yaml["text_filter_after"])
|
192
|
-
$text_filter_after ||= i
|
193
|
-
|
194
190
|
collections = yaml["collections"]
|
195
191
|
|
196
192
|
puts "which collection?"
|
data/lib/atom/element.rb
CHANGED
@@ -170,7 +170,7 @@ module Atom # :nodoc:
|
|
170
170
|
|
171
171
|
self.class.attrs.each do |name,req|
|
172
172
|
value = self[name.to_s]
|
173
|
-
elem.attributes[name.to_s] = value if value
|
173
|
+
elem.attributes[name.to_s] = value.to_s if value
|
174
174
|
end
|
175
175
|
|
176
176
|
self.extensions.children.each do |element|
|
data/lib/atom/entry.rb
CHANGED
@@ -66,7 +66,11 @@ module Atom
|
|
66
66
|
elsif xml.respond_to? :read
|
67
67
|
self.parse(xml.read)
|
68
68
|
else
|
69
|
-
|
69
|
+
begin
|
70
|
+
REXML::Document.new(xml.to_s).to_atom_entry(base)
|
71
|
+
rescue REXML::ParseException
|
72
|
+
raise Atom::ParseError
|
73
|
+
end
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
data/lib/atom/feed.rb
CHANGED
@@ -73,7 +73,11 @@ module Atom
|
|
73
73
|
elsif xml.respond_to? :read
|
74
74
|
self.parse(xml.read)
|
75
75
|
else
|
76
|
-
|
76
|
+
begin
|
77
|
+
REXML::Document.new(xml.to_s).to_atom_feed(base)
|
78
|
+
rescue REXML::ParseException
|
79
|
+
raise Atom::ParseError
|
80
|
+
end
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
@@ -96,6 +100,10 @@ module Atom
|
|
96
100
|
@entries.each &block
|
97
101
|
end
|
98
102
|
|
103
|
+
def empty?
|
104
|
+
@entries.empty?
|
105
|
+
end
|
106
|
+
|
99
107
|
# gets everything in the logical feed (could be a lot of stuff)
|
100
108
|
# (see <http://www.ietf.org/internet-drafts/draft-nottingham-atompub-feed-history-05.txt>)
|
101
109
|
def get_everything!
|
@@ -130,7 +138,7 @@ module Atom
|
|
130
138
|
|
131
139
|
# like #merge, but in place
|
132
140
|
def merge! other_feed
|
133
|
-
[:id, :title, :subtitle, :updated, :rights].each { |p|
|
141
|
+
[:id, :title, :subtitle, :updated, :rights, :logo, :icon].each { |p|
|
134
142
|
self.send("#{p}=", other_feed.send("#{p}"))
|
135
143
|
}
|
136
144
|
|
data/lib/atom/http.rb
CHANGED
data/lib/atom/service.rb
CHANGED
@@ -5,11 +5,6 @@ require "atom/element"
|
|
5
5
|
require "atom/collection"
|
6
6
|
|
7
7
|
module Atom
|
8
|
-
class WrongNamespace < RuntimeError #:nodoc:
|
9
|
-
end
|
10
|
-
class WrongResponse < RuntimeError # :nodoc:
|
11
|
-
end
|
12
|
-
|
13
8
|
# an Atom::Workspace has a #title (Atom::Text) and #collections, an Array of Atom::Collection s
|
14
9
|
class Workspace < Atom::Element
|
15
10
|
element :collections, Atom::Multiple(Atom::Collection)
|
@@ -23,7 +18,11 @@ module Atom
|
|
23
18
|
elsif xml.is_a? REXML::Element
|
24
19
|
xml
|
25
20
|
else
|
26
|
-
|
21
|
+
begin
|
22
|
+
REXML::Document.new(xml)
|
23
|
+
rescue REXML::ParseException
|
24
|
+
raise Atom::ParseError
|
25
|
+
end
|
27
26
|
end
|
28
27
|
|
29
28
|
xml.fill_text_construct(ws, "title")
|
@@ -42,7 +41,12 @@ module Atom
|
|
42
41
|
"./app:accept",
|
43
42
|
{"app" => Atom::PP_NS} )
|
44
43
|
|
45
|
-
|
44
|
+
accepts = []
|
45
|
+
REXML::XPath.each(col_el, "./app:accept", {"app" => Atom::PP_NS}) do |a|
|
46
|
+
accepts << a.texts.join
|
47
|
+
end
|
48
|
+
|
49
|
+
coll.accepts = (accepts.empty? ? ["application/atom+xml;type=entry"] : accepts)
|
46
50
|
|
47
51
|
ws.collections << coll
|
48
52
|
end
|
@@ -62,16 +66,18 @@ module Atom
|
|
62
66
|
self.collections.each do |coll|
|
63
67
|
el = REXML::Element.new "collection"
|
64
68
|
|
65
|
-
el.attributes["href"] = coll.uri
|
69
|
+
el.attributes["href"] = coll.uri.to_s
|
66
70
|
|
67
71
|
title = coll.title.to_element
|
68
72
|
title.name = "atom:title"
|
69
73
|
el << title
|
70
74
|
|
71
75
|
unless coll.accepts.nil?
|
72
|
-
accepts
|
73
|
-
|
74
|
-
|
76
|
+
coll.accepts.each do |acc|
|
77
|
+
accept = REXML::Element.new "accept"
|
78
|
+
accept.text = acc
|
79
|
+
el << accept
|
80
|
+
end
|
75
81
|
end
|
76
82
|
|
77
83
|
root << el
|
@@ -102,8 +108,8 @@ module Atom
|
|
102
108
|
res = @http.get(base, "Accept" => "application/atomsvc+xml")
|
103
109
|
res.validate_content_type(["application/atomsvc+xml"])
|
104
110
|
|
105
|
-
unless res.code == "200"
|
106
|
-
raise
|
111
|
+
unless res.code == "200"
|
112
|
+
raise Atom::HTTPException, "Unexpected HTTP response code: #{res.code}"
|
107
113
|
end
|
108
114
|
|
109
115
|
parse(res.body, base)
|
@@ -128,7 +134,7 @@ module Atom
|
|
128
134
|
end
|
129
135
|
|
130
136
|
unless rxml.root.namespace == PP_NS
|
131
|
-
raise
|
137
|
+
raise Atom::ParseError, "this isn't an atom service document! (wrong namespace: #{rxml.root.namespace})"
|
132
138
|
end
|
133
139
|
|
134
140
|
REXML::XPath.match( rxml, "/app:service/app:workspace", {"app" => Atom::PP_NS} ).each do |ws_el|
|
data/lib/atom/text.rb
CHANGED
@@ -105,16 +105,14 @@ module Atom
|
|
105
105
|
end
|
106
106
|
|
107
107
|
# this should be done via inheritance
|
108
|
-
|
109
|
-
c = convert_contents e
|
108
|
+
c = convert_contents e
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
110
|
+
if c.is_a? String
|
111
|
+
e.text = c
|
112
|
+
elsif c.is_a? REXML::Element
|
113
|
+
e << c.dup
|
114
|
+
else
|
115
|
+
raise RuntimeError, "atom:#{local_name} can't contain type #{@content.class}"
|
118
116
|
end
|
119
117
|
|
120
118
|
e
|
@@ -176,6 +174,15 @@ module Atom
|
|
176
174
|
end
|
177
175
|
end
|
178
176
|
|
177
|
+
def to_element
|
178
|
+
if self["src"]
|
179
|
+
element_super = Element.instance_method(:to_element)
|
180
|
+
return element_super.bind(self).call
|
181
|
+
end
|
182
|
+
|
183
|
+
super
|
184
|
+
end
|
185
|
+
|
179
186
|
private
|
180
187
|
def valid_type? type
|
181
188
|
super or type.match(/\//)
|
data/lib/atom/xml.rb
CHANGED
@@ -2,6 +2,8 @@ require "atom/entry"
|
|
2
2
|
require "atom/feed"
|
3
3
|
require "uri"
|
4
4
|
|
5
|
+
class Atom::ParseError < StandardError; end
|
6
|
+
|
5
7
|
# Two big, interrelated problems:
|
6
8
|
#
|
7
9
|
# * I probably shouldn't be playing around in REXML's namespace
|
@@ -11,9 +13,11 @@ require "uri"
|
|
11
13
|
module REXML # :nodoc: all
|
12
14
|
class Document
|
13
15
|
def to_atom_entry base = ""
|
16
|
+
raise Atom::ParseError unless self.root
|
14
17
|
self.root.to_atom_entry base
|
15
18
|
end
|
16
19
|
def to_atom_feed base = ""
|
20
|
+
raise Atom::ParseError unless self.root
|
17
21
|
self.root.to_atom_feed base
|
18
22
|
end
|
19
23
|
end
|
@@ -79,7 +83,7 @@ module REXML # :nodoc: all
|
|
79
83
|
elsif type == "xhtml"
|
80
84
|
div = XPath.first(text, "./xhtml:div", { "xhtml" => XHTML::NS })
|
81
85
|
unless div
|
82
|
-
raise "Refusing to parse type='xhtml' with no <div/> wrapper"
|
86
|
+
raise Atom::ParseError, "Refusing to parse type='xhtml' with no <div/> wrapper"
|
83
87
|
end
|
84
88
|
|
85
89
|
# content is the serialized content of the <div> wrapper
|
@@ -131,7 +135,7 @@ module REXML # :nodoc: all
|
|
131
135
|
# 'base' is the URI that you fetched this document from.
|
132
136
|
def to_atom_entry base = ""
|
133
137
|
unless self.name == "entry" and self.namespace == Atom::NS
|
134
|
-
raise
|
138
|
+
raise Atom::ParseError, "this isn't an atom:entry! (name: #{self.name}, ns: #{self.namespace})"
|
135
139
|
end
|
136
140
|
|
137
141
|
entry = Atom::Entry.new
|
@@ -168,7 +172,7 @@ module REXML # :nodoc: all
|
|
168
172
|
# 'base' is the URI that you fetched this document from.
|
169
173
|
def to_atom_feed base = ""
|
170
174
|
unless self.name == "feed" and self.namespace == Atom::NS
|
171
|
-
raise
|
175
|
+
raise Atom::ParseError, "this isn't an atom:feed! (name: #{self.name}, ns: #{self.namespace})"
|
172
176
|
end
|
173
177
|
|
174
178
|
feed = Atom::Feed.new
|
data/test/test_protocol.rb
CHANGED
@@ -43,14 +43,14 @@ END
|
|
43
43
|
assert_equal "My Blog", ws.title.to_s
|
44
44
|
|
45
45
|
coll = ws.collections.first
|
46
|
-
assert_equal URI.parse("http://example.org/myblog/entries"), coll.uri
|
47
|
-
assert_equal "Entries", coll.title.to_s
|
48
|
-
assert_equal "entry", coll.accepts
|
46
|
+
assert_equal URI.parse("http://example.org/myblog/entries"), coll.uri
|
47
|
+
assert_equal "Entries", coll.title.to_s
|
48
|
+
assert_equal ["application/atom+xml;type=entry"], coll.accepts
|
49
49
|
|
50
50
|
coll = ws.collections.last
|
51
|
-
assert_equal URI.parse("http://example.org/myblog/fotes"), coll.uri
|
52
|
-
assert_equal "Photos", coll.title.to_s
|
53
|
-
assert_equal "image/*", coll.accepts
|
51
|
+
assert_equal URI.parse("http://example.org/myblog/fotes"), coll.uri
|
52
|
+
assert_equal "Photos", coll.title.to_s
|
53
|
+
assert_equal ["image/*"], coll.accepts
|
54
54
|
|
55
55
|
http = service.instance_variable_get(:@http)
|
56
56
|
assert_instance_of Atom::HTTP, http
|
@@ -74,7 +74,7 @@ END
|
|
74
74
|
|
75
75
|
coll = Atom::Collection.new "http://example.org/audio"
|
76
76
|
coll.title = "Audio"
|
77
|
-
coll.accepts = "audio/*"
|
77
|
+
coll.accepts = ["audio/*"]
|
78
78
|
ws.collections << coll
|
79
79
|
|
80
80
|
nses = { "app" => Atom::PP_NS, "atom" => Atom::NS }
|
@@ -102,7 +102,7 @@ END
|
|
102
102
|
title = REXML::XPath.first(entries, "./atom:title", nses)
|
103
103
|
assert_equal "Entries", title.text
|
104
104
|
|
105
|
-
accepts = REXML::XPath.first(entries, "./app:
|
105
|
+
accepts = REXML::XPath.first(entries, "./app:accept", nses)
|
106
106
|
assert_nil accepts
|
107
107
|
|
108
108
|
audio = colls.last
|
@@ -112,7 +112,7 @@ END
|
|
112
112
|
title = REXML::XPath.first(audio, "./atom:title", nses)
|
113
113
|
assert_equal "Audio", title.text
|
114
114
|
|
115
|
-
accepts = REXML::XPath.first(audio, "./app:
|
115
|
+
accepts = REXML::XPath.first(audio, "./app:accept", nses)
|
116
116
|
assert_equal "audio/*", accepts.text
|
117
117
|
end
|
118
118
|
|
data/test/test_xml.rb
CHANGED
@@ -146,7 +146,7 @@ class AtomTest < Test::Unit::TestCase
|
|
146
146
|
assert entry.draft
|
147
147
|
end
|
148
148
|
|
149
|
-
def
|
149
|
+
def test_extensive_entry_parsing
|
150
150
|
str = '<entry xmlns="http://www.w3.org/2005/Atom">
|
151
151
|
<title>Atom draft-07 snapshot</title>
|
152
152
|
<link rel="alternate" type="text/html"
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: atom-tools
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2007-11-10 00:00:00 -07:00
|
8
8
|
summary: Tools for working with Atom Entries, Feeds and Collections
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -39,14 +39,14 @@ files:
|
|
39
39
|
- test/test_feed.rb
|
40
40
|
- test/test_protocol.rb
|
41
41
|
- test/conformance
|
42
|
-
- test/test_xml.rb
|
43
|
-
- test/test_http.rb
|
44
|
-
- test/test_general.rb
|
45
42
|
- test/conformance/updated.rb
|
46
43
|
- test/conformance/xmlnamespace.rb
|
47
44
|
- test/conformance/title.rb
|
48
45
|
- test/conformance/order.rb
|
49
46
|
- test/conformance/xhtmlcontentdiv.rb
|
47
|
+
- test/test_xml.rb
|
48
|
+
- test/test_http.rb
|
49
|
+
- test/test_general.rb
|
50
50
|
- lib/atom
|
51
51
|
- lib/atom/yaml.rb
|
52
52
|
- lib/atom/feed.rb
|