ratom 0.2.2 → 0.3.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/History.txt +6 -0
- data/README.txt +40 -1
- data/lib/atom.rb +52 -8
- data/lib/atom/pub.rb +0 -1
- data/lib/atom/version.rb +2 -2
- data/lib/atom/xml/parser.rb +19 -1
- data/spec/atom_spec.rb +130 -75
- metadata +4 -4
data/History.txt
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 0.3.0 2008-04-02
|
2
|
+
|
3
|
+
* Raise Atom::Serialization error when you try and serialize non-utf8 content.
|
4
|
+
* Support reading simple extension elements, see README.txt.
|
5
|
+
* Support writing simple extension elements, see README.txt.
|
6
|
+
|
1
7
|
== 0.2.2 2008-02-10
|
2
8
|
|
3
9
|
* Removed dependency on ActiveSupport.
|
data/README.txt
CHANGED
@@ -97,6 +97,45 @@ required to let us update to the entry. For example, lets change the content an
|
|
97
97
|
|
98
98
|
You can also delete an entry using the <tt>destroy!</tt> method, but we won't do that will we?.
|
99
99
|
|
100
|
+
=== Extension elements
|
101
|
+
|
102
|
+
As of version 0.3.0, rAtom support simple extension elements on feeds and entries. As defined in the Atom Syndication Format,
|
103
|
+
simple extension elements consist of XML elements from a non-Atom namespace that have no attributes or child elements, i.e.
|
104
|
+
they are empty or only contain text content. These elements are treated as a name value pair where the element namespace
|
105
|
+
and local name make up the key and the content of the element is the value, empty elements will be treated as an empty string.
|
106
|
+
|
107
|
+
To access extension elements use the [] method on the Feed or Entry. For example, if we are parsing the follow Atom document
|
108
|
+
with extensions:
|
109
|
+
|
110
|
+
<?xml version="1.0"?>
|
111
|
+
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:ex="http://example.org">
|
112
|
+
<title>Feed with extensions</title>
|
113
|
+
<ex:myelement>Something important</ex:myelement>
|
114
|
+
</feed>
|
115
|
+
|
116
|
+
We could then access the extension element on the feed using:
|
117
|
+
|
118
|
+
> feed["http://example.org", "myelement"]
|
119
|
+
=> ["Something important"]
|
120
|
+
|
121
|
+
Note that the return value is an array. This is because XML allows multiple instances of the element.
|
122
|
+
|
123
|
+
To set an extension element you append to the array:
|
124
|
+
|
125
|
+
> feed['http://example.org', 'myelement'] << 'Something less important'
|
126
|
+
=> ["Something important", "Something less important"]
|
127
|
+
|
128
|
+
You can then call to_xml and rAtom will serialize the extension elements into xml.
|
129
|
+
|
130
|
+
> puts feed.to_xml
|
131
|
+
<?xml version="1.0"?>
|
132
|
+
<feed xmlns="http://www.w3.org/2005/Atom">
|
133
|
+
<myelement xmlns="http://example.org">Something important</myelement>
|
134
|
+
<myelement xmlns="http://example.org">Something less important</myelement>
|
135
|
+
</feed>
|
136
|
+
|
137
|
+
Notice that the output repeats the xmlns attribute for each of the extensions, this is semantically the same the input XML, just a bit
|
138
|
+
ugly. It seems to be a limitation of the libxml-Ruby API. But if anyone knows a work around I'd gladly accept a patch (or even advice).
|
100
139
|
|
101
140
|
== TODO
|
102
141
|
|
@@ -105,8 +144,8 @@ You can also delete an entry using the <tt>destroy!</tt> method, but we won't do
|
|
105
144
|
* Examples of editing existing entries.
|
106
145
|
* All my tests have been against internal systems, I'd really like feedback from those who have tried rAtom using existing blog software that supports APP.
|
107
146
|
* Handle all base uri tests.
|
108
|
-
* What to do with extension elements?
|
109
147
|
* Add slug support.
|
148
|
+
* Handle HTTP basic authentication.
|
110
149
|
|
111
150
|
== Source Code
|
112
151
|
|
data/lib/atom.rb
CHANGED
@@ -11,10 +11,40 @@ require 'atom/xml/parser.rb'
|
|
11
11
|
|
12
12
|
module Atom # :nodoc:
|
13
13
|
NAMESPACE = 'http://www.w3.org/2005/Atom' unless defined?(NAMESPACE)
|
14
|
-
|
14
|
+
module Pub
|
15
|
+
NAMESPACE = 'http://www.w3.org/2007/app'
|
16
|
+
end
|
15
17
|
# Raised when a Parsing Error occurs.
|
16
18
|
class ParseError < StandardError; end
|
19
|
+
# Raised when a Serialization Error occurs.
|
20
|
+
class SerializationError < StandardError; end
|
21
|
+
|
22
|
+
# Provides support for reading and writing simple extensions as defined by the Atom Syndication Format.
|
23
|
+
#
|
24
|
+
# A Simple extension is an element from a non-atom namespace that has no attributes and only contains
|
25
|
+
# text content. It is interpreted as a key-value pair when the namespace and the localname of the
|
26
|
+
# extension make up the key. Since in XML you can have many instances of an element, the values are
|
27
|
+
# represented as an array of strings, so to manipulate the values manipulate the array returned by
|
28
|
+
# +[ns, localname]+.
|
29
|
+
#
|
30
|
+
module SimpleExtensions
|
31
|
+
attr_reader :simple_extensions
|
32
|
+
|
33
|
+
# Gets a simple extension value for a given namespace and local name.
|
34
|
+
#
|
35
|
+
# +ns+:: The namespace.
|
36
|
+
# +localname+:: The local name of the extension element.
|
37
|
+
#
|
38
|
+
def [](ns, localname)
|
39
|
+
if @simple_extensions.nil?
|
40
|
+
@simple_extensions = {}
|
41
|
+
end
|
17
42
|
|
43
|
+
key = "{#{ns},#{localname}}"
|
44
|
+
(@simple_extensions[key] or @simple_extensions[key] = [])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
18
48
|
# Represents a Generator as defined by the Atom Syndication Format specification.
|
19
49
|
#
|
20
50
|
# The generator identifies an agent or engine used to a produce a feed.
|
@@ -138,11 +168,23 @@ module Atom # :nodoc:
|
|
138
168
|
end
|
139
169
|
|
140
170
|
def to_xml(nodeonly = true, name = 'content') # :nodoc:
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
171
|
+
require 'iconv'
|
172
|
+
# Convert from utf-8 to utf-8 as a way of making sure the content is UTF-8.
|
173
|
+
#
|
174
|
+
# This is a pretty crappy way to do it but if we don't check libxml just
|
175
|
+
# fails silently and outputs the content element without any content. At
|
176
|
+
# least checking here and raising an exception gives the caller a chance
|
177
|
+
# to try and recitfy the situation.
|
178
|
+
#
|
179
|
+
begin
|
180
|
+
node = XML::Node.new(name)
|
181
|
+
node << Iconv.iconv('utf-8', 'utf-8', self.to_s)
|
182
|
+
node['type'] = 'html'
|
183
|
+
node['xml:lang'] = self.xml_lang
|
184
|
+
node
|
185
|
+
rescue Iconv::IllegalSequence => e
|
186
|
+
raise SerializationError, "Content must be converted to UTF-8 before attempting to serialize to XML: #{e.message}."
|
187
|
+
end
|
146
188
|
end
|
147
189
|
end
|
148
190
|
|
@@ -239,9 +281,10 @@ module Atom # :nodoc:
|
|
239
281
|
# See also http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.feed
|
240
282
|
class Feed
|
241
283
|
include Xml::Parseable
|
284
|
+
include SimpleExtensions
|
242
285
|
extend Forwardable
|
243
286
|
def_delegators :@links, :alternate, :self, :via, :first_page, :last_page, :next_page, :prev_page
|
244
|
-
|
287
|
+
|
245
288
|
loadable!
|
246
289
|
|
247
290
|
element :id, :rights
|
@@ -327,7 +370,7 @@ module Atom # :nodoc:
|
|
327
370
|
else
|
328
371
|
self.entries.each(&block)
|
329
372
|
end
|
330
|
-
end
|
373
|
+
end
|
331
374
|
end
|
332
375
|
|
333
376
|
# Represents an Entry as defined by the Atom Syndication Format specification.
|
@@ -355,6 +398,7 @@ module Atom # :nodoc:
|
|
355
398
|
# See also http://www.atomenabled.org/developers/syndication/atom-format-spec.php#element.entry
|
356
399
|
class Entry
|
357
400
|
include Xml::Parseable
|
401
|
+
include SimpleExtensions
|
358
402
|
extend Forwardable
|
359
403
|
def_delegators :@links, :alternate, :self, :alternates, :enclosures, :edit_link, :via
|
360
404
|
|
data/lib/atom/pub.rb
CHANGED
data/lib/atom/version.rb
CHANGED
data/lib/atom/xml/parser.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
#
|
7
7
|
|
8
8
|
require 'net/http'
|
9
|
+
require 'time'
|
9
10
|
|
10
11
|
# Just a couple methods form transforming strings
|
11
12
|
unless defined?(ActiveSupport)
|
@@ -36,7 +37,7 @@ module Atom
|
|
36
37
|
loop do
|
37
38
|
case xml.node_type
|
38
39
|
when XML::Reader::TYPE_ELEMENT
|
39
|
-
if element_specs.include?(xml.local_name)
|
40
|
+
if element_specs.include?(xml.local_name) && [Atom::NAMESPACE, Atom::Pub::NAMESPACE].include?(xml.namespace_uri)
|
40
41
|
element_specs[xml.local_name].parse(self, xml)
|
41
42
|
elsif attributes.any?
|
42
43
|
while (xml.move_to_next_attribute == 1)
|
@@ -45,6 +46,8 @@ module Atom
|
|
45
46
|
self.send("#{xml.name.sub(/:/, '_')}=", xml.value)
|
46
47
|
end
|
47
48
|
end
|
49
|
+
elsif self.respond_to?(:simple_extensions)
|
50
|
+
self[xml.namespace_uri, xml.local_name] << xml.read_string
|
48
51
|
end
|
49
52
|
end
|
50
53
|
break unless !options[:once] && xml.next == 1 && xml.depth >= starting_depth
|
@@ -114,6 +117,21 @@ module Atom
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
120
|
+
if self.respond_to?(:simple_extensions) && self.simple_extensions
|
121
|
+
self.simple_extensions.each do |name, value_array|
|
122
|
+
if name =~ /\{(.*),(.*)\}/
|
123
|
+
value_array.each do |value|
|
124
|
+
ext = XML::Node.new($2)
|
125
|
+
ext['xmlns'] = $1
|
126
|
+
ext << value
|
127
|
+
node << ext
|
128
|
+
end
|
129
|
+
else
|
130
|
+
STDERR.print "Couldn't split #{name}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
117
135
|
unless nodeonly
|
118
136
|
doc = XML::Document.new
|
119
137
|
doc.root = node
|
data/spec/atom_spec.rb
CHANGED
@@ -7,6 +7,77 @@
|
|
7
7
|
|
8
8
|
require File.dirname(__FILE__) + '/spec_helper.rb'
|
9
9
|
require 'net/http'
|
10
|
+
require 'time'
|
11
|
+
|
12
|
+
shared_examples_for 'simple_single_entry.atom attributes' do
|
13
|
+
it "should parse title" do
|
14
|
+
@feed.title.should == 'Example Feed'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should parse updated" do
|
18
|
+
@feed.updated.should == Time.parse('2003-12-13T18:30:02Z')
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should parse id" do
|
22
|
+
@feed.id.should == 'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have an entries array" do
|
26
|
+
@feed.entries.should be_an_instance_of(Array)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have one element in the entries array" do
|
30
|
+
@feed.entries.size.should == 1
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have an alternate" do
|
34
|
+
@feed.alternate.should_not be_nil
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have an Atom::Link as the alternate" do
|
38
|
+
@feed.alternate.should be_an_instance_of(Atom::Link)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have the correct href in the alternate" do
|
42
|
+
@feed.alternate.href.should == 'http://example.org/'
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should parse title" do
|
46
|
+
@entry.title.should == 'Atom-Powered Robots Run Amok'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should have an alternate" do
|
50
|
+
@entry.alternate.should_not be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have an Atom::Link as the alternate" do
|
54
|
+
@entry.alternate.should be_an_instance_of(Atom::Link)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should have the correct href on the alternate" do
|
58
|
+
@entry.alternate.href.should == 'http://example.org/2003/12/13/atom03'
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should parse id" do
|
62
|
+
@entry.id.should == 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should parse updated" do
|
66
|
+
@entry.updated.should == Time.parse('2003-12-13T18:30:02Z')
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should parse summary" do
|
70
|
+
@entry.summary.should == 'Some text.'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should parse content" do
|
74
|
+
@entry.content.should == 'This <em>is</em> html.'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should parse content type" do
|
78
|
+
@entry.content.type.should == 'html'
|
79
|
+
end
|
80
|
+
end
|
10
81
|
|
11
82
|
describe Atom do
|
12
83
|
describe "Atom::Feed.load_feed" do
|
@@ -70,83 +141,10 @@ describe Atom do
|
|
70
141
|
describe 'SimpleSingleFeed' do
|
71
142
|
before(:all) do
|
72
143
|
@feed = Atom::Feed.load_feed(File.open('spec/fixtures/simple_single_entry.atom'))
|
144
|
+
@entry = @feed.entries.first
|
73
145
|
end
|
74
146
|
|
75
|
-
|
76
|
-
it "should parse title" do
|
77
|
-
@feed.title.should == 'Example Feed'
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should parse updated" do
|
81
|
-
@feed.updated.should == Time.parse('2003-12-13T18:30:02Z')
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should parse id" do
|
85
|
-
@feed.id.should == 'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6'
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should have an entries array" do
|
89
|
-
@feed.entries.should be_an_instance_of(Array)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "should have one element in the entries array" do
|
93
|
-
@feed.entries.size.should == 1
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should have an alternate" do
|
97
|
-
@feed.alternate.should_not be_nil
|
98
|
-
end
|
99
|
-
|
100
|
-
it "should have an Atom::Link as the alternate" do
|
101
|
-
@feed.alternate.should be_an_instance_of(Atom::Link)
|
102
|
-
end
|
103
|
-
|
104
|
-
it "should have the correct href in the alternate" do
|
105
|
-
@feed.alternate.href.should == 'http://example.org/'
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
describe Atom::Entry do
|
110
|
-
before(:each) do
|
111
|
-
@entry = @feed.entries.first
|
112
|
-
end
|
113
|
-
|
114
|
-
it "should parse title" do
|
115
|
-
@entry.title.should == 'Atom-Powered Robots Run Amok'
|
116
|
-
end
|
117
|
-
|
118
|
-
it "should have an alternate" do
|
119
|
-
@entry.alternate.should_not be_nil
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should have an Atom::Link as the alternate" do
|
123
|
-
@entry.alternate.should be_an_instance_of(Atom::Link)
|
124
|
-
end
|
125
|
-
|
126
|
-
it "should have the correct href on the alternate" do
|
127
|
-
@entry.alternate.href.should == 'http://example.org/2003/12/13/atom03'
|
128
|
-
end
|
129
|
-
|
130
|
-
it "should parse id" do
|
131
|
-
@entry.id.should == 'urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a'
|
132
|
-
end
|
133
|
-
|
134
|
-
it "should parse updated" do
|
135
|
-
@entry.updated.should == Time.parse('2003-12-13T18:30:02Z')
|
136
|
-
end
|
137
|
-
|
138
|
-
it "should parse summary" do
|
139
|
-
@entry.summary.should == 'Some text.'
|
140
|
-
end
|
141
|
-
|
142
|
-
it "should parse content" do
|
143
|
-
@entry.content.should == 'This <em>is</em> html.'
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should parse content type" do
|
147
|
-
@entry.content.type.should == 'html'
|
148
|
-
end
|
149
|
-
end
|
147
|
+
it_should_behave_like "simple_single_entry.atom attributes"
|
150
148
|
end
|
151
149
|
|
152
150
|
describe 'ComplexFeed' do
|
@@ -905,6 +903,53 @@ describe Atom do
|
|
905
903
|
entry_count.should == 1
|
906
904
|
end
|
907
905
|
end
|
906
|
+
|
907
|
+
describe "entry_with_simple_extensions.atom" do
|
908
|
+
before(:each) do
|
909
|
+
@feed = Atom::Feed.load_feed(File.open('spec/fixtures/entry_with_simple_extensions.atom'))
|
910
|
+
@entry = @feed.entries.first
|
911
|
+
end
|
912
|
+
|
913
|
+
it "should load simple extension for feed" do
|
914
|
+
@feed["http://example.org/example", 'simple1'].should == ['Simple1 Value']
|
915
|
+
end
|
916
|
+
|
917
|
+
it "should load empty simple extension for feed" do
|
918
|
+
@feed["http://example.org/example", 'simple-empty'].should == ['']
|
919
|
+
end
|
920
|
+
|
921
|
+
it "should load simple extension 1 for entry" do
|
922
|
+
@entry["http://example.org/example", 'simple1'].should == ['Simple1 Entry Value']
|
923
|
+
end
|
924
|
+
|
925
|
+
it "should load simple extension 2 for entry" do
|
926
|
+
@entry["http://example.org/example", 'simple2'].should == ['Simple2', 'Simple2a']
|
927
|
+
end
|
928
|
+
|
929
|
+
it "should find a simple extension in another namespace" do
|
930
|
+
@entry["http://example2.org/example2", 'simple1'].should == ['Simple Entry Value (NS2)']
|
931
|
+
end
|
932
|
+
|
933
|
+
it "should read an extension with the same local name as an Atom element" do
|
934
|
+
@feed['http://example.org/example', 'title'].should == ['Extension Title']
|
935
|
+
end
|
936
|
+
|
937
|
+
it_should_behave_like 'simple_single_entry.atom attributes'
|
938
|
+
end
|
939
|
+
|
940
|
+
describe 'writing simple extensions' do
|
941
|
+
it "should recode and re-read a simple extension element" do
|
942
|
+
entry = Atom::Entry.new do |entry|
|
943
|
+
entry.id = 'urn:test'
|
944
|
+
entry.title = 'Simple Ext. Test'
|
945
|
+
entry.updated = Time.now
|
946
|
+
entry['http://example.org', 'title'] << 'Example title'
|
947
|
+
end
|
948
|
+
|
949
|
+
entry2 = Atom::Entry.load_entry(entry.to_xml)
|
950
|
+
entry2['http://example.org', 'title'].should == ['Example title']
|
951
|
+
end
|
952
|
+
end
|
908
953
|
end
|
909
954
|
|
910
955
|
describe Atom::Link do
|
@@ -972,6 +1017,16 @@ describe Atom do
|
|
972
1017
|
other = Atom::Entry.load_entry(@entry.to_xml)
|
973
1018
|
@entry.should == other
|
974
1019
|
end
|
1020
|
+
|
1021
|
+
it "should raise error when to_xml'ing non-utf8 content" do
|
1022
|
+
lambda {
|
1023
|
+
puts(Atom::Entry.new do |entry|
|
1024
|
+
entry.title = "My entry"
|
1025
|
+
entry.id = "urn:entry:1"
|
1026
|
+
entry.content = Atom::Content::Html.new("this is not \227 utf8")
|
1027
|
+
end.to_xml)
|
1028
|
+
}.should raise_error(Atom::SerializationError)
|
1029
|
+
end
|
975
1030
|
end
|
976
1031
|
|
977
1032
|
describe 'Atom::Feed initializer' do
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ratom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
platform:
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peerworks
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-04-02 00:00:00 +10:30
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
109
|
requirements: []
|
110
110
|
|
111
111
|
rubyforge_project: ratom
|
112
|
-
rubygems_version:
|
112
|
+
rubygems_version: 1.1.0
|
113
113
|
signing_key:
|
114
114
|
specification_version: 2
|
115
115
|
summary: Atom Syndication and Publication API
|