ruby_doozer 0.2.0 → 0.3.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.
- 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
|