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.
- data/COPYING +18 -0
- data/README +103 -0
- data/Rakefile +77 -0
- data/bin/atom-client.rb +246 -0
- data/bin/atom-server.rb~ +71 -0
- data/doc/classes/Atom/App.html +217 -0
- data/doc/classes/Atom/Author.html +130 -0
- data/doc/classes/Atom/Category.html +128 -0
- data/doc/classes/Atom/Collection.html +322 -0
- data/doc/classes/Atom/Content.html +129 -0
- data/doc/classes/Atom/Contributor.html +119 -0
- data/doc/classes/Atom/Element.html +325 -0
- data/doc/classes/Atom/Entry.html +365 -0
- data/doc/classes/Atom/Feed.html +585 -0
- data/doc/classes/Atom/HTTP.html +374 -0
- data/doc/classes/Atom/Link.html +137 -0
- data/doc/classes/Atom/Text.html +229 -0
- data/doc/classes/XHTML.html +118 -0
- data/doc/created.rid +1 -0
- data/doc/files/README.html +213 -0
- data/doc/files/lib/atom/app_rb.html +110 -0
- data/doc/files/lib/atom/collection_rb.html +110 -0
- data/doc/files/lib/atom/element_rb.html +109 -0
- data/doc/files/lib/atom/entry_rb.html +111 -0
- data/doc/files/lib/atom/feed_rb.html +112 -0
- data/doc/files/lib/atom/http_rb.html +109 -0
- data/doc/files/lib/atom/text_rb.html +108 -0
- data/doc/files/lib/atom/xml_rb.html +110 -0
- data/doc/files/lib/atom/yaml_rb.html +109 -0
- data/doc/fr_class_index.html +39 -0
- data/doc/fr_file_index.html +36 -0
- data/doc/fr_method_index.html +62 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/lib/atom/app.rb +87 -0
- data/lib/atom/collection.rb +75 -0
- data/lib/atom/element.rb +277 -0
- data/lib/atom/entry.rb +135 -0
- data/lib/atom/feed.rb +229 -0
- data/lib/atom/http.rb +132 -0
- data/lib/atom/text.rb +163 -0
- data/lib/atom/xml.rb +200 -0
- data/lib/atom/yaml.rb +101 -0
- data/setup.rb +1585 -0
- data/test/conformance/order.rb +117 -0
- data/test/conformance/title.rb +108 -0
- data/test/conformance/updated.rb +33 -0
- data/test/conformance/xhtmlcontentdiv.rb +18 -0
- data/test/conformance/xmlnamespace.rb +54 -0
- data/test/runtests.rb +14 -0
- data/test/test_constructs.rb +91 -0
- data/test/test_feed.rb +128 -0
- data/test/test_general.rb +99 -0
- data/test/test_http.rb +86 -0
- data/test/test_protocol.rb +69 -0
- data/test/test_xml.rb +353 -0
- 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)
|
data/bin/atom-client.rb
ADDED
@@ -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
|
data/bin/atom-server.rb~
ADDED
@@ -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
|