atom-tools 1.0.0 → 2.0.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/COPYING +3 -3
- data/README +4 -44
- data/Rakefile +9 -2
- data/bin/atom-cp +159 -0
- data/bin/atom-grep +78 -0
- data/bin/atom-post +72 -0
- data/bin/atom-purge +82 -0
- data/lib/atom/cache.rb +178 -0
- data/lib/atom/collection.rb +77 -17
- data/lib/atom/element.rb +520 -166
- data/lib/atom/entry.rb +82 -142
- data/lib/atom/feed.rb +48 -66
- data/lib/atom/http.rb +115 -35
- data/lib/atom/service.rb +56 -113
- data/lib/atom/text.rb +79 -63
- data/lib/atom/tools.rb +163 -0
- data/test/conformance/order.rb +11 -10
- data/test/conformance/title.rb +9 -9
- data/test/test_constructs.rb +23 -10
- data/test/test_feed.rb +0 -44
- data/test/test_general.rb +0 -40
- data/test/test_http.rb +18 -0
- data/test/test_protocol.rb +60 -22
- data/test/test_xml.rb +73 -41
- metadata +47 -37
- data/bin/atom-client.rb +0 -275
- data/lib/atom/xml.rb +0 -213
- data/lib/atom/yaml.rb +0 -116
metadata
CHANGED
@@ -1,39 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: atom-tools
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2007-11-10 00:00:00 -07:00
|
8
|
-
summary: Tools for working with Atom Entries, Feeds and Collections
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email:
|
12
|
-
homepage:
|
13
|
-
rubyforge_project:
|
14
|
-
description:
|
15
|
-
autorequire:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 2.0.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Brendan Taylor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-28 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email:
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
31
24
|
files:
|
32
25
|
- COPYING
|
33
26
|
- README
|
34
27
|
- Rakefile
|
35
28
|
- setup.rb
|
36
|
-
- bin/atom-
|
29
|
+
- bin/atom-grep
|
30
|
+
- bin/atom-post
|
31
|
+
- bin/atom-purge
|
32
|
+
- bin/atom-cp
|
37
33
|
- test/test_constructs.rb
|
38
34
|
- test/runtests.rb
|
39
35
|
- test/test_feed.rb
|
@@ -48,26 +44,40 @@ files:
|
|
48
44
|
- test/test_http.rb
|
49
45
|
- test/test_general.rb
|
50
46
|
- lib/atom
|
51
|
-
- lib/atom/
|
47
|
+
- lib/atom/cache.rb
|
52
48
|
- lib/atom/feed.rb
|
53
49
|
- lib/atom/text.rb
|
54
50
|
- lib/atom/collection.rb
|
55
51
|
- lib/atom/service.rb
|
56
52
|
- lib/atom/entry.rb
|
57
|
-
- lib/atom/
|
53
|
+
- lib/atom/tools.rb
|
58
54
|
- lib/atom/element.rb
|
59
55
|
- lib/atom/http.rb
|
60
|
-
|
61
|
-
|
56
|
+
has_rdoc: true
|
57
|
+
homepage:
|
58
|
+
post_install_message:
|
62
59
|
rdoc_options: []
|
63
60
|
|
64
|
-
|
65
|
-
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
70
75
|
requirements: []
|
71
76
|
|
72
|
-
|
73
|
-
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.0.1
|
79
|
+
signing_key:
|
80
|
+
specification_version: 2
|
81
|
+
summary: Tools for working with Atom Entries, Feeds and Collections
|
82
|
+
test_files:
|
83
|
+
- test/runtests.rb
|
data/bin/atom-client.rb
DELETED
@@ -1,275 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
require "optparse"
|
4
|
-
|
5
|
-
options = {}
|
6
|
-
|
7
|
-
OptionParser.new do |opts|
|
8
|
-
opts.banner = "Usage: #{$0} [options]"
|
9
|
-
|
10
|
-
opts.on("-c", "--coll-url [URL]", "URL of the collection you would like to manipulate") do |url|
|
11
|
-
options[:url] = url
|
12
|
-
end
|
13
|
-
|
14
|
-
opts.on("-u", "--user [USERNAME]", "Username to authenticate with") do |user|
|
15
|
-
options[:user] = user
|
16
|
-
end
|
17
|
-
|
18
|
-
opts.on("-p", "--password [PASSWORD]", "Password to authenticate with") do |pass|
|
19
|
-
options[:pass] = pass
|
20
|
-
end
|
21
|
-
|
22
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
23
|
-
puts opts
|
24
|
-
exit
|
25
|
-
end
|
26
|
-
end.parse!
|
27
|
-
|
28
|
-
require "tempfile"
|
29
|
-
|
30
|
-
require "atom/yaml"
|
31
|
-
require "atom/service"
|
32
|
-
require "atom/http"
|
33
|
-
|
34
|
-
require "rubygems"
|
35
|
-
require "bluecloth"
|
36
|
-
|
37
|
-
require "time"
|
38
|
-
|
39
|
-
require 'yaml'
|
40
|
-
|
41
|
-
class Tempfile
|
42
|
-
def edit_externally
|
43
|
-
self.close
|
44
|
-
|
45
|
-
system("#{EDITOR} #{self.path}")
|
46
|
-
|
47
|
-
self.open
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
class String
|
52
|
-
def edit_externally
|
53
|
-
tempfile = Tempfile.new("entry")
|
54
|
-
tempfile.puts self
|
55
|
-
|
56
|
-
tempfile.edit_externally
|
57
|
-
|
58
|
-
ret = tempfile.read
|
59
|
-
tempfile.delete
|
60
|
-
|
61
|
-
ret
|
62
|
-
end
|
63
|
-
end
|
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
|
-
|
80
|
-
def edit
|
81
|
-
yaml = YAML.load(self.to_yaml)
|
82
|
-
|
83
|
-
# human readability
|
84
|
-
yaml.delete "id"
|
85
|
-
|
86
|
-
if yaml["links"]
|
87
|
-
yaml["links"].find_all { |l| l["rel"] == "alternate" or l["rel"] == "edit" }.each { |l| yaml["links"].delete(l) }
|
88
|
-
yaml.delete("links") if yaml["links"].empty?
|
89
|
-
end
|
90
|
-
|
91
|
-
new_yaml, entry = write_entry(yaml.to_yaml)
|
92
|
-
|
93
|
-
entry.id = self.id
|
94
|
-
|
95
|
-
[new_yaml["slug"], entry]
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# maybe this should handle displaying the list too.
|
100
|
-
def choose_from list
|
101
|
-
item = nil
|
102
|
-
itemno = nil
|
103
|
-
|
104
|
-
# oh wow this is pathetic
|
105
|
-
until item
|
106
|
-
if itemno
|
107
|
-
puts "try picking a number on the list."
|
108
|
-
end
|
109
|
-
|
110
|
-
print "? "
|
111
|
-
itemno = $stdin.gets.chomp
|
112
|
-
|
113
|
-
item = list[itemno.to_i]
|
114
|
-
end
|
115
|
-
|
116
|
-
item
|
117
|
-
end
|
118
|
-
|
119
|
-
def choose_entry_url coll
|
120
|
-
puts "which entry?"
|
121
|
-
|
122
|
-
coll.entries.each_with_index do |entry, index|
|
123
|
-
puts "#{index}: #{entry.title}"
|
124
|
-
end
|
125
|
-
|
126
|
-
entry = choose_from coll.entries
|
127
|
-
|
128
|
-
edit_link = entry.links.find do |link|
|
129
|
-
link["rel"] = "edit"
|
130
|
-
end
|
131
|
-
|
132
|
-
edit_link["href"]
|
133
|
-
end
|
134
|
-
|
135
|
-
def write_entry(editstring = "")
|
136
|
-
begin
|
137
|
-
edited = editstring.edit_externally
|
138
|
-
|
139
|
-
if edited == editstring
|
140
|
-
puts "You didn't edit anything, aborting."
|
141
|
-
exit
|
142
|
-
end
|
143
|
-
|
144
|
-
yaml = YAML.load(edited)
|
145
|
-
|
146
|
-
entry = Atom::Entry.from_yaml yaml
|
147
|
-
|
148
|
-
entry.prepare_for_output
|
149
|
-
|
150
|
-
# XXX disabled until the APP WG can decide what a valid entry is
|
151
|
-
=begin
|
152
|
-
valid, message = entry.valid?
|
153
|
-
unless valid
|
154
|
-
print "entry is invalid (#{message}). post anyway? (y/n)? "
|
155
|
-
(gets.chomp == "y") || (raise Atom::InvalidEntry.new)
|
156
|
-
end
|
157
|
-
=end
|
158
|
-
|
159
|
-
# this has to be here ATM to we can detect malformed atom:content
|
160
|
-
puts entry.to_s
|
161
|
-
rescue ArgumentError,REXML::ParseException => e
|
162
|
-
puts e
|
163
|
-
|
164
|
-
puts "press enter to edit again..."
|
165
|
-
$stdin.gets
|
166
|
-
|
167
|
-
editstring = edited
|
168
|
-
|
169
|
-
retry
|
170
|
-
rescue Atom::InvalidEntry
|
171
|
-
editstring = edited
|
172
|
-
retry
|
173
|
-
end
|
174
|
-
|
175
|
-
[yaml, entry]
|
176
|
-
end
|
177
|
-
|
178
|
-
module Atom
|
179
|
-
class InvalidEntry < RuntimeError; end
|
180
|
-
end
|
181
|
-
|
182
|
-
EDITOR = ENV["EDITOR"] || "env vim"
|
183
|
-
|
184
|
-
http = Atom::HTTP.new
|
185
|
-
|
186
|
-
url = if options[:url]
|
187
|
-
options[:url]
|
188
|
-
else
|
189
|
-
yaml = YAML.load(File.read("#{ENV["HOME"]}/.atom-client"))
|
190
|
-
collections = yaml["collections"]
|
191
|
-
|
192
|
-
puts "which collection?"
|
193
|
-
|
194
|
-
collections.keys.each_with_index do |name,index|
|
195
|
-
puts "#{index}: #{name}"
|
196
|
-
end
|
197
|
-
|
198
|
-
tmp = choose_from collections.values
|
199
|
-
|
200
|
-
http.user = tmp["user"] if tmp["user"]
|
201
|
-
http.pass = tmp["pass"] if tmp["pass"]
|
202
|
-
|
203
|
-
tmp["url"]
|
204
|
-
end
|
205
|
-
|
206
|
-
http.user = options[:user] if options[:user]
|
207
|
-
http.pass = options[:pass] if options[:pass]
|
208
|
-
|
209
|
-
# this is where all the Atom stuff starts
|
210
|
-
|
211
|
-
coll = Atom::Collection.new(url, http)
|
212
|
-
|
213
|
-
# XXX generate a real id
|
214
|
-
CLIENT_ID = "http://necronomicorp.com/nil"
|
215
|
-
|
216
|
-
new = lambda do
|
217
|
-
entry = Atom::Entry.new
|
218
|
-
|
219
|
-
entry.title = ""
|
220
|
-
entry.content = ""
|
221
|
-
|
222
|
-
slug, entry = entry.edit
|
223
|
-
|
224
|
-
entry.id = CLIENT_ID
|
225
|
-
entry.published = Time.now.iso8601
|
226
|
-
|
227
|
-
res = coll.post! entry, slug
|
228
|
-
|
229
|
-
# XXX error recovery here, lost updates suck
|
230
|
-
puts res.body
|
231
|
-
end
|
232
|
-
|
233
|
-
edit = lambda do
|
234
|
-
coll.update!
|
235
|
-
|
236
|
-
coll.entries.each_with_index do |entry,idx|
|
237
|
-
puts "#{idx}: #{entry.title}"
|
238
|
-
end
|
239
|
-
|
240
|
-
entry = choose_from(coll.entries) { |entry| entry.title }
|
241
|
-
|
242
|
-
url = entry.edit_url
|
243
|
-
|
244
|
-
raise "this entry has no edit link" unless url
|
245
|
-
|
246
|
-
entry = http.get_atom_entry url
|
247
|
-
|
248
|
-
slug, new_entry = entry.edit
|
249
|
-
|
250
|
-
res = coll.put! new_entry, url
|
251
|
-
|
252
|
-
# XXX error recovery here, lost updates suck
|
253
|
-
puts res.body
|
254
|
-
end
|
255
|
-
|
256
|
-
delete = lambda do
|
257
|
-
coll.update!
|
258
|
-
|
259
|
-
coll.entries.each_with_index do |entry,idx|
|
260
|
-
puts "#{idx} #{entry.title}"
|
261
|
-
end
|
262
|
-
|
263
|
-
entry = choose_from(coll.entries)
|
264
|
-
res = coll.delete! entry
|
265
|
-
|
266
|
-
puts res.body
|
267
|
-
end
|
268
|
-
|
269
|
-
actions = [ new, edit, delete ]
|
270
|
-
|
271
|
-
puts "0: new entry"
|
272
|
-
puts "1: edit entry"
|
273
|
-
puts "2: delete entry"
|
274
|
-
|
275
|
-
choose_from(actions).call
|
data/lib/atom/xml.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
require "atom/entry"
|
2
|
-
require "atom/feed"
|
3
|
-
require "uri"
|
4
|
-
|
5
|
-
class Atom::ParseError < StandardError; end
|
6
|
-
|
7
|
-
# Two big, interrelated problems:
|
8
|
-
#
|
9
|
-
# * I probably shouldn't be playing around in REXML's namespace
|
10
|
-
# * REXML isn't a great parser, and other options would be nice
|
11
|
-
#
|
12
|
-
# This shouldn't be hard to do, it's just tedious and non-critical
|
13
|
-
module REXML # :nodoc: all
|
14
|
-
class Document
|
15
|
-
def to_atom_entry base = ""
|
16
|
-
raise Atom::ParseError unless self.root
|
17
|
-
self.root.to_atom_entry base
|
18
|
-
end
|
19
|
-
def to_atom_feed base = ""
|
20
|
-
raise Atom::ParseError unless self.root
|
21
|
-
self.root.to_atom_feed base
|
22
|
-
end
|
23
|
-
end
|
24
|
-
class Element
|
25
|
-
def get_atom_element name
|
26
|
-
XPath.first(self, "./atom:#{name}", { "atom" => Atom::NS })
|
27
|
-
end
|
28
|
-
|
29
|
-
def each_atom_element name
|
30
|
-
XPath.each(self, "./atom:#{name}", { "atom" => Atom::NS }) do |elem|
|
31
|
-
yield elem
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def copy_extensions(coll)
|
36
|
-
# XXX also look for attributes
|
37
|
-
children.find_all do |child|
|
38
|
-
child.respond_to? :namespace and child.namespace != Atom::NS
|
39
|
-
end.each do |elem|
|
40
|
-
e = elem.dup
|
41
|
-
|
42
|
-
# because namespaces might be defined on parents
|
43
|
-
unless e.prefix.empty?
|
44
|
-
e.add_namespace e.prefix, e.namespace
|
45
|
-
end
|
46
|
-
|
47
|
-
coll << e
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# get the text content of a descendant element in the Atom namespace
|
52
|
-
def get_atom_text name
|
53
|
-
elem = get_atom_element name
|
54
|
-
if elem
|
55
|
-
elem.text
|
56
|
-
else
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# a workaround for the odd way in which REXML handles namespaces
|
62
|
-
# returns the value of the attribute +name+ that's in the same namespace as this element
|
63
|
-
def ns_attr name
|
64
|
-
if not self.prefix.empty?
|
65
|
-
attr = self.prefix + ":" + name
|
66
|
-
else
|
67
|
-
attr = name
|
68
|
-
end
|
69
|
-
|
70
|
-
self.attributes[attr]
|
71
|
-
end
|
72
|
-
|
73
|
-
def fill_text_construct(entry, name)
|
74
|
-
text = get_atom_element(name)
|
75
|
-
if text
|
76
|
-
type = text.ns_attr("type")
|
77
|
-
src = text.ns_attr("src")
|
78
|
-
|
79
|
-
if src and name == :content
|
80
|
-
# the only content is out of line
|
81
|
-
entry.send( "#{name}=".to_sym, "")
|
82
|
-
entry.send(name.to_sym)["src"] = src
|
83
|
-
elsif type == "xhtml"
|
84
|
-
div = XPath.first(text, "./xhtml:div", { "xhtml" => XHTML::NS })
|
85
|
-
unless div
|
86
|
-
raise Atom::ParseError, "Refusing to parse type='xhtml' with no <div/> wrapper"
|
87
|
-
end
|
88
|
-
|
89
|
-
# content is the serialized content of the <div> wrapper
|
90
|
-
entry.send( "#{name}=".to_sym, div )
|
91
|
-
else
|
92
|
-
raw = text.text || ""
|
93
|
-
entry.send( "#{name}=", raw )
|
94
|
-
end
|
95
|
-
|
96
|
-
if text.attributes["xml:base"]
|
97
|
-
entry.send(name.to_sym).base = text.attributes["xml:base"]
|
98
|
-
end
|
99
|
-
|
100
|
-
if type and type != "text"
|
101
|
-
entry.send(name.to_sym)["type"] = type
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def fill_elem_element(top, kind)
|
107
|
-
each_atom_element(kind) do |elem|
|
108
|
-
person = top.send("#{kind}s".to_sym).new
|
109
|
-
|
110
|
-
["name", "uri", "email"].each do |name|
|
111
|
-
person.send("#{name}=".to_sym, elem.get_atom_text(name))
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def fill_attr_element(top, array, kind)
|
117
|
-
each_atom_element(kind) do |elem|
|
118
|
-
thing = array.new
|
119
|
-
|
120
|
-
thing.class.attrs.each do |name,req|
|
121
|
-
value = elem.ns_attr name.to_s
|
122
|
-
if value and name == :href
|
123
|
-
begin
|
124
|
-
thing[name.to_s] = (top.base.to_uri + value).to_s
|
125
|
-
rescue URI::BadURIError
|
126
|
-
raise "Document contains relative URIs and no xml:base. You must pass a base URI to #parse()"
|
127
|
-
end
|
128
|
-
elsif value
|
129
|
-
thing[name.to_s] = value
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# 'base' is the URI that you fetched this document from.
|
136
|
-
def to_atom_entry base = ""
|
137
|
-
unless self.name == "entry" and self.namespace == Atom::NS
|
138
|
-
raise Atom::ParseError, "this isn't an atom:entry! (name: #{self.name}, ns: #{self.namespace})"
|
139
|
-
end
|
140
|
-
|
141
|
-
entry = Atom::Entry.new
|
142
|
-
|
143
|
-
entry.base = if attributes["xml:base"]
|
144
|
-
(URI.parse(base) + attributes["xml:base"]).to_s
|
145
|
-
else
|
146
|
-
# go with the URL we were passed in
|
147
|
-
base
|
148
|
-
end
|
149
|
-
|
150
|
-
# Text constructs
|
151
|
-
entry.class.elements.find_all { |n,k,r| k.ancestors.member? Atom::Text }.each do |n,k,r|
|
152
|
-
fill_text_construct(entry, n)
|
153
|
-
end
|
154
|
-
|
155
|
-
["id", "published", "updated"].each do |name|
|
156
|
-
entry.send("#{name}=".to_sym, get_atom_text(name))
|
157
|
-
end
|
158
|
-
|
159
|
-
["author", "contributor"].each do |type|
|
160
|
-
fill_elem_element(entry, type)
|
161
|
-
end
|
162
|
-
|
163
|
-
{"link" => entry.links, "category" => entry.categories}.each do |k,v|
|
164
|
-
fill_attr_element(entry, v, k)
|
165
|
-
end
|
166
|
-
|
167
|
-
copy_extensions(entry.extensions)
|
168
|
-
|
169
|
-
entry
|
170
|
-
end
|
171
|
-
|
172
|
-
# 'base' is the URI that you fetched this document from.
|
173
|
-
def to_atom_feed base = ""
|
174
|
-
unless self.name == "feed" and self.namespace == Atom::NS
|
175
|
-
raise Atom::ParseError, "this isn't an atom:feed! (name: #{self.name}, ns: #{self.namespace})"
|
176
|
-
end
|
177
|
-
|
178
|
-
feed = Atom::Feed.new
|
179
|
-
|
180
|
-
feed.base = if attributes["xml:base"]
|
181
|
-
(URI.parse(base) + attributes["xml:base"]).to_s
|
182
|
-
else
|
183
|
-
# go with the URL we were passed in
|
184
|
-
base
|
185
|
-
end
|
186
|
-
|
187
|
-
# Text constructs
|
188
|
-
feed.class.elements.find_all { |n,k,r| k.ancestors.member? Atom::Text }.each do |n,k,r|
|
189
|
-
fill_text_construct(feed, n)
|
190
|
-
end
|
191
|
-
|
192
|
-
["id", "updated", "generator", "icon", "logo"].each do |name|
|
193
|
-
feed.send("#{name}=".to_sym, get_atom_text(name))
|
194
|
-
end
|
195
|
-
|
196
|
-
["author", "contributor"].each do |type|
|
197
|
-
fill_elem_element(feed, type)
|
198
|
-
end
|
199
|
-
|
200
|
-
{"link" => feed.links, "category" => feed.categories}.each do |k,v|
|
201
|
-
fill_attr_element(feed, v, k)
|
202
|
-
end
|
203
|
-
|
204
|
-
each_atom_element("entry") do |elem|
|
205
|
-
feed << elem.to_atom_entry(feed.base)
|
206
|
-
end
|
207
|
-
|
208
|
-
copy_extensions(feed.extensions)
|
209
|
-
|
210
|
-
feed
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|