ruby_doozer 0.1.0 → 0.2.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/Gemfile +1 -1
- data/Gemfile.lock +2 -1
- data/README.md +47 -7
- data/Rakefile +1 -1
- data/lib/ruby_doozer.rb +3 -0
- data/lib/ruby_doozer/registry.rb +379 -0
- data/lib/ruby_doozer/version.rb +1 -1
- data/test/client_test.rb +6 -0
- data/test/registry_test.rb +120 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca8f8f8ae259ceb2da7979c8ca5c7a2307b6fcc7
|
4
|
+
data.tar.gz: 4faf064cc2e33a25a3e612f66d59c528c948e491
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f085c6219b6a3901d37937079036df515e5ad9fe4e54253147109e10cb3d8b51fa74cab823336883f7ad0d049d53149474f8d1fa31384a167137be7574b8a2f
|
7
|
+
data.tar.gz: 1a3c63775c7587c24f984b5d130c9aa17c464d338c305434df7f4127a472a154891f229f4639f96728b538db3bad9f89b55d9d44eba4d5cb7984d2f275d602fa
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -7,6 +7,7 @@ GEM
|
|
7
7
|
atomic (1.0.1)
|
8
8
|
bourne (1.4.0)
|
9
9
|
mocha (~> 0.13.2)
|
10
|
+
gene_pool (1.3.0)
|
10
11
|
i18n (0.6.1)
|
11
12
|
metaclass (0.0.1)
|
12
13
|
mocha (0.13.3)
|
@@ -34,7 +35,7 @@ PLATFORMS
|
|
34
35
|
ruby
|
35
36
|
|
36
37
|
DEPENDENCIES
|
37
|
-
|
38
|
+
gene_pool
|
38
39
|
rake
|
39
40
|
resilient_socket
|
40
41
|
ruby_protobuf
|
data/README.md
CHANGED
@@ -19,22 +19,62 @@ client.close
|
|
19
19
|
|
20
20
|
### Logging
|
21
21
|
|
22
|
-
Since
|
22
|
+
Since ruby_doozer uses SemanticLogger, trace level logging of all TCP/IP
|
23
23
|
calls can be enabled as follows:
|
24
24
|
|
25
25
|
```ruby
|
26
26
|
require 'rubygems'
|
27
|
-
require '
|
27
|
+
require 'ruby_doozer'
|
28
28
|
|
29
29
|
SemanticLogger::Logger.default_level = :trace
|
30
|
-
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('
|
30
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('doozer.log')
|
31
|
+
|
32
|
+
client = RubyDoozer::Client.new(:server => '127.0.0.1:8046')
|
33
|
+
client.set('/test/foo', 'value')
|
34
|
+
result = client.get('/test/foo')
|
35
|
+
client.close
|
36
|
+
```
|
37
|
+
|
38
|
+
### Registry
|
39
|
+
|
40
|
+
RubyDoozer also includes a Registry class to support storing all configuration
|
41
|
+
information in doozer. This Centralized Configuration allows configuration changes
|
42
|
+
to be made dynamically at run-time and all interested parties will be notified
|
43
|
+
of the changes.
|
44
|
+
|
45
|
+
For example, making a change to the central database configuration will notify
|
46
|
+
all application servers to drop their database connections and re-establish them
|
47
|
+
to the new servers:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'rubygems'
|
51
|
+
require 'ruby_doozer'
|
52
|
+
|
53
|
+
SemanticLogger::Logger.appenders << SemanticLogger::Appender::File.new('registry.log')
|
54
|
+
|
55
|
+
region = "Development"
|
56
|
+
application = "sprites"
|
57
|
+
path = "/#{region}/#{application}/config/resources".downcase
|
58
|
+
|
59
|
+
config_registry = RubyDoozer::Registry.new(:root_path => path)
|
60
|
+
|
61
|
+
# Store the configuration information in doozer as a serialized string
|
62
|
+
config_registry['master'] = "Some JSON config string"
|
63
|
+
|
64
|
+
# Allow time for Doozer to publish the new config
|
65
|
+
sleep 0.5
|
66
|
+
|
67
|
+
# Retrieve the current configuration
|
68
|
+
database_config = config_registry['master']
|
69
|
+
slave_database_config = config_registry['secondary']
|
31
70
|
|
32
|
-
|
33
|
-
|
71
|
+
# Register for any changes to the configuration
|
72
|
+
config_registry.on_update('master') do |path, value|
|
73
|
+
puts "Time to re-establish database connections to new server: #{value}"
|
34
74
|
end
|
35
75
|
|
36
|
-
|
37
|
-
|
76
|
+
# Change the configuration and all subscribers will be notified
|
77
|
+
config_registry['master'] = "Some updated JSON config string"
|
38
78
|
```
|
39
79
|
|
40
80
|
### Notes
|
data/Rakefile
CHANGED
@@ -6,7 +6,6 @@ require 'rubygems/package'
|
|
6
6
|
require 'rake/clean'
|
7
7
|
require 'rake/testtask'
|
8
8
|
require 'date'
|
9
|
-
require 'semantic_logger'
|
10
9
|
require 'ruby_doozer/version'
|
11
10
|
|
12
11
|
desc "Build gem"
|
@@ -27,6 +26,7 @@ task :gem do |t|
|
|
27
26
|
spec.add_dependency 'semantic_logger'
|
28
27
|
spec.add_dependency 'resilient_socket'
|
29
28
|
spec.add_dependency 'ruby_protobuf'
|
29
|
+
spec.add_dependency 'gene_pool'
|
30
30
|
end
|
31
31
|
Gem::Package.build gemspec
|
32
32
|
end
|
data/lib/ruby_doozer.rb
CHANGED
@@ -0,0 +1,379 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
require 'gene_pool'
|
3
|
+
require 'semantic_logger'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Registry
|
7
|
+
#
|
8
|
+
# Store information in doozer and subscribe to future changes
|
9
|
+
#
|
10
|
+
# Notifies registered subscribers when information has changed
|
11
|
+
#
|
12
|
+
# All paths specified are relative to the root_path. As such the root path
|
13
|
+
# is never returned, nor is it required when a path is supplied as input.
|
14
|
+
# For example, with a root_path of /foo/bar, any paths passed in will leave
|
15
|
+
# out the root_path: host/name
|
16
|
+
#
|
17
|
+
module RubyDoozer
|
18
|
+
class Registry
|
19
|
+
# Logging instance for this class
|
20
|
+
include SemanticLogger::Loggable
|
21
|
+
|
22
|
+
attr_reader :doozer_config, :doozer_pool
|
23
|
+
|
24
|
+
# Create a Registry instance to manage a path of information within doozer
|
25
|
+
#
|
26
|
+
# :root_path [String]
|
27
|
+
# Root path to load and then monitor for changes
|
28
|
+
# It is not recommended to set the root_path to "/" as it will generate
|
29
|
+
# significant traffic since it will also monitor Doozer Admin changes
|
30
|
+
# Mandatory
|
31
|
+
#
|
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
|
+
# :doozer [Hash]
|
39
|
+
# Doozer configuration information
|
40
|
+
#
|
41
|
+
# :servers [Array of String]
|
42
|
+
# Array of URL's of doozer servers to connect to with port numbers
|
43
|
+
# ['server1:2000', 'server2:2000']
|
44
|
+
#
|
45
|
+
# An attempt will be made to connect to alternative servers when the
|
46
|
+
# current server cannot be connected to
|
47
|
+
# Default: ['127.0.0.1:8046']
|
48
|
+
#
|
49
|
+
# :read_timeout [Float]
|
50
|
+
# Time in seconds to timeout on read
|
51
|
+
# Can be overridden by supplying a timeout in the read call
|
52
|
+
# Default: 5
|
53
|
+
#
|
54
|
+
# :connect_timeout [Float]
|
55
|
+
# Time in seconds to timeout when trying to connect to the server
|
56
|
+
# Default: 3
|
57
|
+
#
|
58
|
+
# :connect_retry_count [Fixnum]
|
59
|
+
# Number of times to retry connecting when a connection fails
|
60
|
+
# Default: 10
|
61
|
+
#
|
62
|
+
# :connect_retry_interval [Float]
|
63
|
+
# Number of seconds between connection retry attempts after the first failed attempt
|
64
|
+
# Default: 0.5
|
65
|
+
#
|
66
|
+
# :server_selector [Symbol|Proc]
|
67
|
+
# When multiple servers are supplied using :servers, this option will
|
68
|
+
# determine which server is selected from the list
|
69
|
+
# :ordered
|
70
|
+
# Select a server in the order supplied in the array, with the first
|
71
|
+
# having the highest priority. The second server will only be connected
|
72
|
+
# to if the first server is unreachable
|
73
|
+
# :random
|
74
|
+
# Randomly select a server from the list every time a connection
|
75
|
+
# is established, including during automatic connection recovery.
|
76
|
+
# Proc:
|
77
|
+
# When a Proc is supplied, it will be called passing in the list
|
78
|
+
# of servers. The Proc must return one server name
|
79
|
+
# Example:
|
80
|
+
# :server_selector => Proc.new do |servers|
|
81
|
+
# servers.last
|
82
|
+
# end
|
83
|
+
# Default: :random
|
84
|
+
#
|
85
|
+
# :pool_size [Integer]
|
86
|
+
# Maximum size of the connection pool to doozer
|
87
|
+
# Default: 10
|
88
|
+
#
|
89
|
+
def initialize(params)
|
90
|
+
params = params.dup
|
91
|
+
@root_path = params.delete(:root_path)
|
92
|
+
raise "Missing mandatory parameter :root_path" unless @root_path
|
93
|
+
|
94
|
+
# Add leading '/' to root_path if missing
|
95
|
+
@root_path = "/#{@root_path}" unless @root_path.start_with?('/')
|
96
|
+
|
97
|
+
# Strip trailing '/' if supplied
|
98
|
+
@root_path = @root_path[0..-2] if @root_path.end_with?("/")
|
99
|
+
@root_path_with_trail = "#{@root_path}/"
|
100
|
+
|
101
|
+
local_copy = params.delete(:cache)
|
102
|
+
@registry = (local_copy == false) ? nil : ThreadSafe::Hash.new
|
103
|
+
|
104
|
+
@doozer_config = params.delete(:doozer) || {}
|
105
|
+
@doozer_config[:servers] ||= ['127.0.0.1:8046']
|
106
|
+
@doozer_config[:read_timeout] ||= 5
|
107
|
+
@doozer_config[:connect_timeout] ||= 3
|
108
|
+
@doozer_config[:connect_retry_interval] ||= 0.5
|
109
|
+
@doozer_config[:connect_retry_count] ||= 10
|
110
|
+
@doozer_config[:server_selector] ||= :random
|
111
|
+
|
112
|
+
# Connection pool settings
|
113
|
+
@doozer_pool = GenePool.new(
|
114
|
+
:name =>"Doozer Connection Pool",
|
115
|
+
:pool_size => @doozer_config.delete(:pool_size) || 10,
|
116
|
+
:timeout => @doozer_config.delete(:pool_timeout) || 30,
|
117
|
+
:warn_timeout => @doozer_config.delete(:pool_warn_timeout) || 5,
|
118
|
+
:idle_timeout => @doozer_config.delete(:pool_idle_timeout) || 600,
|
119
|
+
:logger => logger,
|
120
|
+
:close_proc => :close
|
121
|
+
) do
|
122
|
+
RubyDoozer::Client.new(@doozer_config)
|
123
|
+
end
|
124
|
+
|
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
|
+
# Generate warning log entries for any unknown configuration options
|
145
|
+
params.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: #{k}"}
|
146
|
+
end
|
147
|
+
|
148
|
+
# 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
|
+
def [](path)
|
153
|
+
if @registry
|
154
|
+
@registry[path]
|
155
|
+
else
|
156
|
+
doozer_pool.with_connection do |doozer|
|
157
|
+
doozer[full_path(path)]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# 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
|
+
def []=(path,value)
|
167
|
+
doozer_pool.with_connection do |doozer|
|
168
|
+
doozer[full_path(path)] = value
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# 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
|
+
def delete(path)
|
177
|
+
old_value = @registry[path] if @registry
|
178
|
+
doozer_pool.with_connection do |doozer|
|
179
|
+
doozer.delete(full_path(path))
|
180
|
+
end
|
181
|
+
old_value
|
182
|
+
end
|
183
|
+
|
184
|
+
# Iterate over every key, value pair in the registry at the root_path
|
185
|
+
#
|
186
|
+
# If :cache was set to false on the initializer this call will
|
187
|
+
# make network calls to doozer to retrieve the current values
|
188
|
+
# Otherwise it is an in memory call against a duplicate of the registry
|
189
|
+
#
|
190
|
+
# Example:
|
191
|
+
# registry.each_pair {|k,v| puts "#{k} => #{v}"}
|
192
|
+
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
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns a copy of the registry as a Hash
|
206
|
+
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
|
214
|
+
end
|
215
|
+
|
216
|
+
# Cleanup on process termination
|
217
|
+
def finalize
|
218
|
+
logger.info "Finalizing"
|
219
|
+
if @monitor_thread
|
220
|
+
@monitor_thread.kill
|
221
|
+
@monitor_thread = nil
|
222
|
+
end
|
223
|
+
@doozer_pool.close if @doozer_pool
|
224
|
+
@doozer_pool = nil
|
225
|
+
end
|
226
|
+
|
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
|
+
# When an entry is updated the block will be called
|
251
|
+
# Parameters
|
252
|
+
# path
|
253
|
+
# The relative path _excluding_ the root_path to watch for changes
|
254
|
+
# block
|
255
|
+
# The block to be called
|
256
|
+
#
|
257
|
+
# Parameters passed to the block:
|
258
|
+
# path
|
259
|
+
# The path that was deleted from doozer
|
260
|
+
#
|
261
|
+
# value
|
262
|
+
# New value from doozer
|
263
|
+
#
|
264
|
+
# old_value
|
265
|
+
# nil if :cache is false
|
266
|
+
# otherwise the last value for this path in the local copy of the registry
|
267
|
+
#
|
268
|
+
# Example:
|
269
|
+
# registry.on_update do |path, value, old_value|
|
270
|
+
# puts "#{path} was updated to #{value} with previous value #{old_value}"
|
271
|
+
# 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
|
278
|
+
end
|
279
|
+
|
280
|
+
# When an entry is deleted the block will be called
|
281
|
+
# Parameters
|
282
|
+
# path
|
283
|
+
# The relative path _excluding_ the root_path to watch for changes
|
284
|
+
# block
|
285
|
+
# The block to be called
|
286
|
+
#
|
287
|
+
# Parameters passed to the block:
|
288
|
+
# path
|
289
|
+
# The path that was deleted from doozer
|
290
|
+
#
|
291
|
+
# old_value
|
292
|
+
# nil if :cache is false
|
293
|
+
# otherwise the last value for this path in the local copy of the registry
|
294
|
+
#
|
295
|
+
# Example:
|
296
|
+
# registry.on_delete do |path, old_value|
|
297
|
+
# puts "#{path} was deleted with previous value #{old_value}"
|
298
|
+
# end
|
299
|
+
def on_delete(path, &block)
|
300
|
+
(@delete_subscribers[path] ||= ThreadSafe::Array.new) << block
|
301
|
+
end
|
302
|
+
|
303
|
+
############################
|
304
|
+
protected
|
305
|
+
|
306
|
+
#Subscription = Struct.new(:path, :block, :reg_exp)
|
307
|
+
|
308
|
+
|
309
|
+
# Returns the full path given a relative path
|
310
|
+
def full_path(relative_path)
|
311
|
+
"#{@root_path}/#{relative_path}"
|
312
|
+
end
|
313
|
+
|
314
|
+
# Returns the full path given a relative path
|
315
|
+
def relative_path(full_path)
|
316
|
+
full_path.sub(@root_path_with_trail, '')
|
317
|
+
end
|
318
|
+
|
319
|
+
# The path has been added or updated in the registry
|
320
|
+
def changed(path, value)
|
321
|
+
logger.debug { "Changed: #{path} => #{value}" }
|
322
|
+
old_value = nil
|
323
|
+
|
324
|
+
# Keeping a local copy of the registry?
|
325
|
+
if @registry
|
326
|
+
# Already have a value?
|
327
|
+
old_value = @registry[path]
|
328
|
+
|
329
|
+
# Update in memory copy
|
330
|
+
@registry[path] = value
|
331
|
+
end
|
332
|
+
|
333
|
+
if subscribers = old_value ? @update_subscribers[path] : @create_subscribers[path]
|
334
|
+
subscribers.each{|subscriber| subscriber.call(path, value, old_value)}
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# Existing data has been removed from the registry
|
339
|
+
def deleted(path)
|
340
|
+
logger.debug { "Deleted: #{path}" }
|
341
|
+
old_value = @registry.delete(path) if @registry
|
342
|
+
if subscribers = @delete_subscribers[path]
|
343
|
+
subscribers.each{|subscriber| subscriber.call(path, old_value)}
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Waits for any updates from Doozer and updates the internal service registry
|
348
|
+
def watch_registry(watch_path, revision)
|
349
|
+
logger.info "Start monitoring #{watch_path}"
|
350
|
+
# This thread must use its own dedicated doozer connection
|
351
|
+
doozer = RubyDoozer::Client.new(@doozer_config)
|
352
|
+
|
353
|
+
# Watch for any changes
|
354
|
+
doozer.watch(watch_path, revision) do |node|
|
355
|
+
logger.trace "Registry Change Notification", node
|
356
|
+
|
357
|
+
# Remove the Root path
|
358
|
+
path = relative_path(node.path)
|
359
|
+
|
360
|
+
case node.flags
|
361
|
+
when 4
|
362
|
+
changed(path, node.value)
|
363
|
+
when 8
|
364
|
+
deleted(path)
|
365
|
+
else
|
366
|
+
logger.error "Unknown flags returned by doozer:#{node.flags}"
|
367
|
+
end
|
368
|
+
logger.trace "Updated registry", @registry
|
369
|
+
end
|
370
|
+
logger.info "Stopping monitoring thread normally"
|
371
|
+
rescue Exception => exc
|
372
|
+
logger.error "Exception in monitoring thread", exc
|
373
|
+
ensure
|
374
|
+
doozer.close if doozer
|
375
|
+
logger.info "Stopped monitoring for changes in the doozer registry"
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
end
|
data/lib/ruby_doozer/version.rb
CHANGED
data/test/client_test.rb
CHANGED
@@ -9,6 +9,12 @@ require 'ruby_doozer/client'
|
|
9
9
|
# NOTE:
|
10
10
|
# This test assumes that doozerd is running locally on the default port of 8046
|
11
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
|
+
|
12
18
|
# Unit Test for RubyDoozer::Client
|
13
19
|
class ClientTest < Test::Unit::TestCase
|
14
20
|
context RubyDoozer::Client do
|
@@ -0,0 +1,120 @@
|
|
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 RegistryTest < Test::Unit::TestCase
|
20
|
+
context RubyDoozer::Registry 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
|
+
@client = RubyDoozer::Client.new(:server => 'localhost:8046')
|
31
|
+
@test_data.each_pair {|k,v| @client.set("#{@root_path}/#{k}",v)}
|
32
|
+
|
33
|
+
@registry = RubyDoozer::Registry.new(:root_path => @root_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown
|
37
|
+
@registry.finalize if @registry
|
38
|
+
if @client
|
39
|
+
@test_data.each_pair do |k,v|
|
40
|
+
@client.delete("#{@root_path}/#{k}")
|
41
|
+
end
|
42
|
+
@client.delete("#{@root_path}/three")
|
43
|
+
@client.close
|
44
|
+
end
|
45
|
+
end
|
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
|
55
|
+
|
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
|
61
|
+
|
62
|
+
should "successfully set and retrieve data" do
|
63
|
+
@registry['three'] = 'value'
|
64
|
+
# Allow doozer to send back the change
|
65
|
+
sleep 0.5
|
66
|
+
result = @registry['three']
|
67
|
+
assert_equal 'value', result
|
68
|
+
end
|
69
|
+
|
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
|
76
|
+
end
|
77
|
+
@registry['three'] = 'created'
|
78
|
+
# Allow doozer to send back the change
|
79
|
+
sleep 0.5
|
80
|
+
assert_equal 'three', created_path
|
81
|
+
assert_equal 'created', created_value
|
82
|
+
end
|
83
|
+
|
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
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
should "invoke callbacks on delete" do
|
102
|
+
deleted_path = nil
|
103
|
+
deleted_value = nil
|
104
|
+
@registry.on_delete('bar') do |path, old_value|
|
105
|
+
deleted_path = path
|
106
|
+
deleted_value = old_value
|
107
|
+
end
|
108
|
+
# Allow doozer to send back the change
|
109
|
+
@registry.delete('bar')
|
110
|
+
sleep 0.5
|
111
|
+
assert_equal 'bar', deleted_path
|
112
|
+
assert_equal 'test', deleted_value
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_doozer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-03-
|
11
|
+
date: 2013-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: semantic_logger
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: gene_pool
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Ruby Client for doozer
|
56
70
|
email:
|
57
71
|
- reidmo@gmail.com
|
@@ -68,8 +82,10 @@ files:
|
|
68
82
|
- lib/ruby_doozer/client.rb
|
69
83
|
- lib/ruby_doozer/exceptions.rb
|
70
84
|
- lib/ruby_doozer/msg.pb.rb
|
85
|
+
- lib/ruby_doozer/registry.rb
|
71
86
|
- lib/ruby_doozer/version.rb
|
72
87
|
- test/client_test.rb
|
88
|
+
- test/registry_test.rb
|
73
89
|
homepage: https://github.com/ClarityServices/ruby_doozer
|
74
90
|
licenses:
|
75
91
|
- Apache License V2.0
|