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 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