atom-tools 0.9.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.
Files changed (57) hide show
  1. data/COPYING +18 -0
  2. data/README +103 -0
  3. data/Rakefile +77 -0
  4. data/bin/atom-client.rb +246 -0
  5. data/bin/atom-server.rb~ +71 -0
  6. data/doc/classes/Atom/App.html +217 -0
  7. data/doc/classes/Atom/Author.html +130 -0
  8. data/doc/classes/Atom/Category.html +128 -0
  9. data/doc/classes/Atom/Collection.html +322 -0
  10. data/doc/classes/Atom/Content.html +129 -0
  11. data/doc/classes/Atom/Contributor.html +119 -0
  12. data/doc/classes/Atom/Element.html +325 -0
  13. data/doc/classes/Atom/Entry.html +365 -0
  14. data/doc/classes/Atom/Feed.html +585 -0
  15. data/doc/classes/Atom/HTTP.html +374 -0
  16. data/doc/classes/Atom/Link.html +137 -0
  17. data/doc/classes/Atom/Text.html +229 -0
  18. data/doc/classes/XHTML.html +118 -0
  19. data/doc/created.rid +1 -0
  20. data/doc/files/README.html +213 -0
  21. data/doc/files/lib/atom/app_rb.html +110 -0
  22. data/doc/files/lib/atom/collection_rb.html +110 -0
  23. data/doc/files/lib/atom/element_rb.html +109 -0
  24. data/doc/files/lib/atom/entry_rb.html +111 -0
  25. data/doc/files/lib/atom/feed_rb.html +112 -0
  26. data/doc/files/lib/atom/http_rb.html +109 -0
  27. data/doc/files/lib/atom/text_rb.html +108 -0
  28. data/doc/files/lib/atom/xml_rb.html +110 -0
  29. data/doc/files/lib/atom/yaml_rb.html +109 -0
  30. data/doc/fr_class_index.html +39 -0
  31. data/doc/fr_file_index.html +36 -0
  32. data/doc/fr_method_index.html +62 -0
  33. data/doc/index.html +24 -0
  34. data/doc/rdoc-style.css +208 -0
  35. data/lib/atom/app.rb +87 -0
  36. data/lib/atom/collection.rb +75 -0
  37. data/lib/atom/element.rb +277 -0
  38. data/lib/atom/entry.rb +135 -0
  39. data/lib/atom/feed.rb +229 -0
  40. data/lib/atom/http.rb +132 -0
  41. data/lib/atom/text.rb +163 -0
  42. data/lib/atom/xml.rb +200 -0
  43. data/lib/atom/yaml.rb +101 -0
  44. data/setup.rb +1585 -0
  45. data/test/conformance/order.rb +117 -0
  46. data/test/conformance/title.rb +108 -0
  47. data/test/conformance/updated.rb +33 -0
  48. data/test/conformance/xhtmlcontentdiv.rb +18 -0
  49. data/test/conformance/xmlnamespace.rb +54 -0
  50. data/test/runtests.rb +14 -0
  51. data/test/test_constructs.rb +91 -0
  52. data/test/test_feed.rb +128 -0
  53. data/test/test_general.rb +99 -0
  54. data/test/test_http.rb +86 -0
  55. data/test/test_protocol.rb +69 -0
  56. data/test/test_xml.rb +353 -0
  57. metadata +107 -0
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ copyright © 2006 Brendan Taylor
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,103 @@
1
+ = atom-tools README
2
+
3
+ atom-tools is an all-in-one library for parsing, creating and manipulating Atom <http://www.w3.org/2005/Atom> feeds and entries.
4
+ It handles all the nasty XML and HTTP details so that you don't have to.
5
+
6
+ == Example
7
+
8
+ require "atom/feed"
9
+ require "open-uri"
10
+
11
+ feed = Atom::Feed.new "http://www.tbray.org/ongoing/ongoing.atom"
12
+ # => <http://www.tbray.org/ongoing/ongoing.atom entries: 0 title=''>
13
+
14
+ feed.update!
15
+ feed.entries.length
16
+ # => 20
17
+
18
+ feed.title.to_s
19
+ # => "ongoing"
20
+
21
+ feed.authors.first.name
22
+ # => "Tim Bray"
23
+
24
+ entry = feed.entries.first
25
+ # => #<Atom::Entry id:'http://www.tbray.org/ongoing/When/200x/2006/11/07/Munich'>
26
+
27
+ entry.title.to_s
28
+ # => "M\303\274nchen"
29
+
30
+ entry.links.last
31
+ # => {"href"=>"http://www.tbray.org/ongoing/When/200x/2006/11/07/Munich#comments", "rel"=>"replies", "type" => "application/xhtml+xml"}
32
+
33
+ entry.summary.to_s
34
+ # => "That local spelling is nicer than the ugly English [...]"
35
+
36
+ Things are explained in more detail in the RDoc.
37
+
38
+ == The Atom Publishing Protocol
39
+
40
+ require "atom/app"
41
+
42
+ app = Atom::App.new "http://necronomicorp.com/app.xml"
43
+ coll = app.collections.first
44
+ # => <http://necronomicorp.com/testatom?app entries: 0 title='testing: entry endpoint'>
45
+
46
+ coll.update!
47
+ # => <http://necronomicorp.com/testatom?app entries: 10 title='testing the APP'>
48
+
49
+ entry = coll.entries.first
50
+ entry.title
51
+ # => 'html entities'#text
52
+
53
+ # modify the title
54
+ entry.title = "HTML entities"
55
+
56
+ # store the modified entry
57
+ coll.put! entry
58
+ # => #<Net::HTTPOK 200 OK readbody=true>
59
+
60
+ coll.entries.first.title
61
+ # => 'HTML entities'#text
62
+
63
+ For details on authentication, see the documentation for Atom::HTTP
64
+
65
+ == Advanced Use
66
+
67
+ === Extension Elements
68
+
69
+ irt = REXML::Element.new("in-reply-to")
70
+ irt.add_namespace "http://purl.org/syndication/thread/1.0"
71
+
72
+ irt.attributes["ref"] = "tag:entries.com,2005:1"
73
+
74
+ entry.extensions << irt
75
+
76
+ entry.to_s
77
+ # => '<entry xmlns="http://www.w3.org/2005/Atom"><in-reply-to ref="tag:entries.com,2005:1" xmlns="http://purl.org/syndication/thread/1.0"/></entry>'
78
+
79
+ == YAML
80
+
81
+ if you feel like writing this stuff by hand, atom-tools can slurp an
82
+ atom:entry from YAML:
83
+
84
+ require "atom/yaml"
85
+
86
+ yaml = <<END
87
+ title: Atom-Drunk Pirates Run Amok!
88
+ tags: tag1 tag2
89
+ authors:
90
+ -
91
+ name: Brendan Taylor
92
+ email: whateley@gmail.com
93
+ -
94
+ name: Harvey
95
+ uri: http://fake.com/
96
+
97
+ content: |
98
+ <p>blah blah blah blah</p>
99
+
100
+ <p>and so on.</p>
101
+ END
102
+
103
+ entry = Atom::Entry.from_yaml(yaml)
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require "rake"
2
+ require "rake/testtask"
3
+ require "rake/rdoctask"
4
+ require "rake/gempackagetask"
5
+
6
+ require "rake/clean"
7
+
8
+ NAME = "atom-tools"
9
+ VERS = "0.9.0"
10
+
11
+ # the following from markaby-0.5's tools/rakehelp
12
+ def setup_tests
13
+ Rake::TestTask.new do |t|
14
+ t.libs << "test"
15
+ t.test_files = FileList['test/test*.rb']
16
+ t.verbose = true
17
+ end
18
+ end
19
+
20
+ def setup_rdoc files
21
+ Rake::RDocTask.new do |rdoc|
22
+ rdoc.title = NAME + " documentation"
23
+ rdoc.rdoc_dir = 'doc'
24
+ rdoc.options << '--line-numbers'
25
+ rdoc.options << '--inline-source'
26
+ rdoc.rdoc_files.add(files)
27
+ end
28
+ end
29
+
30
+ def setup_gem(pkg_name, pkg_version, author, summary, dependencies, test_file)
31
+ pkg_version = pkg_version
32
+ pkg_name = pkg_name
33
+ pkg_file_name = "#{pkg_name}-#{pkg_version}"
34
+
35
+ spec = Gem::Specification.new do |s|
36
+ s.name = pkg_name
37
+ s.version = pkg_version
38
+ s.platform = Gem::Platform::RUBY
39
+ s.author = author
40
+ s.summary = summary
41
+ s.test_file = test_file
42
+ s.has_rdoc = true
43
+ s.extra_rdoc_files = [ "README" ]
44
+ dependencies.each do |dep|
45
+ s.add_dependency(*dep)
46
+ end
47
+ s.files = %w(COPYING README Rakefile setup.rb) +
48
+ Dir.glob("{bin,doc,test,lib}/**/*") +
49
+ Dir.glob("ext/**/*.{h,c,rb}") +
50
+ Dir.glob("examples/**/*.rb") +
51
+ Dir.glob("tools/*.rb")
52
+
53
+ s.require_path = "lib"
54
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
55
+
56
+ s.bindir = "bin"
57
+ end
58
+
59
+ Rake::GemPackageTask.new(spec) do |p|
60
+ p.gem_spec = spec
61
+ p.need_tar = true
62
+ end
63
+
64
+ task :install do
65
+ sh %{rake package}
66
+ sh %{gem install pkg/#{pkg_name}-#{pkg_version}}
67
+ end
68
+ end
69
+
70
+ task :default => [:package]
71
+
72
+ setup_tests
73
+ setup_rdoc ['README', 'lib/**/*.rb']
74
+
75
+ summary = "Tools for working with Atom Entries, Feeds and Collections"
76
+ test_file = "test/runtests.rb"
77
+ setup_gem(NAME, VERS, "Brendan Taylor", summary, [], test_file)
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # syntax: ./atom-client.rb <introspection-url> [username] [password]
4
+ # a
5
+
6
+ require "tempfile"
7
+
8
+ require "atom/yaml"
9
+ require "atom/app"
10
+ require "atom/http"
11
+
12
+ require "rubygems"
13
+ require "bluecloth"
14
+
15
+ require "time"
16
+
17
+ require 'yaml'
18
+
19
+ class Tempfile
20
+ def edit_externally
21
+ self.close
22
+
23
+ system("#{EDITOR} #{self.path}")
24
+
25
+ self.open
26
+ end
27
+ end
28
+
29
+ class String
30
+ def edit_externally
31
+ tempfile = Tempfile.new("entry")
32
+ tempfile.puts self
33
+
34
+ tempfile.edit_externally
35
+
36
+ ret = tempfile.read
37
+ tempfile.delete
38
+
39
+ ret
40
+ end
41
+ end
42
+
43
+ class Atom::Entry
44
+ def prepare_for_output
45
+ filter_hook
46
+
47
+ update!
48
+ end
49
+
50
+ def filter_hook
51
+ # so much for actual text content...
52
+ if @content and @content["type"] == "text"
53
+ self.content = BlueCloth.new( @content.to_s ).to_html
54
+ @content["type"] = "xhtml"
55
+ end
56
+ end
57
+
58
+ def edit
59
+ yaml = YAML.load(self.to_yaml)
60
+
61
+ # humans don't care about these things
62
+ yaml.delete "id"
63
+
64
+ if yaml["links"]
65
+ yaml["links"].delete(yaml["links"].find { |l| l["rel"] == "edit" })
66
+ yaml["links"].delete(yaml["links"].find { |l| l["rel"] == "alternate" })
67
+ yaml.delete("links") if yaml["links"].empty?
68
+ end
69
+
70
+ entry = write_entry(yaml.to_yaml)
71
+ # the id doesn't appear in YAML, it should remain the same
72
+ entry.id = self.id
73
+
74
+ entry
75
+ end
76
+ end
77
+
78
+ # maybe this should handle displaying the list too.
79
+ def choose_from list
80
+ item = nil
81
+ itemno = nil
82
+
83
+ # oh wow this is pathetic
84
+ until item
85
+ if itemno
86
+ puts "try picking a number on the list."
87
+ end
88
+
89
+ print "? "
90
+ itemno = $stdin.gets.chomp
91
+
92
+ item = list[itemno.to_i]
93
+ end
94
+
95
+ item
96
+ end
97
+
98
+ def choose_collection server
99
+ puts "which collection?"
100
+
101
+ collections = []
102
+
103
+ # still lame
104
+ server.collections.each_with_index do |coll, index|
105
+ collections << coll
106
+
107
+ puts "#{index}: #{coll.title}"
108
+ end
109
+
110
+ choose_from collections
111
+ end
112
+
113
+ def choose_entry_url coll
114
+ puts "which entry?"
115
+
116
+ coll.entries.each_with_index do |entry, index|
117
+ puts "#{index}: #{entry.title}"
118
+ end
119
+
120
+ entry = choose_from coll.entries
121
+
122
+ edit_link = entry.links.find do |link|
123
+ link["rel"] = "edit"
124
+ end
125
+
126
+ edit_link["href"]
127
+ end
128
+
129
+ def write_entry(editstring = "")
130
+ begin
131
+ edited = editstring.edit_externally
132
+
133
+ if edited == editstring
134
+ puts "unchanged content, aborted"
135
+ exit
136
+ end
137
+
138
+ entry = Atom::Entry.from_yaml edited
139
+
140
+ entry.prepare_for_output
141
+
142
+ # XXX disabled until the APP WG can decide what a valid entry is
143
+ =begin
144
+ valid, message = entry.valid?
145
+ unless valid
146
+ print "entry is invalid (#{message}). post anyway? (y/n)? "
147
+ (gets.chomp == "y") || (raise Atom::InvalidEntry.new)
148
+ end
149
+ =end
150
+
151
+ # this has to be here ATM to we can detect malformed atom:content
152
+ puts entry.to_s
153
+ rescue ArgumentError,REXML::ParseException => e
154
+ puts e
155
+
156
+ puts "press enter to edit again..."
157
+ $stdin.gets
158
+
159
+ editstring = edited
160
+
161
+ retry
162
+ rescue Atom::InvalidEntry
163
+ editstring = edited
164
+ retry
165
+ end
166
+
167
+ entry
168
+ end
169
+
170
+ module Atom
171
+ class InvalidEntry < RuntimeError
172
+ end
173
+ end
174
+
175
+ EDITOR = ENV["EDITOR"] || "env vim"
176
+
177
+ # now that i'm supporting -07 the interface has been shittified. apologies.
178
+ introspection_url = ARGV[0]
179
+
180
+ http = Atom::HTTP.new
181
+ http.user = ARGV[1]
182
+ http.pass = ARGV[2]
183
+
184
+ server = Atom::App.new(introspection_url, http)
185
+
186
+ coll = choose_collection server
187
+
188
+ # XXX the server should *probably* replace this, but who knows yet?
189
+ CLIENT_ID = "http://necronomicorp.com/dev/null"
190
+
191
+ new = lambda do
192
+ entry = Atom::Entry.new
193
+
194
+ entry.title = ""
195
+ entry.content = ""
196
+
197
+ entry = entry.edit
198
+
199
+ entry.id = CLIENT_ID
200
+ entry.published = Time.now.iso8601
201
+
202
+ res = coll.post! entry
203
+
204
+ # XXX error recovery here, lost updates suck
205
+ puts res.body
206
+ end
207
+
208
+ edit = lambda do
209
+ coll.update!
210
+
211
+ coll.entries.each_with_index do |entry,idx|
212
+ puts "#{idx}: #{entry.title}"
213
+ end
214
+
215
+ entry = choose_from(coll.entries) { |entry| entry.title }
216
+
217
+ url = entry.edit_url
218
+
219
+ entry = http.get_atom_entry url
220
+
221
+ res = coll.put! entry.edit, url
222
+
223
+ # XXX error recovery here, lost updates suck
224
+ puts res.body
225
+ end
226
+
227
+ delete = lambda do
228
+ coll.update!
229
+
230
+ coll.entries.each_with_index do |entry,idx|
231
+ puts "#{idx} #{entry.title}"
232
+ end
233
+
234
+ entry = choose_from(coll.entries)
235
+ res = coll.delete! entry
236
+
237
+ puts res.body
238
+ end
239
+
240
+ actions = [ new, edit, delete ]
241
+
242
+ puts "0: new entry"
243
+ puts "1: edit entry"
244
+ puts "2: delete entry"
245
+
246
+ choose_from(actions).call
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "atom/pub-server"
4
+ require "webrick/httpserver"
5
+ require "atom/feed"
6
+
7
+ # syntax: ./atom-server <port> <path>
8
+
9
+ module Atom
10
+ # @docs must implement [], []=, next_id!
11
+ # including servlet must implement gen_id, url_to_key, key_to_url, do_GET
12
+ class MemoryCollection < WEBrick::HTTPServlet::AbstractServlet
13
+ include Atom::AtomPub
14
+
15
+ def initialize server, docs
16
+ super
17
+
18
+ @docs = docs
19
+ end
20
+
21
+ def gen_id key
22
+ key
23
+ end
24
+
25
+ def key_to_url req, key
26
+ req.script_name + "/" + key
27
+ end
28
+
29
+ def url_to_key url
30
+ url.split("/").last
31
+ end
32
+
33
+ def add_edit entry, key
34
+ edit = entry.links.new
35
+ edit["rel"] = "edit"
36
+ edit
37
+ end
38
+
39
+ def do_GET req, res
40
+ res.body = if req.path_info.empty? or req.path_info == "/"
41
+ feed = Atom::Feed.new
42
+
43
+ @docs.each do |key,doc|
44
+ entry = doc.to_atom_entry
45
+ add_edit(entry, key)["href"] = key_to_url(req, key)
46
+ feed << entry
47
+ end
48
+
49
+ feed.entries.first.to_s
50
+ else
51
+ key = url_to_key(req.request_uri.to_s)
52
+ entry = @docs[key].to_atom_entry
53
+ add_edit(entry, key)["href"] = key_to_url(req, key)
54
+ entry.to_s
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ h = WEBrick::HTTPServer.new(:Port => ARGV[0])
61
+
62
+ docs = {}
63
+ docs.instance_variable_set :@last_key, "0"
64
+
65
+ def docs.next_key!
66
+ @last_key.next!
67
+ end
68
+
69
+ h.mount(ARGV[1], Atom::MemoryCollection, docs)
70
+
71
+ h.start