ruby_doozer 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +49 -0
- data/lib/ruby_doozer/cached_registry.rb +128 -0
- data/lib/ruby_doozer/registry.rb +69 -114
- data/lib/ruby_doozer/version.rb +1 -1
- data/lib/ruby_doozer.rb +3 -2
- data/test/cached_registry_test.rb +152 -0
- data/test/registry_test.rb +65 -50
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef156e41c4d70417b9ccb4128c22b817c926c474
|
4
|
+
data.tar.gz: c8c054bea13c4933368e12838526de1febe4e37a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9483ea907342682619bb3e44a5bffa9c63b0f9519fedc9ced96b7e4b579fe07c2e6ab73f259a6711db5ad1f9f0b130f8d84187336523ac8660030121f57d81ef
|
7
|
+
data.tar.gz: 8c5c70714dff4fe12c1cb7fda7573bcb37cb73f927f388460a6e0772d66d3e07778ea218d4d730b6c89af09255b7404e125c890a235eee3550fdb9b0cceca1b0
|
data/README.md
CHANGED
@@ -68,11 +68,60 @@ sleep 0.5
|
|
68
68
|
database_config = config_registry['master']
|
69
69
|
slave_database_config = config_registry['secondary']
|
70
70
|
|
71
|
+
# Register for any changes to the configuration, including updates
|
72
|
+
config_registry.on_update('master') do |path, value|
|
73
|
+
puts "Time to re-establish database connections to new server: #{value}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Change the configuration and all subscribers will be notified
|
77
|
+
config_registry['master'] = "Some updated JSON config string"
|
78
|
+
```
|
79
|
+
|
80
|
+
### Cached Registry
|
81
|
+
|
82
|
+
Cached Registry is a specialized registry that keeps a local copy of the entire
|
83
|
+
registry in memory. It also keeps the local copy synchronized with any changes
|
84
|
+
that occur in doozer
|
85
|
+
|
86
|
+
The local copy is useful for scenarios where frequent reads are being
|
87
|
+
performed against the Registry and the data must be kept up to date.
|
88
|
+
|
89
|
+
Cached Registry can also distinguish between creates and updates.
|
90
|
+
The new #on_update callbacks will be called when existing data has been modified.
|
91
|
+
As a result Registry#on_update callbacks will only be called for existing data
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
require 'rubygems'
|
95
|
+
require 'ruby_doozer'
|
96
|
+
|
97
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('registry.log')
|
98
|
+
|
99
|
+
region = "Development"
|
100
|
+
application = "sprites"
|
101
|
+
path = "/#{region}/#{application}/config/resources".downcase
|
102
|
+
|
103
|
+
config_registry = RubyDoozer::CachedRegistry.new(:root_path => path)
|
104
|
+
|
105
|
+
# Store the configuration information in doozer as a serialized string
|
106
|
+
config_registry['master'] = "Some JSON config string"
|
107
|
+
|
108
|
+
# Allow time for Doozer to publish the new config
|
109
|
+
sleep 0.5
|
110
|
+
|
111
|
+
# Retrieve the current configuration
|
112
|
+
database_config = config_registry['master']
|
113
|
+
slave_database_config = config_registry['secondary']
|
114
|
+
|
71
115
|
# Register for any changes to the configuration
|
72
116
|
config_registry.on_update('master') do |path, value|
|
73
117
|
puts "Time to re-establish database connections to new server: #{value}"
|
74
118
|
end
|
75
119
|
|
120
|
+
# Register for all create events
|
121
|
+
config_registry.on_create('*') do |path, value|
|
122
|
+
puts "CREATED #{path}"
|
123
|
+
end
|
124
|
+
|
76
125
|
# Change the configuration and all subscribers will be notified
|
77
126
|
config_registry['master'] = "Some updated JSON config string"
|
78
127
|
```
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
require 'gene_pool'
|
3
|
+
require 'semantic_logger'
|
4
|
+
|
5
|
+
#
|
6
|
+
# CachedRegistry
|
7
|
+
#
|
8
|
+
# Store information in doozer and subscribe to future changes
|
9
|
+
# and keep a local copy of the information in doozer
|
10
|
+
#
|
11
|
+
# Notifies registered subscribers when information has changed
|
12
|
+
#
|
13
|
+
# All paths specified are relative to the root_path. As such the root path
|
14
|
+
# is never returned, nor is it required when a path is supplied as input.
|
15
|
+
# For example, with a root_path of /foo/bar, any paths passed in will leave
|
16
|
+
# out the root_path: host/name
|
17
|
+
#
|
18
|
+
# Keeps a local copy in memory of all descendant values of the supplied root_path
|
19
|
+
# Allows high-frequency calls to retrieve registry data
|
20
|
+
# The cache will be kept in synch with any changes on the server
|
21
|
+
module RubyDoozer
|
22
|
+
class CachedRegistry < Registry
|
23
|
+
# Logging instance for this class
|
24
|
+
include SemanticLogger::Loggable
|
25
|
+
|
26
|
+
# Create a Registry instance to manage a path of information within doozer
|
27
|
+
#
|
28
|
+
# See RubyDoozer::Registry for complete list of options
|
29
|
+
#
|
30
|
+
def initialize(params)
|
31
|
+
super
|
32
|
+
@registry = ThreadSafe::Hash.new
|
33
|
+
|
34
|
+
path = "#{@root_path}/**"
|
35
|
+
doozer_pool.with_connection do |doozer|
|
36
|
+
@current_revision = doozer.current_revision
|
37
|
+
# Fetch all the configuration information from Doozer and set the internal copy
|
38
|
+
doozer.walk(path, @current_revision).each do |node|
|
39
|
+
@registry[relative_path(node.path)] = node.value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Start monitoring thread
|
44
|
+
monitor_thread
|
45
|
+
end
|
46
|
+
|
47
|
+
# Retrieve the latest value from a specific path from the registry
|
48
|
+
def [](path)
|
49
|
+
@registry[path]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Iterate over every key, value pair in the registry at the root_path
|
53
|
+
#
|
54
|
+
# Example:
|
55
|
+
# registry.each_pair {|k,v| puts "#{k} => #{v}"}
|
56
|
+
def each_pair(&block)
|
57
|
+
# Have to duplicate the registry otherwise changes to the registry will
|
58
|
+
# interfere with the iterator
|
59
|
+
@registry.dup.each_pair(&block)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns [Array<String>] all paths in the registry
|
63
|
+
def paths
|
64
|
+
@registry.keys
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a copy of the registry as a Hash
|
68
|
+
def to_h
|
69
|
+
@registry.dup
|
70
|
+
end
|
71
|
+
|
72
|
+
# When an entry is created the block will be called
|
73
|
+
# Parameters
|
74
|
+
# path
|
75
|
+
# The relative path to watch for changes
|
76
|
+
# block
|
77
|
+
# The block to be called
|
78
|
+
#
|
79
|
+
# Parameters passed to the block:
|
80
|
+
# path
|
81
|
+
# The path that was created
|
82
|
+
# Supplying a path of '*' means all paths
|
83
|
+
# Default: '*'
|
84
|
+
#
|
85
|
+
# value
|
86
|
+
# New value from doozer
|
87
|
+
#
|
88
|
+
# Example:
|
89
|
+
# registry.on_update do |path, value|
|
90
|
+
# puts "#{path} was created with #{value}"
|
91
|
+
# end
|
92
|
+
def on_create(path='*', &block)
|
93
|
+
((@create_subscribers ||= ThreadSafe::Hash.new)[path] ||= ThreadSafe::Array.new) << block
|
94
|
+
end
|
95
|
+
|
96
|
+
############################
|
97
|
+
protected
|
98
|
+
|
99
|
+
# The path has been added or updated in the registry
|
100
|
+
def changed(path, value)
|
101
|
+
previous_value = @registry[path]
|
102
|
+
|
103
|
+
# Update in memory copy
|
104
|
+
@registry[path] = value
|
105
|
+
|
106
|
+
# It is an update if we already have a value
|
107
|
+
if previous_value
|
108
|
+
# Call parent which will notify Updated Subscribers
|
109
|
+
super
|
110
|
+
else
|
111
|
+
logger.debug { "Created: #{path} => #{value}" }
|
112
|
+
|
113
|
+
return unless @create_subscribers
|
114
|
+
|
115
|
+
# Subscribers to specific paths
|
116
|
+
if subscribers = @create_subscribers[path]
|
117
|
+
subscribers.each{|subscriber| subscriber.call(path, value)}
|
118
|
+
end
|
119
|
+
|
120
|
+
# Any subscribers for all events?
|
121
|
+
if all_subscribers = @create_subscribers['*']
|
122
|
+
all_subscribers.each{|subscriber| subscriber.call(path, value)}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
data/lib/ruby_doozer/registry.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'thread_safe'
|
2
2
|
require 'gene_pool'
|
3
3
|
require 'semantic_logger'
|
4
|
+
require 'sync_attr'
|
4
5
|
|
5
6
|
#
|
6
7
|
# Registry
|
@@ -16,10 +17,11 @@ require 'semantic_logger'
|
|
16
17
|
#
|
17
18
|
module RubyDoozer
|
18
19
|
class Registry
|
20
|
+
include SyncAttr
|
19
21
|
# Logging instance for this class
|
20
22
|
include SemanticLogger::Loggable
|
21
23
|
|
22
|
-
attr_reader :doozer_config, :doozer_pool
|
24
|
+
attr_reader :doozer_config, :doozer_pool, :current_revision
|
23
25
|
|
24
26
|
# Create a Registry instance to manage a path of information within doozer
|
25
27
|
#
|
@@ -29,12 +31,6 @@ module RubyDoozer
|
|
29
31
|
# significant traffic since it will also monitor Doozer Admin changes
|
30
32
|
# Mandatory
|
31
33
|
#
|
32
|
-
# :cache [Boolean]
|
33
|
-
# Keep a local copy in memory of all descendant values of the supplied root_path
|
34
|
-
# Allows high-frequency calls to retrieve registry data
|
35
|
-
# The cache will be kept in synch with any changes on the server
|
36
|
-
# Default: true
|
37
|
-
#
|
38
34
|
# :doozer [Hash]
|
39
35
|
# Doozer configuration information
|
40
36
|
#
|
@@ -98,9 +94,6 @@ module RubyDoozer
|
|
98
94
|
@root_path = @root_path[0..-2] if @root_path.end_with?("/")
|
99
95
|
@root_path_with_trail = "#{@root_path}/"
|
100
96
|
|
101
|
-
local_copy = params.delete(:cache)
|
102
|
-
@registry = (local_copy == false) ? nil : ThreadSafe::Hash.new
|
103
|
-
|
104
97
|
@doozer_config = params.delete(:doozer) || {}
|
105
98
|
@doozer_config[:servers] ||= ['127.0.0.1:8046']
|
106
99
|
@doozer_config[:read_timeout] ||= 5
|
@@ -122,47 +115,23 @@ module RubyDoozer
|
|
122
115
|
RubyDoozer::Client.new(@doozer_config)
|
123
116
|
end
|
124
117
|
|
125
|
-
@create_subscribers = ThreadSafe::Hash.new
|
126
|
-
@update_subscribers = ThreadSafe::Hash.new
|
127
|
-
@delete_subscribers = ThreadSafe::Hash.new
|
128
|
-
|
129
|
-
revision = nil
|
130
|
-
path = "#{@root_path}/**"
|
131
|
-
doozer_pool.with_connection do |doozer|
|
132
|
-
revision = doozer.current_revision
|
133
|
-
if @registry
|
134
|
-
# Fetch all the configuration information from Doozer and set the internal copy
|
135
|
-
doozer.walk(path, revision).each do |node|
|
136
|
-
@registry[relative_path(node.path)] = node.value
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Start monitoring thread to keep the registry in synch with doozer
|
142
|
-
@monitor_thread = Thread.new { watch_registry(path, revision + 1) }
|
143
|
-
|
144
118
|
# Generate warning log entries for any unknown configuration options
|
145
119
|
params.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: #{k}"}
|
146
120
|
end
|
147
121
|
|
122
|
+
# Start callback monitoring thread
|
123
|
+
sync_attr_reader :monitor_thread do
|
124
|
+
Thread.new { watch_registry }
|
125
|
+
end
|
126
|
+
|
148
127
|
# Retrieve the latest value from a specific path from the registry
|
149
|
-
# If :cache was set to false on the initializer this call will
|
150
|
-
# make a network call to doozer to retrieve the current value
|
151
|
-
# Otherwise it is an in memory call and can be called frequently
|
152
128
|
def [](path)
|
153
|
-
|
154
|
-
|
155
|
-
else
|
156
|
-
doozer_pool.with_connection do |doozer|
|
157
|
-
doozer[full_path(path)]
|
158
|
-
end
|
129
|
+
doozer_pool.with_connection do |doozer|
|
130
|
+
doozer[full_path(path)]
|
159
131
|
end
|
160
132
|
end
|
161
133
|
|
162
134
|
# Replace the latest value at a specific path
|
163
|
-
# The in-memory copy will be updated when doozer sends out the change
|
164
|
-
# TODO Should we also update the in memory copy rather than wait for the change
|
165
|
-
# notification?
|
166
135
|
def []=(path,value)
|
167
136
|
doozer_pool.with_connection do |doozer|
|
168
137
|
doozer[full_path(path)] = value
|
@@ -170,15 +139,10 @@ module RubyDoozer
|
|
170
139
|
end
|
171
140
|
|
172
141
|
# Delete the value at a specific path
|
173
|
-
# The in-memory copy will be updated when doozer sends out the change
|
174
|
-
# TODO Should we also delete the in memory copy rather than wait for the change
|
175
|
-
# notification?
|
176
142
|
def delete(path)
|
177
|
-
old_value = @registry[path] if @registry
|
178
143
|
doozer_pool.with_connection do |doozer|
|
179
144
|
doozer.delete(full_path(path))
|
180
145
|
end
|
181
|
-
old_value
|
182
146
|
end
|
183
147
|
|
184
148
|
# Iterate over every key, value pair in the registry at the root_path
|
@@ -190,27 +154,26 @@ module RubyDoozer
|
|
190
154
|
# Example:
|
191
155
|
# registry.each_pair {|k,v| puts "#{k} => #{v}"}
|
192
156
|
def each_pair(&block)
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
doozer_pool.with_connection do |doozer|
|
198
|
-
doozer.walk(path, revision).each do |node|
|
199
|
-
block.call(relative_path(node.path), node.value)
|
200
|
-
end
|
157
|
+
path = "#{@root_path}/**"
|
158
|
+
doozer_pool.with_connection do |doozer|
|
159
|
+
doozer.walk(path, doozer.current_revision).each do |node|
|
160
|
+
block.call(relative_path(node.path), node.value)
|
201
161
|
end
|
202
162
|
end
|
203
163
|
end
|
204
164
|
|
165
|
+
# Returns [Array<String>] all paths in the registry
|
166
|
+
def paths
|
167
|
+
paths = []
|
168
|
+
each_pair {|k,v| paths << k}
|
169
|
+
paths
|
170
|
+
end
|
171
|
+
|
205
172
|
# Returns a copy of the registry as a Hash
|
206
173
|
def to_h
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
h = {}
|
211
|
-
each_pair {|k,v| h[k] = v}
|
212
|
-
h
|
213
|
-
end
|
174
|
+
h = {}
|
175
|
+
each_pair {|k,v| h[k] = v}
|
176
|
+
h
|
214
177
|
end
|
215
178
|
|
216
179
|
# Cleanup on process termination
|
@@ -224,39 +187,18 @@ module RubyDoozer
|
|
224
187
|
@doozer_pool = nil
|
225
188
|
end
|
226
189
|
|
227
|
-
# When an entry is created the block will be called
|
228
|
-
# Parameters
|
229
|
-
# path
|
230
|
-
# The relative path _excluding_ the root_path to watch for changes
|
231
|
-
# #TODO Or a regular expression
|
232
|
-
# block
|
233
|
-
# The block to be called
|
234
|
-
#
|
235
|
-
# Parameters passed to the block:
|
236
|
-
# path
|
237
|
-
# The path that was created
|
238
|
-
#
|
239
|
-
# value
|
240
|
-
# New value from doozer
|
241
|
-
#
|
242
|
-
# Example:
|
243
|
-
# registry.on_update do |path, value|
|
244
|
-
# puts "#{path} was created with #{value}"
|
245
|
-
# end
|
246
|
-
def on_create(path, &block)
|
247
|
-
(@create_subscribers[path] ||= ThreadSafe::Array.new) << block
|
248
|
-
end
|
249
|
-
|
250
190
|
# When an entry is updated the block will be called
|
251
191
|
# Parameters
|
252
192
|
# path
|
253
|
-
# The relative path
|
193
|
+
# The relative path to watch for changes
|
254
194
|
# block
|
255
195
|
# The block to be called
|
256
196
|
#
|
257
197
|
# Parameters passed to the block:
|
258
198
|
# path
|
259
|
-
# The path that was
|
199
|
+
# The path that was updated in doozer
|
200
|
+
# Supplying a path of '*' means all paths
|
201
|
+
# Default: '*'
|
260
202
|
#
|
261
203
|
# value
|
262
204
|
# New value from doozer
|
@@ -269,24 +211,24 @@ module RubyDoozer
|
|
269
211
|
# registry.on_update do |path, value, old_value|
|
270
212
|
# puts "#{path} was updated to #{value} with previous value #{old_value}"
|
271
213
|
# end
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
def on_update(path, &block)
|
277
|
-
(@update_subscribers[path] ||= ThreadSafe::Array.new) << block
|
214
|
+
def on_update(path='*', &block)
|
215
|
+
# Start monitoring thread if not already started
|
216
|
+
monitor_thread
|
217
|
+
((@update_subscribers ||= ThreadSafe::Hash.new)[path] ||= ThreadSafe::Array.new) << block
|
278
218
|
end
|
279
219
|
|
280
220
|
# When an entry is deleted the block will be called
|
281
221
|
# Parameters
|
282
222
|
# path
|
283
|
-
# The relative path
|
223
|
+
# The relative path to watch for changes
|
284
224
|
# block
|
285
225
|
# The block to be called
|
286
226
|
#
|
287
227
|
# Parameters passed to the block:
|
288
228
|
# path
|
289
229
|
# The path that was deleted from doozer
|
230
|
+
# Supplying a path of '*' means all paths
|
231
|
+
# Default: '*'
|
290
232
|
#
|
291
233
|
# old_value
|
292
234
|
# nil if :cache is false
|
@@ -296,16 +238,15 @@ module RubyDoozer
|
|
296
238
|
# registry.on_delete do |path, old_value|
|
297
239
|
# puts "#{path} was deleted with previous value #{old_value}"
|
298
240
|
# end
|
299
|
-
def on_delete(path, &block)
|
300
|
-
|
241
|
+
def on_delete(path='*', &block)
|
242
|
+
# Start monitoring thread if not already started
|
243
|
+
monitor_thread
|
244
|
+
((@delete_subscribers ||= ThreadSafe::Hash.new)[path] ||= ThreadSafe::Array.new) << block
|
301
245
|
end
|
302
246
|
|
303
247
|
############################
|
304
248
|
protected
|
305
249
|
|
306
|
-
#Subscription = Struct.new(:path, :block, :reg_exp)
|
307
|
-
|
308
|
-
|
309
250
|
# Returns the full path given a relative path
|
310
251
|
def full_path(relative_path)
|
311
252
|
"#{@root_path}/#{relative_path}"
|
@@ -318,42 +259,54 @@ module RubyDoozer
|
|
318
259
|
|
319
260
|
# The path has been added or updated in the registry
|
320
261
|
def changed(path, value)
|
321
|
-
logger.debug { "
|
322
|
-
old_value = nil
|
262
|
+
logger.debug { "Updated: #{path} => #{value}" }
|
323
263
|
|
324
|
-
|
325
|
-
if @registry
|
326
|
-
# Already have a value?
|
327
|
-
old_value = @registry[path]
|
264
|
+
return unless @update_subscribers
|
328
265
|
|
329
|
-
|
330
|
-
|
266
|
+
# Subscribers to specific paths
|
267
|
+
if subscribers = @update_subscribers[path]
|
268
|
+
subscribers.each{|subscriber| subscriber.call(path, value)}
|
331
269
|
end
|
332
270
|
|
333
|
-
|
334
|
-
|
271
|
+
# Any subscribers for all events?
|
272
|
+
if all_subscribers = @update_subscribers['*']
|
273
|
+
all_subscribers.each{|subscriber| subscriber.call(path, value)}
|
335
274
|
end
|
336
275
|
end
|
337
276
|
|
338
277
|
# Existing data has been removed from the registry
|
339
278
|
def deleted(path)
|
340
279
|
logger.debug { "Deleted: #{path}" }
|
341
|
-
|
280
|
+
|
281
|
+
return unless @delete_subscribers
|
282
|
+
|
283
|
+
# Subscribers to specific paths
|
342
284
|
if subscribers = @delete_subscribers[path]
|
343
|
-
subscribers.each{|subscriber| subscriber.call(path
|
285
|
+
subscribers.each{|subscriber| subscriber.call(path)}
|
286
|
+
end
|
287
|
+
|
288
|
+
# Any subscribers for all events?
|
289
|
+
if all_subscribers = @delete_subscribers['*']
|
290
|
+
all_subscribers.each{|subscriber| subscriber.call(path)}
|
344
291
|
end
|
345
292
|
end
|
346
293
|
|
347
294
|
# Waits for any updates from Doozer and updates the internal service registry
|
348
|
-
def watch_registry
|
295
|
+
def watch_registry
|
296
|
+
watch_path = "#{@root_path}/**"
|
349
297
|
logger.info "Start monitoring #{watch_path}"
|
350
298
|
# This thread must use its own dedicated doozer connection
|
351
299
|
doozer = RubyDoozer::Client.new(@doozer_config)
|
300
|
+
@current_revision ||= doozer.current_revision
|
352
301
|
|
353
|
-
# Watch for any changes
|
354
|
-
|
302
|
+
# Watch for any new changes
|
303
|
+
logger.debug "Monitoring thread started. Waiting for Registry Changes"
|
304
|
+
doozer.watch(watch_path, @current_revision + 1) do |node|
|
355
305
|
logger.trace "Registry Change Notification", node
|
356
306
|
|
307
|
+
# Update the current_revision with every change notification
|
308
|
+
@current_revision = node.rev
|
309
|
+
|
357
310
|
# Remove the Root path
|
358
311
|
path = relative_path(node.path)
|
359
312
|
|
@@ -365,9 +318,11 @@ module RubyDoozer
|
|
365
318
|
else
|
366
319
|
logger.error "Unknown flags returned by doozer:#{node.flags}"
|
367
320
|
end
|
368
|
-
logger.trace "Updated registry", @registry
|
369
321
|
end
|
370
322
|
logger.info "Stopping monitoring thread normally"
|
323
|
+
|
324
|
+
# #TODO need more exception handling here
|
325
|
+
|
371
326
|
rescue Exception => exc
|
372
327
|
logger.error "Exception in monitoring thread", exc
|
373
328
|
ensure
|
data/lib/ruby_doozer/version.rb
CHANGED
data/lib/ruby_doozer.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'semantic_logger'
|
2
2
|
|
3
3
|
module RubyDoozer
|
4
|
-
autoload :Client,
|
5
|
-
autoload :Registry,
|
4
|
+
autoload :Client, 'ruby_doozer/client'
|
5
|
+
autoload :Registry, 'ruby_doozer/registry'
|
6
|
+
autoload :CachedRegistry, 'ruby_doozer/cached_registry'
|
6
7
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Allow test to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'ruby_doozer'
|
8
|
+
|
9
|
+
# NOTE:
|
10
|
+
# This test assumes that doozerd is running locally on the default port of 8046
|
11
|
+
|
12
|
+
# Register an appender if one is not already registered
|
13
|
+
if SemanticLogger::Logger.appenders.size == 0
|
14
|
+
SemanticLogger::Logger.default_level = :trace
|
15
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('test.log')
|
16
|
+
end
|
17
|
+
|
18
|
+
# Unit Test for RubyDoozer::Client
|
19
|
+
class CachedRegistryTest < Test::Unit::TestCase
|
20
|
+
context RubyDoozer::CachedRegistry do
|
21
|
+
context "with test data" do
|
22
|
+
setup do
|
23
|
+
@test_data = {
|
24
|
+
'bar' => 'test',
|
25
|
+
'one' => 'one',
|
26
|
+
'two' => 'two',
|
27
|
+
}
|
28
|
+
# Doozer does not allow '_' in path names
|
29
|
+
@root_path = "/registrytest"
|
30
|
+
@registry = RubyDoozer::CachedRegistry.new(:root_path => @root_path)
|
31
|
+
@test_data.each_pair {|k,v| @registry[k] = v}
|
32
|
+
# Give doozer time to send back the changes
|
33
|
+
sleep 0.5
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown
|
37
|
+
if @registry
|
38
|
+
@test_data.each_pair {|k,v| @registry.delete(k)}
|
39
|
+
@registry.delete('three')
|
40
|
+
@registry.finalize
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
should "have complete registry" do
|
45
|
+
@test_data.each_pair do |k,v|
|
46
|
+
assert_equal v, @registry[k], "Expected #{k}=>#{v}, #{@registry.to_h.inspect}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
should "iterate over complete registry" do
|
51
|
+
@registry.each_pair do |k,v|
|
52
|
+
assert_equal v, @test_data[k], "Registry #{k}=>#{v}, #{@registry.to_h.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "successfully set and retrieve data" do
|
57
|
+
@registry['three'] = 'value'
|
58
|
+
# Give doozer time to send back the change
|
59
|
+
sleep 0.5
|
60
|
+
result = @registry['three']
|
61
|
+
assert_equal 'value', result
|
62
|
+
end
|
63
|
+
|
64
|
+
[nil, '*'].each do |monitor_path|
|
65
|
+
context "with monitor_path:#{monitor_path}" do
|
66
|
+
should "callback on create" do
|
67
|
+
created_path = nil
|
68
|
+
created_value = nil
|
69
|
+
@registry.on_create(monitor_path||'three') do |path, value|
|
70
|
+
created_path = path
|
71
|
+
created_value = value
|
72
|
+
end
|
73
|
+
@registry['three'] = 'created'
|
74
|
+
# Allow doozer to send back the change
|
75
|
+
sleep 0.5
|
76
|
+
assert_equal 'three', created_path
|
77
|
+
assert_equal 'created', created_value
|
78
|
+
end
|
79
|
+
|
80
|
+
should "callback on update" do
|
81
|
+
updated_path = nil
|
82
|
+
updated_value = nil
|
83
|
+
@registry.on_update(monitor_path||'bar') do |path, value|
|
84
|
+
updated_path = path
|
85
|
+
updated_value = value
|
86
|
+
end
|
87
|
+
@registry['bar'] = 'updated'
|
88
|
+
# Allow doozer to send back the change
|
89
|
+
sleep 0.5
|
90
|
+
assert_equal 'bar', updated_path
|
91
|
+
assert_equal 'updated', updated_value
|
92
|
+
end
|
93
|
+
|
94
|
+
should "callback on delete" do
|
95
|
+
deleted_path = nil
|
96
|
+
@registry.on_delete(monitor_path||'bar') do |path|
|
97
|
+
deleted_path = path
|
98
|
+
end
|
99
|
+
# Allow doozer to send back the change
|
100
|
+
@registry.delete('bar')
|
101
|
+
sleep 0.5
|
102
|
+
assert_equal 'bar', deleted_path
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
['other', 'one'].each do |monitor_path|
|
108
|
+
context "with monitor_path:#{monitor_path}" do
|
109
|
+
should "not callback on create" do
|
110
|
+
created_path = nil
|
111
|
+
created_value = nil
|
112
|
+
@registry.on_create(monitor_path) do |path, value|
|
113
|
+
created_path = path
|
114
|
+
created_value = value
|
115
|
+
end
|
116
|
+
@registry['three'] = 'created'
|
117
|
+
# Allow doozer to send back the change
|
118
|
+
sleep 0.5
|
119
|
+
assert_equal nil, created_path
|
120
|
+
assert_equal nil, created_value
|
121
|
+
end
|
122
|
+
|
123
|
+
should "not callback on update" do
|
124
|
+
updated_path = nil
|
125
|
+
updated_value = nil
|
126
|
+
@registry.on_update(monitor_path) do |path, value|
|
127
|
+
updated_path = path
|
128
|
+
updated_value = value
|
129
|
+
end
|
130
|
+
@registry['bar'] = 'updated'
|
131
|
+
# Give doozer time to send back the change
|
132
|
+
sleep 0.5
|
133
|
+
assert_equal nil, updated_path
|
134
|
+
assert_equal nil, updated_value
|
135
|
+
end
|
136
|
+
|
137
|
+
should "not callback on delete" do
|
138
|
+
deleted_path = nil
|
139
|
+
@registry.on_delete(monitor_path) do |path|
|
140
|
+
deleted_path = path
|
141
|
+
end
|
142
|
+
@registry.delete('bar')
|
143
|
+
# Give doozer time to send back the change
|
144
|
+
sleep 0.5
|
145
|
+
assert_equal nil, deleted_path
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
data/test/registry_test.rb
CHANGED
@@ -44,74 +44,89 @@ class RegistryTest < Test::Unit::TestCase
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
assert_equal v, @registry[k], "Expected #{k}=>#{v}, #{@registry.to_h.inspect}"
|
53
|
-
end
|
54
|
-
end
|
47
|
+
should "have complete registry" do
|
48
|
+
@test_data.each_pair do |k,v|
|
49
|
+
assert_equal v, @registry[k], "Expected #{k}=>#{v}, #{@registry.to_h.inspect}"
|
50
|
+
end
|
51
|
+
end
|
55
52
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
should "iterate over complete registry" do
|
54
|
+
@registry.each_pair do |k,v|
|
55
|
+
assert_equal v, @test_data[k], "Registry #{k}=>#{v}, #{@registry.to_h.inspect}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
should "successfully set and retrieve data" do
|
60
|
+
@registry['three'] = 'value'
|
61
|
+
# Allow doozer to send back the change
|
62
|
+
sleep 0.3
|
63
|
+
result = @registry['three']
|
64
|
+
assert_equal 'value', result
|
65
|
+
end
|
61
66
|
|
62
|
-
|
63
|
-
|
67
|
+
[nil, '*'].each do |monitor_path|
|
68
|
+
context "with monitor_path:#{monitor_path}" do
|
69
|
+
should "callback on update" do
|
70
|
+
updated_path = nil
|
71
|
+
updated_value = nil
|
72
|
+
@registry.on_update(monitor_path||'bar') do |path, value|
|
73
|
+
updated_path = path
|
74
|
+
updated_value = value
|
75
|
+
end
|
76
|
+
# Allow monitoring thread to start
|
77
|
+
sleep 0.1
|
78
|
+
@registry['bar'] = 'updated'
|
64
79
|
# Allow doozer to send back the change
|
65
|
-
sleep 0.
|
66
|
-
|
67
|
-
assert_equal '
|
80
|
+
sleep 0.3
|
81
|
+
assert_equal 'bar', updated_path
|
82
|
+
assert_equal 'updated', updated_value
|
68
83
|
end
|
69
84
|
|
70
|
-
should "
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
created_path = path
|
75
|
-
created_value = value
|
85
|
+
should "callback on delete" do
|
86
|
+
deleted_path = nil
|
87
|
+
@registry.on_delete(monitor_path||'bar') do |path|
|
88
|
+
deleted_path = path
|
76
89
|
end
|
77
|
-
|
90
|
+
# Allow monitoring thread to start
|
91
|
+
sleep 0.1
|
78
92
|
# Allow doozer to send back the change
|
79
|
-
|
80
|
-
|
81
|
-
assert_equal '
|
93
|
+
@registry.delete('bar')
|
94
|
+
sleep 0.3
|
95
|
+
assert_equal 'bar', deleted_path
|
82
96
|
end
|
97
|
+
end
|
98
|
+
end
|
83
99
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
@registry['bar'] = 'updated'
|
94
|
-
# Allow doozer to send back the change
|
95
|
-
sleep 0.5
|
96
|
-
assert_equal 'bar', updated_path
|
97
|
-
assert_equal 'updated', updated_value
|
100
|
+
['other', 'one'].each do |monitor_path|
|
101
|
+
context "with monitor_path:#{monitor_path}" do
|
102
|
+
should "not callback on update" do
|
103
|
+
updated_path = nil
|
104
|
+
updated_value = nil
|
105
|
+
@registry.on_update(monitor_path) do |path, value|
|
106
|
+
updated_path = path
|
107
|
+
updated_value = value
|
98
108
|
end
|
109
|
+
# Allow monitoring thread to start
|
110
|
+
sleep 0.1
|
111
|
+
@registry['bar'] = 'updated'
|
112
|
+
# Allow doozer to send back the change
|
113
|
+
sleep 0.3
|
114
|
+
assert_equal nil, updated_path
|
115
|
+
assert_equal nil, updated_value
|
99
116
|
end
|
100
117
|
|
101
|
-
should "
|
118
|
+
should "not callback on delete" do
|
102
119
|
deleted_path = nil
|
103
|
-
|
104
|
-
@registry.on_delete('bar') do |path, old_value|
|
120
|
+
@registry.on_delete(monitor_path) do |path|
|
105
121
|
deleted_path = path
|
106
|
-
deleted_value = old_value
|
107
122
|
end
|
123
|
+
# Allow monitoring thread to start
|
124
|
+
sleep 0.1
|
108
125
|
# Allow doozer to send back the change
|
109
126
|
@registry.delete('bar')
|
110
|
-
sleep 0.
|
111
|
-
assert_equal
|
112
|
-
assert_equal 'test', deleted_value
|
127
|
+
sleep 0.3
|
128
|
+
assert_equal nil, deleted_path
|
113
129
|
end
|
114
|
-
|
115
130
|
end
|
116
131
|
end
|
117
132
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_doozer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
@@ -79,11 +79,13 @@ files:
|
|
79
79
|
- README.md
|
80
80
|
- Rakefile
|
81
81
|
- lib/ruby_doozer.rb
|
82
|
+
- lib/ruby_doozer/cached_registry.rb
|
82
83
|
- lib/ruby_doozer/client.rb
|
83
84
|
- lib/ruby_doozer/exceptions.rb
|
84
85
|
- lib/ruby_doozer/msg.pb.rb
|
85
86
|
- lib/ruby_doozer/registry.rb
|
86
87
|
- lib/ruby_doozer/version.rb
|
88
|
+
- test/cached_registry_test.rb
|
87
89
|
- test/client_test.rb
|
88
90
|
- test/registry_test.rb
|
89
91
|
homepage: https://github.com/ClarityServices/ruby_doozer
|