atom-tools 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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