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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca8f8f8ae259ceb2da7979c8ca5c7a2307b6fcc7
4
- data.tar.gz: 4faf064cc2e33a25a3e612f66d59c528c948e491
3
+ metadata.gz: ef156e41c4d70417b9ccb4128c22b817c926c474
4
+ data.tar.gz: c8c054bea13c4933368e12838526de1febe4e37a
5
5
  SHA512:
6
- metadata.gz: 2f085c6219b6a3901d37937079036df515e5ad9fe4e54253147109e10cb3d8b51fa74cab823336883f7ad0d049d53149474f8d1fa31384a167137be7574b8a2f
7
- data.tar.gz: 1a3c63775c7587c24f984b5d130c9aa17c464d338c305434df7f4127a472a154891f229f4639f96728b538db3bad9f89b55d9d44eba4d5cb7984d2f275d602fa
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
@@ -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
- if @registry
154
- @registry[path]
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
- if @registry
194
- @registry.dup.each_pair(&block)
195
- else
196
- path = "#{@root_path}/**"
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
- if @registry
208
- @registry.dup
209
- else
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 _excluding_ the root_path to watch for changes
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 deleted from doozer
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
- # Warning:
274
- # If :cache is set to false then on_update will never be called
275
- # Every change will go to subscribers of #on_create()
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 _excluding_ the root_path to watch for changes
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
- (@delete_subscribers[path] ||= ThreadSafe::Array.new) << block
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 { "Changed: #{path} => #{value}" }
322
- old_value = nil
262
+ logger.debug { "Updated: #{path} => #{value}" }
323
263
 
324
- # Keeping a local copy of the registry?
325
- if @registry
326
- # Already have a value?
327
- old_value = @registry[path]
264
+ return unless @update_subscribers
328
265
 
329
- # Update in memory copy
330
- @registry[path] = value
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
- if subscribers = old_value ? @update_subscribers[path] : @create_subscribers[path]
334
- subscribers.each{|subscriber| subscriber.call(path, value, old_value)}
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
- old_value = @registry.delete(path) if @registry
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, old_value)}
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(watch_path, revision)
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
- doozer.watch(watch_path, revision) do |node|
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
@@ -1,3 +1,3 @@
1
1
  module RubyDoozer #:nodoc
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/ruby_doozer.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'semantic_logger'
2
2
 
3
3
  module RubyDoozer
4
- autoload :Client, 'ruby_doozer/client'
5
- autoload :Registry, 'ruby_doozer/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
@@ -44,74 +44,89 @@ class RegistryTest < Test::Unit::TestCase
44
44
  end
45
45
  end
46
46
 
47
- # Run tests with and without a local cache
48
- [true, false].each do |cache|
49
- context "cache:#{cache}" do
50
- should "have complete registry" do
51
- @test_data.each_pair do |k,v|
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
- should "iterate over complete registry" do
57
- @registry.each_pair do |k,v|
58
- assert_equal v, @test_data[k], "Registry #{k}=>#{v}, #{@registry.to_h.inspect}"
59
- end
60
- end
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
- should "successfully set and retrieve data" do
63
- @registry['three'] = 'value'
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.5
66
- result = @registry['three']
67
- assert_equal 'value', result
80
+ sleep 0.3
81
+ assert_equal 'bar', updated_path
82
+ assert_equal 'updated', updated_value
68
83
  end
69
84
 
70
- should "invoke callbacks on create" do
71
- created_path = nil
72
- created_value = nil
73
- @registry.on_create('three') do |path, value|
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
- @registry['three'] = 'created'
90
+ # Allow monitoring thread to start
91
+ sleep 0.1
78
92
  # Allow doozer to send back the change
79
- sleep 0.5
80
- assert_equal 'three', created_path
81
- assert_equal 'created', created_value
93
+ @registry.delete('bar')
94
+ sleep 0.3
95
+ assert_equal 'bar', deleted_path
82
96
  end
97
+ end
98
+ end
83
99
 
84
- should "invoke callbacks on update" do
85
- # Update only triggers when the cache is enabled
86
- if cache
87
- updated_path = nil
88
- updated_value = nil
89
- @registry.on_update('bar') do |path, value|
90
- updated_path = path
91
- updated_value = value
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 "invoke callbacks on delete" do
118
+ should "not callback on delete" do
102
119
  deleted_path = nil
103
- deleted_value = nil
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.5
111
- assert_equal 'bar', deleted_path
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.2.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