zookeeper 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.4.0. More attr-readers (StarvingMarvin) and 1.9 compatibility (tsuraan)
2
+
1
3
  v0.3.2. Handle close, closed connections and expired sessions a little more gracefully.
2
4
 
3
5
  v0.3.1. ACL bugfix.
data/Manifest CHANGED
@@ -1,7 +1,9 @@
1
1
  CHANGELOG
2
2
  LICENSE
3
+ Manifest
3
4
  README
4
5
  Rakefile
6
+ examples/cloud_config.rb
5
7
  ext/extconf.rb
6
8
  ext/zkc-3.3.1.tar.gz
7
9
  ext/zookeeper_c.c
@@ -13,4 +15,9 @@ lib/zookeeper/callbacks.rb
13
15
  lib/zookeeper/constants.rb
14
16
  lib/zookeeper/exceptions.rb
15
17
  lib/zookeeper/stat.rb
16
- Manifest
18
+ test/test_basic.rb
19
+ test/test_callback1.rb
20
+ test/test_close.rb
21
+ test/test_esoteric.rb
22
+ test/test_watcher1.rb
23
+ test/test_watcher2.rb
@@ -0,0 +1,125 @@
1
+ require "rubygems"
2
+ require "zookeeper"
3
+
4
+ # A basic cloud-based YAML config library. Ruby Zookeeper client example.
5
+ #
6
+ # If you pass in a file as 'zk:/foo.yml/blah' it will go out to zookeeper.
7
+ # Otherwise the file is assumed to be local. The yml file will get parsed
8
+ # and cached locally, and keys after the .yml get interpreted as keys into
9
+ # the YAML.
10
+ #
11
+ # e.g. get(zk:/config/service.yml/key1/key2/key3..) =>
12
+ # zk.get(:path => /config/service.yml)
13
+ # yaml <= YAML.parse(data)
14
+ # yaml[key1][key2][key3]...
15
+ #
16
+ # If keys are unspecified, it returns the parsed YAML as one big object
17
+ #
18
+ # TODO if staleness is set to 0, read in YAML immediately before next
19
+ # get(...)
20
+
21
+ class CloudConfig
22
+ class NodeNotFound < StandardError; end
23
+ class BadPathError < StandardError; end
24
+
25
+ DEFAULT_SERVERS = "localhost:2181"
26
+
27
+ def initialize(zkservers = DEFAULT_SERVERS, staleness = 15) # maximum allowed staleness in seconds
28
+ @staleness = staleness
29
+ @lock = Mutex.new
30
+ @zkservers = DEFAULT_SERVERS
31
+
32
+ # cache
33
+ @data = {}
34
+ @zkcb = Zookeeper::WatcherCallback.new { dirty_callback(@zkcb.context) }
35
+ @zk = nil
36
+ end
37
+
38
+ def get(node)
39
+ filename, keys = extract_filename(node)
40
+
41
+ # read(filename) is potentially a zk call, so do not hold the lock during the read
42
+ if @lock.synchronize { !@data.has_key?(filename) }
43
+ d = YAML.load(read(filename))
44
+ @lock.synchronize { @data[filename] = d }
45
+ end
46
+
47
+ # synchronized b/c we potentially have a background thread updating data nodes from zk
48
+ # if keys is empty, return the whole file, otherwise roll up the keys
49
+ @lock.synchronize {
50
+ keys.empty? ? @data[filename] : keys.inject(@data[filename]) { |hash, key| hash[key] }
51
+ }
52
+ end
53
+
54
+ # todo:
55
+ # factor get-and-watch into a different subsystem (so you can have
56
+ # polling stat() ops on local filesystem.)
57
+ def read(yaml)
58
+ # read yaml file and register watcher. if watcher fires, set up
59
+ # background thread to do read and update data.
60
+ if yaml.match(/^zk:/)
61
+ @zk ||= init_zk
62
+ yaml = yaml['zk:'.length..-1] # strip off zk: from zk:/config/path.yml
63
+ resp = get_and_register(yaml)
64
+
65
+ if resp[:rc] != Zookeeper::ZOK
66
+ @zk.unregister_watcher(resp[:req_id])
67
+ raise NodeNotFound
68
+ end
69
+
70
+ resp[:data]
71
+ else
72
+ raise NodeNotFound unless File.exists?(yaml)
73
+ File.read(yaml)
74
+ end
75
+ end
76
+
77
+ def extract_filename(node)
78
+ path_elements = node.split("/")
79
+
80
+ yamlindex = path_elements.map{ |x| x.match("\.yml$") != nil }.index(true)
81
+ raise BadPathError unless yamlindex
82
+
83
+ yamlname = path_elements[0..yamlindex].join '/'
84
+ yamlkeys = path_elements[(yamlindex+1)..-1]
85
+
86
+ return yamlname, yamlkeys
87
+ end
88
+
89
+ private
90
+ def init_zk
91
+ Zookeeper.new(@zkservers)
92
+ end
93
+
94
+ def get_and_register(znode)
95
+ @zk.get(:path => znode, :watcher => @zkcb,
96
+ :watcher_context => { :path => znode,
97
+ :wait => rand(@staleness) })
98
+ end
99
+
100
+ def dirty_callback(context)
101
+ path = context[:path]
102
+ wait = context[:wait]
103
+
104
+ # Fire off a background update that waits a randomized period of time up
105
+ # to @staleness seconds.
106
+ Thread.new do
107
+ sleep wait
108
+ background_update(path)
109
+ end
110
+ end
111
+
112
+ def background_update(zkpath)
113
+ # do a synchronous get/register a new watcher
114
+ resp = get_and_register(zkpath)
115
+ if resp[:rc] != Zookeeper::ZOK
116
+ # puts "Unable to read #{zkpath} from Zookeeper!" @logger.error
117
+ zk.unregister_watcher(resp[:req_id])
118
+ else
119
+ # puts "Updating data."
120
+ d = YAML.load(resp[:data])
121
+ @lock.synchronize { @data["zk:#{zkpath}"] = d }
122
+ end
123
+ end
124
+ end
125
+
data/ext/zookeeper_c.c CHANGED
@@ -75,7 +75,7 @@ static VALUE method_init(VALUE self, VALUE hostPort) {
75
75
 
76
76
  zk_local_ctx->zh =
77
77
  zookeeper_init(
78
- RSTRING(hostPort)->ptr,
78
+ RSTRING_PTR(hostPort),
79
79
  zkrb_state_callback,
80
80
  10000,
81
81
  &zk_local_ctx->myid,
@@ -127,19 +127,19 @@ static VALUE method_get_children(VALUE self, VALUE reqid, VALUE path, VALUE asyn
127
127
  int rc;
128
128
  switch (call_type) {
129
129
  case SYNC:
130
- rc = zoo_get_children2(zk->zh, RSTRING(path)->ptr, 0, &strings, &stat);
130
+ rc = zoo_get_children2(zk->zh, RSTRING_PTR(path), 0, &strings, &stat);
131
131
  break;
132
132
 
133
133
  case SYNC_WATCH:
134
- rc = zoo_wget_children2(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, &strings, &stat);
134
+ rc = zoo_wget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &strings, &stat);
135
135
  break;
136
136
 
137
137
  case ASYNC:
138
- rc = zoo_aget_children2(zk->zh, RSTRING(path)->ptr, 0, zkrb_strings_stat_callback, data_ctx);
138
+ rc = zoo_aget_children2(zk->zh, RSTRING_PTR(path), 0, zkrb_strings_stat_callback, data_ctx);
139
139
  break;
140
140
 
141
141
  case ASYNC_WATCH:
142
- rc = zoo_awget_children2(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, zkrb_strings_stat_callback, data_ctx);
142
+ rc = zoo_awget_children2(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_strings_stat_callback, data_ctx);
143
143
  break;
144
144
  }
145
145
 
@@ -160,19 +160,19 @@ static VALUE method_exists(VALUE self, VALUE reqid, VALUE path, VALUE async, VAL
160
160
  int rc;
161
161
  switch (call_type) {
162
162
  case SYNC:
163
- rc = zoo_exists(zk->zh, RSTRING(path)->ptr, 0, &stat);
163
+ rc = zoo_exists(zk->zh, RSTRING_PTR(path), 0, &stat);
164
164
  break;
165
165
 
166
166
  case SYNC_WATCH:
167
- rc = zoo_wexists(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, &stat);
167
+ rc = zoo_wexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, &stat);
168
168
  break;
169
169
 
170
170
  case ASYNC:
171
- rc = zoo_aexists(zk->zh, RSTRING(path)->ptr, 0, zkrb_stat_callback, data_ctx);
171
+ rc = zoo_aexists(zk->zh, RSTRING_PTR(path), 0, zkrb_stat_callback, data_ctx);
172
172
  break;
173
173
 
174
174
  case ASYNC_WATCH:
175
- rc = zoo_awexists(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, zkrb_stat_callback, data_ctx);
175
+ rc = zoo_awexists(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_stat_callback, data_ctx);
176
176
  break;
177
177
  }
178
178
 
@@ -191,8 +191,8 @@ static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALU
191
191
  struct Stat stat;
192
192
  if (data != Qnil) Check_Type(data, T_STRING);
193
193
  Check_Type(flags, T_FIXNUM);
194
- const char *data_ptr = (data == Qnil) ? NULL : RSTRING(data)->ptr;
195
- size_t data_len = (data == Qnil) ? -1 : RSTRING(data)->len;
194
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
195
+ size_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
196
196
 
197
197
  struct ACL_vector *aclptr = NULL;
198
198
  if (acls != Qnil) { aclptr = zkrb_ruby_to_aclvector(acls); }
@@ -201,11 +201,11 @@ static VALUE method_create(VALUE self, VALUE reqid, VALUE path, VALUE data, VALU
201
201
  int rc;
202
202
  switch (call_type) {
203
203
  case SYNC:
204
- rc = zoo_create(zk->zh, RSTRING(path)->ptr, data_ptr, data_len, aclptr, FIX2INT(flags), realpath, sizeof(realpath));
204
+ rc = zoo_create(zk->zh, RSTRING_PTR(path), data_ptr, data_len, aclptr, FIX2INT(flags), realpath, sizeof(realpath));
205
205
  if (aclptr != NULL) deallocate_ACL_vector(aclptr);
206
206
  break;
207
207
  case ASYNC:
208
- rc = zoo_acreate(zk->zh, RSTRING(path)->ptr, data_ptr, data_len, aclptr, FIX2INT(flags), zkrb_string_callback, data_ctx);
208
+ rc = zoo_acreate(zk->zh, RSTRING_PTR(path), data_ptr, data_len, aclptr, FIX2INT(flags), zkrb_string_callback, data_ctx);
209
209
  if (aclptr != NULL) deallocate_ACL_vector(aclptr);
210
210
  break;
211
211
  default:
@@ -230,10 +230,10 @@ static VALUE method_delete(VALUE self, VALUE reqid, VALUE path, VALUE version, V
230
230
  int rc = 0;
231
231
  switch (call_type) {
232
232
  case SYNC:
233
- rc = zoo_delete(zk->zh, RSTRING(path)->ptr, FIX2INT(version));
233
+ rc = zoo_delete(zk->zh, RSTRING_PTR(path), FIX2INT(version));
234
234
  break;
235
235
  case ASYNC:
236
- rc = zoo_adelete(zk->zh, RSTRING(path)->ptr, FIX2INT(version), zkrb_void_callback, data_ctx);
236
+ rc = zoo_adelete(zk->zh, RSTRING_PTR(path), FIX2INT(version), zkrb_void_callback, data_ctx);
237
237
  break;
238
238
  default:
239
239
  /* TODO(wickman) raise proper argument error */
@@ -258,19 +258,19 @@ static VALUE method_get(VALUE self, VALUE reqid, VALUE path, VALUE async, VALUE
258
258
 
259
259
  switch (call_type) {
260
260
  case SYNC:
261
- rc = zoo_get(zk->zh, RSTRING(path)->ptr, 0, data, &data_len, &stat);
261
+ rc = zoo_get(zk->zh, RSTRING_PTR(path), 0, data, &data_len, &stat);
262
262
  break;
263
263
 
264
264
  case SYNC_WATCH:
265
- rc = zoo_wget(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, data, &data_len, &stat);
265
+ rc = zoo_wget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, data, &data_len, &stat);
266
266
  break;
267
267
 
268
268
  case ASYNC:
269
- rc = zoo_aget(zk->zh, RSTRING(path)->ptr, 0, zkrb_data_callback, data_ctx);
269
+ rc = zoo_aget(zk->zh, RSTRING_PTR(path), 0, zkrb_data_callback, data_ctx);
270
270
  break;
271
271
 
272
272
  case ASYNC_WATCH:
273
- rc = zoo_awget(zk->zh, RSTRING(path)->ptr, zkrb_state_callback, watch_ctx, zkrb_data_callback, data_ctx);
273
+ rc = zoo_awget(zk->zh, RSTRING_PTR(path), zkrb_state_callback, watch_ctx, zkrb_data_callback, data_ctx);
274
274
  break;
275
275
  }
276
276
 
@@ -291,16 +291,16 @@ static VALUE method_set(VALUE self, VALUE reqid, VALUE path, VALUE data, VALUE a
291
291
 
292
292
  struct Stat stat;
293
293
  if (data != Qnil) Check_Type(data, T_STRING);
294
- const char *data_ptr = (data == Qnil) ? NULL : RSTRING(data)->ptr;
295
- size_t data_len = (data == Qnil) ? -1 : RSTRING(data)->len;
294
+ const char *data_ptr = (data == Qnil) ? NULL : RSTRING_PTR(data);
295
+ size_t data_len = (data == Qnil) ? -1 : RSTRING_LEN(data);
296
296
 
297
297
  int rc;
298
298
  switch (call_type) {
299
299
  case SYNC:
300
- rc = zoo_set2(zk->zh, RSTRING(path)->ptr, data_ptr, data_len, FIX2INT(version), &stat);
300
+ rc = zoo_set2(zk->zh, RSTRING_PTR(path), data_ptr, data_len, FIX2INT(version), &stat);
301
301
  break;
302
302
  case ASYNC:
303
- rc = zoo_aset(zk->zh, RSTRING(path)->ptr, data_ptr, data_len, FIX2INT(version),
303
+ rc = zoo_aset(zk->zh, RSTRING_PTR(path), data_ptr, data_len, FIX2INT(version),
304
304
  zkrb_stat_callback, data_ctx);
305
305
  break;
306
306
  default:
@@ -325,11 +325,11 @@ static VALUE method_set_acl(VALUE self, VALUE reqid, VALUE path, VALUE acls, VAL
325
325
  int rc;
326
326
  switch (call_type) {
327
327
  case SYNC:
328
- rc = zoo_set_acl(zk->zh, RSTRING(path)->ptr, FIX2INT(version), aclptr);
328
+ rc = zoo_set_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr);
329
329
  deallocate_ACL_vector(aclptr);
330
330
  break;
331
331
  case ASYNC:
332
- rc = zoo_aset_acl(zk->zh, RSTRING(path)->ptr, FIX2INT(version), aclptr, zkrb_void_callback, data_ctx);
332
+ rc = zoo_aset_acl(zk->zh, RSTRING_PTR(path), FIX2INT(version), aclptr, zkrb_void_callback, data_ctx);
333
333
  deallocate_ACL_vector(aclptr);
334
334
  break;
335
335
  default:
@@ -351,10 +351,10 @@ static VALUE method_get_acl(VALUE self, VALUE reqid, VALUE path, VALUE async) {
351
351
  int rc;
352
352
  switch (call_type) {
353
353
  case SYNC:
354
- rc = zoo_get_acl(zk->zh, RSTRING(path)->ptr, &acls, &stat);
354
+ rc = zoo_get_acl(zk->zh, RSTRING_PTR(path), &acls, &stat);
355
355
  break;
356
356
  case ASYNC:
357
- rc = zoo_aget_acl(zk->zh, RSTRING(path)->ptr, zkrb_acl_callback, data_ctx);
357
+ rc = zoo_aget_acl(zk->zh, RSTRING_PTR(path), zkrb_acl_callback, data_ctx);
358
358
  break;
359
359
  default:
360
360
  /* TODO(wickman) raise proper argument error */
data/ext/zookeeper_lib.c CHANGED
@@ -411,7 +411,7 @@ struct ACL_vector * zkrb_ruby_to_aclvector(VALUE acl_ary) {
411
411
  Check_Type(acl_ary, T_ARRAY);
412
412
 
413
413
  struct ACL_vector *v = malloc(sizeof(struct ACL_vector));
414
- allocate_ACL_vector(v, RARRAY(acl_ary)->len);
414
+ allocate_ACL_vector(v, RARRAY_LEN(acl_ary));
415
415
 
416
416
  int k;
417
417
  for (k = 0; k < v->count; ++k) {
@@ -442,17 +442,17 @@ struct Id zkrb_ruby_to_id(VALUE rubyid) {
442
442
  VALUE ident = rb_iv_get(rubyid, "@id");
443
443
 
444
444
  if (scheme != Qnil) {
445
- id.scheme = malloc(RSTRING(scheme)->len + 1);
446
- strncpy(id.scheme, RSTRING(scheme)->ptr, RSTRING(scheme)->len);
447
- id.scheme[RSTRING(scheme)->len] = '\0';
445
+ id.scheme = malloc(RSTRING_LEN(scheme) + 1);
446
+ strncpy(id.scheme, RSTRING_PTR(scheme), RSTRING_LEN(scheme));
447
+ id.scheme[RSTRING_LEN(scheme)] = '\0';
448
448
  } else {
449
449
  id.scheme = NULL;
450
450
  }
451
451
 
452
452
  if (ident != Qnil) {
453
- id.id = malloc(RSTRING(ident)->len + 1);
454
- strncpy(id.id, RSTRING(ident)->ptr, RSTRING(ident)->len);
455
- id.id[RSTRING(ident)->len] = '\0';
453
+ id.id = malloc(RSTRING_LEN(ident) + 1);
454
+ strncpy(id.id, RSTRING_PTR(ident), RSTRING_LEN(ident));
455
+ id.id[RSTRING_LEN(ident)] = '\0';
456
456
  } else {
457
457
  id.id = NULL;
458
458
  }
data/ext/zookeeper_lib.h CHANGED
@@ -11,6 +11,16 @@
11
11
  #define ZK_FALSE 0
12
12
  #define ZKRB_GLOBAL_REQ -1
13
13
 
14
+ #ifndef RSTRING_LEN
15
+ # define RSTRING_LEN(x) RSTRING(x)->len
16
+ #endif
17
+ #ifndef RSTRING_PTR
18
+ # define RSTRING_PTR(x) RSTRING(x)->ptr
19
+ #endif
20
+ #ifndef RARRAY_LEN
21
+ # define RARRAY_LEN(x) RARRAY(x)->len
22
+ #endif
23
+
14
24
  extern int ZKRBDebugging;
15
25
 
16
26
  struct zkrb_data_completion {
@@ -1,6 +1,6 @@
1
1
  module ZookeeperStat
2
2
  class Stat
3
- attr_reader :version, :exists
3
+ attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :cverzion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid
4
4
  def initialize(val)
5
5
  @exists = !!val
6
6
  @czxid, @mzxid, @ctime, @mtime, @version, @cversion, @aversion,
data/zookeeper.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{zookeeper}
5
- s.version = "0.3.2"
5
+ s.version = "0.4.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman"]
9
- s.date = %q{2010-08-17}
9
+ s.date = %q{2010-12-01}
10
10
  s.description = %q{An interface to the Zookeeper distributed configuration server.}
11
11
  s.email = %q{}
12
12
  s.extensions = ["ext/extconf.rb"]
13
13
  s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "ext/zookeeper_c.c", "lib/zookeeper.rb"]
14
- s.files = ["CHANGELOG", "LICENSE", "README", "Rakefile", "ext/extconf.rb", "ext/zkc-3.3.1.tar.gz", "ext/zookeeper_c.c", "ext/zookeeper_lib.c", "ext/zookeeper_lib.h", "lib/zookeeper.rb", "lib/zookeeper/acls.rb", "lib/zookeeper/callbacks.rb", "lib/zookeeper/constants.rb", "lib/zookeeper/exceptions.rb", "lib/zookeeper/stat.rb", "Manifest", "zookeeper.gemspec", "test/test_basic.rb", "test/test_callback1.rb", "test/test_close.rb", "test/test_esoteric.rb", "test/test_watcher1.rb", "test/test_watcher2.rb"]
14
+ s.files = ["CHANGELOG", "LICENSE", "README", "Rakefile", "examples/cloud_config.rb", "ext/extconf.rb", "ext/zkc-3.3.1.tar.gz", "ext/zookeeper_c.c", "ext/zookeeper_lib.c", "ext/zookeeper_lib.h", "lib/zookeeper.rb", "lib/zookeeper/acls.rb", "lib/zookeeper/callbacks.rb", "lib/zookeeper/constants.rb", "lib/zookeeper/exceptions.rb", "lib/zookeeper/stat.rb", "test/test_basic.rb", "test/test_callback1.rb", "test/test_close.rb", "test/test_esoteric.rb", "test/test_watcher1.rb", "test/test_watcher2.rb", "Manifest", "zookeeper.gemspec"]
15
15
  s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/zookeeper/}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Zookeeper", "--main", "README"]
17
17
  s.require_paths = ["lib", "ext"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zookeeper
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 2
10
- version: 0.3.2
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Phillip Pearson, Eric Maland, Evan Weaver, Brian Wickman
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-17 00:00:00 -07:00
18
+ date: 2010-12-01 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -36,6 +36,7 @@ files:
36
36
  - LICENSE
37
37
  - README
38
38
  - Rakefile
39
+ - examples/cloud_config.rb
39
40
  - ext/extconf.rb
40
41
  - ext/zkc-3.3.1.tar.gz
41
42
  - ext/zookeeper_c.c
@@ -47,14 +48,14 @@ files:
47
48
  - lib/zookeeper/constants.rb
48
49
  - lib/zookeeper/exceptions.rb
49
50
  - lib/zookeeper/stat.rb
50
- - Manifest
51
- - zookeeper.gemspec
52
51
  - test/test_basic.rb
53
52
  - test/test_callback1.rb
54
53
  - test/test_close.rb
55
54
  - test/test_esoteric.rb
56
55
  - test/test_watcher1.rb
57
56
  - test/test_watcher2.rb
57
+ - Manifest
58
+ - zookeeper.gemspec
58
59
  has_rdoc: true
59
60
  homepage: http://blog.evanweaver.com/files/doc/fauna/zookeeper/
60
61
  licenses: []