boogaloo 0.1
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.
- data/CHANGELOG +2 -0
- data/DEBUGGING +4 -0
- data/MIT-LICENSE +7 -0
- data/README +106 -0
- data/RUNNING_UNIT_TESTS +5 -0
- data/Rakefile +44 -0
- data/bin/boogaloo +159 -0
- data/doc/classes/Boogaloo.html +149 -0
- data/doc/classes/Boogaloo/Cache.html +121 -0
- data/doc/classes/Boogaloo/Cache/Base.html +664 -0
- data/doc/classes/Boogaloo/Cache/Persistent.html +122 -0
- data/doc/classes/Boogaloo/Cache/Temporary.html +439 -0
- data/doc/classes/Boogaloo/Client.html +111 -0
- data/doc/classes/Boogaloo/Client/Connection.html +228 -0
- data/doc/classes/Boogaloo/Config.html +540 -0
- data/doc/classes/Boogaloo/ServiceGateway.html +176 -0
- data/doc/classes/Boogaloo/ServiceRequest.html +210 -0
- data/doc/classes/Boogaloo/ThreadPool.html +205 -0
- data/doc/classes/Boogaloo/WorkerThread.html +239 -0
- data/doc/created.rid +1 -0
- data/doc/files/DEBUGGING.html +113 -0
- data/doc/files/MIT-LICENSE.html +129 -0
- data/doc/files/README.html +261 -0
- data/doc/files/examples/boogaloo_conf.html +130 -0
- data/doc/files/lib/boogaloo/cache/base_rb.html +101 -0
- data/doc/files/lib/boogaloo/cache/persistent_rb.html +108 -0
- data/doc/files/lib/boogaloo/cache/temporary_rb.html +101 -0
- data/doc/files/lib/boogaloo/client/connection_rb.html +101 -0
- data/doc/files/lib/boogaloo/config_rb.html +109 -0
- data/doc/files/lib/boogaloo/service_gateway_rb.html +110 -0
- data/doc/files/lib/boogaloo/service_request_rb.html +108 -0
- data/doc/files/lib/boogaloo/thread_pool_rb.html +101 -0
- data/doc/files/lib/boogaloo/util_rb.html +240 -0
- data/doc/files/lib/boogaloo/version_rb.html +114 -0
- data/doc/files/lib/boogaloo/worker_thread_rb.html +101 -0
- data/doc/fr_class_index.html +38 -0
- data/doc/fr_file_index.html +41 -0
- data/doc/fr_method_index.html +67 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/examples/boogaloo.conf +17 -0
- data/lib/boogaloo/cache/base.rb +222 -0
- data/lib/boogaloo/cache/persistent.rb +15 -0
- data/lib/boogaloo/cache/temporary.rb +173 -0
- data/lib/boogaloo/client/connection.rb +55 -0
- data/lib/boogaloo/config.rb +206 -0
- data/lib/boogaloo/service_gateway.rb +37 -0
- data/lib/boogaloo/service_request.rb +39 -0
- data/lib/boogaloo/thread_pool.rb +40 -0
- data/lib/boogaloo/util.rb +29 -0
- data/lib/boogaloo/version.rb +1 -0
- data/lib/boogaloo/worker_thread.rb +46 -0
- data/test/test_persistent_cache.rb +99 -0
- data/test/test_service_gateway.rb +27 -0
- data/test/test_synchronicity.rb +29 -0
- data/test/test_temporary_cache.rb +42 -0
- metadata +113 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'boogaloo/util'
|
3
|
+
|
4
|
+
module Boogaloo
|
5
|
+
|
6
|
+
# See examples/boogalooo.conf[link:files/examples/boogaloo_conf.html] for an example configuration file.
|
7
|
+
#
|
8
|
+
# = Available Configuration Options
|
9
|
+
#
|
10
|
+
# == Section: Server
|
11
|
+
#
|
12
|
+
# +host+:: (required) The host name or IP address to bind the socket to.
|
13
|
+
# +port+:: (required) The port to bind the socket to.
|
14
|
+
# +worker_threads+:: (required) Boogaloo uses a pool of threads created at start-up that wait for requests to handle. 3 is a reasonable default.
|
15
|
+
# +debug+:: (optional) Enable/disable debugging. See DEBUGGING[link:files/DEBUGGING.html] for for information.
|
16
|
+
#
|
17
|
+
# == Section: PersistentCaches
|
18
|
+
#
|
19
|
+
# +instance_name+:: (required) The name to use for accessing the cache on the client.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# PersistentCaches:
|
24
|
+
#
|
25
|
+
# MyPersistentCache:
|
26
|
+
# instance_name: main_cache
|
27
|
+
#
|
28
|
+
# == Section: TemporaryCaches
|
29
|
+
#
|
30
|
+
# +instance_name+:: (required) The name to use for accessing the cache on the client.
|
31
|
+
# +check_frequency+:: (required) The frequency in seconds at which the cache will check for expired objects to delete.
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
#
|
35
|
+
# TemporaryCaches:
|
36
|
+
#
|
37
|
+
# MyTempCache:
|
38
|
+
# instance_name: temp_cache
|
39
|
+
# check_frequency: 10
|
40
|
+
#
|
41
|
+
# == Section: Services
|
42
|
+
#
|
43
|
+
# +instance_name+:: (required) The name to use for accessing the service on the client.
|
44
|
+
# +path+:: (required) The path to the directory containing your interface.rb file.
|
45
|
+
# +parameters+:: (optional) If your interface constructor accepts parameters you can specify them hear.
|
46
|
+
# +require_path+:: (optional) If your service needs to require files not in the standard $LOAD_PATH, you can specify directories to add to the $LOAD_PATH here either as a list or in singular form.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# Services:
|
51
|
+
#
|
52
|
+
# MyService:
|
53
|
+
# instance_name: my_service
|
54
|
+
# path: /home/me/BoogalooServices/MyService
|
55
|
+
# parameters:
|
56
|
+
# - Hello
|
57
|
+
# - World
|
58
|
+
# require_path:
|
59
|
+
# - /some/dir
|
60
|
+
# - /some/other/dir
|
61
|
+
#
|
62
|
+
class Config
|
63
|
+
|
64
|
+
# Initialize a new cofig instance.
|
65
|
+
#
|
66
|
+
# Parameters:
|
67
|
+
#
|
68
|
+
# +file+:: The configuration file to read.
|
69
|
+
def initialize(file)
|
70
|
+
|
71
|
+
@config = YAML.load File.read(file)
|
72
|
+
error("Server configuration not found.") if not @config.key?('Server')
|
73
|
+
error("host attribute missing for Server configuration.") if not @config['Server'].key?('host')
|
74
|
+
error("port attribute missing for Server configuration.") if not @config['Server'].key?('port')
|
75
|
+
error("worker_threads attribute missing for Server configuration.") if not @config['Server'].key?('worker_threads')
|
76
|
+
|
77
|
+
@persistent_caches = {}
|
78
|
+
@temporary_caches = {}
|
79
|
+
@services = {}
|
80
|
+
|
81
|
+
if @config['PersistentCaches']
|
82
|
+
|
83
|
+
@config['PersistentCaches'].each do |name, config|
|
84
|
+
|
85
|
+
error "name attribute missing for persistent cache." if not name or not name.is_a?(String)
|
86
|
+
error "configration missing for persistent cache '#{name}'." if not config.is_a?(Hash)
|
87
|
+
error "instance_name attribute missing for persistent cache '#{name}'." if not config.key?('instance_name')
|
88
|
+
|
89
|
+
conflict = nil
|
90
|
+
@persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
91
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
|
92
|
+
|
93
|
+
@persistent_caches[name] = config
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
if @config['TemporaryCaches']
|
100
|
+
|
101
|
+
@config['TemporaryCaches'].each do |name, config|
|
102
|
+
|
103
|
+
error "name attribute missing for temporary cache." if not name or not name.is_a?(String)
|
104
|
+
error "configration missing for temporary cache '#{name}'." if not config.is_a?(Hash)
|
105
|
+
error "instance_name attribute missing for temporary cache '#{name}'." if not config.key?('instance_name')
|
106
|
+
error "check_frequency attribute missing for temporary cache '#{name}'." if not config.key?('check_frequency')
|
107
|
+
|
108
|
+
conflict = nil
|
109
|
+
@temporary_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
110
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with temporary cache '#{conflict}'." if conflict
|
111
|
+
|
112
|
+
@persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
113
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
|
114
|
+
|
115
|
+
@temporary_caches[name] = config
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
if @config['Services']
|
122
|
+
|
123
|
+
@config['Services'].each do |name, config|
|
124
|
+
|
125
|
+
error "name attribute missing for service." if not name or not name.is_a?(String)
|
126
|
+
error "configration missing for service cache '#{name}'." if not config.is_a?(Hash)
|
127
|
+
error "path attribute missing for service '#{name}'." if not config.key?('path')
|
128
|
+
error "instance_name attribute missing for service '#{name}'." if not config.key?('instance_name')
|
129
|
+
|
130
|
+
conflict = nil
|
131
|
+
@services.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
132
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with service '#{conflict}'." if conflict
|
133
|
+
|
134
|
+
@temporary_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
135
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with temporary cache '#{conflict}'." if conflict
|
136
|
+
|
137
|
+
@persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
|
138
|
+
error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
|
139
|
+
|
140
|
+
@services[name] = config
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
if @config['Server']['debug'] == true
|
147
|
+
|
148
|
+
$debug = true
|
149
|
+
notice("Debugging enabled")
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# The entire _Server_ configuration section.
|
156
|
+
def server
|
157
|
+
|
158
|
+
@config['Server']
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
# The server host.
|
163
|
+
def server_host
|
164
|
+
|
165
|
+
server['host']
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
# The server port.
|
170
|
+
def server_port
|
171
|
+
|
172
|
+
server['port']
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
# The number of worker threads.
|
177
|
+
def server_worker_threads
|
178
|
+
|
179
|
+
server['worker_threads']
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
# The entire _PersistentCaches_ configuration section.
|
184
|
+
def persistent_caches
|
185
|
+
|
186
|
+
@persistent_caches
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
# The entire _TemporaryCaches_ configuration section.
|
191
|
+
def temporary_caches
|
192
|
+
|
193
|
+
@temporary_caches
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
# The entire _Services_ configuration section.
|
198
|
+
def services
|
199
|
+
|
200
|
+
@services
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'boogaloo/thread_pool'
|
2
|
+
require 'boogaloo/worker_thread'
|
3
|
+
require 'boogaloo/service_request'
|
4
|
+
|
5
|
+
module Boogaloo
|
6
|
+
|
7
|
+
class ServiceGateway
|
8
|
+
|
9
|
+
# Initialize a new service gateway.
|
10
|
+
#
|
11
|
+
# Parameters:
|
12
|
+
#
|
13
|
+
# +pool+:: A reference to a ThreadPool
|
14
|
+
# +services+:: A Hash of instance_name/instance pairs.
|
15
|
+
def initialize(pool, services)
|
16
|
+
|
17
|
+
services.each do |name, instance|
|
18
|
+
|
19
|
+
instance_variable_set("@"+name, instance)
|
20
|
+
|
21
|
+
self.class.instance_eval do
|
22
|
+
|
23
|
+
define_method(name) do
|
24
|
+
|
25
|
+
Boogaloo::ServiceRequest.new(instance_variable_get("@"+name), pool)
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'boogaloo/util'
|
2
|
+
|
3
|
+
module Boogaloo
|
4
|
+
|
5
|
+
# Handles server requests using threads from the thread pool.
|
6
|
+
class ServiceRequest
|
7
|
+
|
8
|
+
# Initialize a new ServiceRequest instance.
|
9
|
+
#
|
10
|
+
# Parameters:
|
11
|
+
#
|
12
|
+
# +obj+:: The service instance for which to handle the request.
|
13
|
+
# +pool+:: A reference to the thread pool.
|
14
|
+
def initialize(obj, pool)
|
15
|
+
|
16
|
+
@obj = obj
|
17
|
+
@pool = pool
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# Forward method calls to the service instance via a WorkerThread.
|
22
|
+
def method_missing(meth, *a, &b)
|
23
|
+
|
24
|
+
(t_start = Time.now) if $debug
|
25
|
+
worker = @pool.pop
|
26
|
+
debug("Waited #{"%.5f" % (Time.now - t_start)} seconds for available WorkerThread") if $debug and not t_start.nil?
|
27
|
+
worker.use_instance(@obj)
|
28
|
+
ret = worker.perform(meth, *a)
|
29
|
+
@pool.push worker
|
30
|
+
|
31
|
+
raise ret if (ret.is_a?(NoMethodError) or ret.is_a?(TypeError))
|
32
|
+
|
33
|
+
ret
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Boogaloo
|
2
|
+
|
3
|
+
class ThreadPool < Queue
|
4
|
+
|
5
|
+
# Initailize a new thread pool.
|
6
|
+
#
|
7
|
+
# Parameters:
|
8
|
+
#
|
9
|
+
# +threads+:: The number of threads to populate the pool with.
|
10
|
+
def initialize(threads)
|
11
|
+
|
12
|
+
super()
|
13
|
+
|
14
|
+
@threads = threads
|
15
|
+
|
16
|
+
for i in 1..@threads
|
17
|
+
|
18
|
+
push(Boogaloo::WorkerThread.new)
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Shutdown the pool.
|
25
|
+
def shutdown
|
26
|
+
|
27
|
+
for i in 1..@threads
|
28
|
+
|
29
|
+
worker = pop
|
30
|
+
worker.exit
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
size == 0 ? true : false
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
APP_NAME = 'Boogaloo'
|
2
|
+
|
3
|
+
# Output an error message to stderr and perform an immediate ungraceful server shutdown.
|
4
|
+
def error(msg)
|
5
|
+
|
6
|
+
$stderr.puts "[#{APP_NAME}] ERROR: #{msg}"
|
7
|
+
exit 1
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
# Output an information message.
|
12
|
+
def notice(msg)
|
13
|
+
|
14
|
+
puts "[#{APP_NAME}] #{msg}"
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
# Output a warning message to stderr.
|
19
|
+
def warning(msg)
|
20
|
+
|
21
|
+
$stderr.puts "[#{APP_NAME}] WARNING: #{msg}"
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def debug(msg)
|
26
|
+
|
27
|
+
puts "[#{APP_NAME}] DEBUG: #{msg}"
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
PKG_VERSION = "0.1"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Boogaloo
|
2
|
+
|
3
|
+
# Handles method calls to a service instance in a thread.
|
4
|
+
class WorkerThread < Thread
|
5
|
+
|
6
|
+
# Initialize a new WorkerThread instance.
|
7
|
+
def initialize
|
8
|
+
|
9
|
+
super {}
|
10
|
+
@obj = nil
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# Tells the WorkerThread to forward method calls to the given service instance.
|
15
|
+
#
|
16
|
+
# Parameters:
|
17
|
+
#
|
18
|
+
# +obj+:: The service instance to forward to.
|
19
|
+
def use_instance(obj)
|
20
|
+
|
21
|
+
@obj = obj
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Perform method calls on the service instance.
|
26
|
+
def perform(meth, *a, &b)
|
27
|
+
|
28
|
+
ret = nil
|
29
|
+
|
30
|
+
begin
|
31
|
+
|
32
|
+
ret = @obj.send(meth, *a)
|
33
|
+
|
34
|
+
rescue NoMethodError, TypeError => e
|
35
|
+
|
36
|
+
return e
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
ret
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'drb'
|
2
|
+
require 'lib/boogaloo/cache/persistent'
|
3
|
+
require 'lib/boogaloo/util'
|
4
|
+
|
5
|
+
class TestPersistentCache < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_persistent_cache
|
8
|
+
|
9
|
+
@cache = Boogaloo::Cache::Persistent.new('TestCache', nil)
|
10
|
+
do_cache_stats_test
|
11
|
+
|
12
|
+
# Test cache add
|
13
|
+
@cache.set("test_namespace", "test_key_1", "Hello, World!")
|
14
|
+
assert_equal @cache.stat_namespaces, 2
|
15
|
+
assert_equal @cache.stat_objects, 1
|
16
|
+
assert @cache.get("test_namespace", "test_key_1")
|
17
|
+
assert_equal @cache.get("test_namespace", "test_key_1"), "Hello, World!"
|
18
|
+
assert_equal @cache.stat_hits, 2
|
19
|
+
|
20
|
+
# Test cache set
|
21
|
+
@cache.set("test_namespace", "test_key_1", "Hello again, World!")
|
22
|
+
assert_equal @cache.stat_namespaces, 2
|
23
|
+
assert_equal @cache.stat_objects, 1
|
24
|
+
# set should overwrite the previous add
|
25
|
+
assert @cache.get("test_namespace", "test_key_1")
|
26
|
+
assert_equal @cache.get("test_namespace", "test_key_1"), "Hello again, World!"
|
27
|
+
assert_equal @cache.stat_hits, 4
|
28
|
+
|
29
|
+
# Test global namespace
|
30
|
+
@cache.set(nil, "test_key_2", "Hey")
|
31
|
+
assert_equal @cache.stat_namespaces, 2
|
32
|
+
assert_equal @cache.stat_objects, 2
|
33
|
+
assert @cache.get(nil, "test_key_2")
|
34
|
+
assert_equal @cache.get(nil, "test_key_2"), "Hey"
|
35
|
+
assert_equal @cache.stat_hits, 6
|
36
|
+
|
37
|
+
# Test cache miss
|
38
|
+
@cache.get("non_existent", "not there!")
|
39
|
+
@cache.get(nil, "booo")
|
40
|
+
assert_equal @cache.stat_misses, 2
|
41
|
+
|
42
|
+
# Test reset
|
43
|
+
@cache.reset
|
44
|
+
do_cache_stats_test
|
45
|
+
|
46
|
+
# Test inspect_keys
|
47
|
+
@cache.set(nil, "test_key_3", "hello!")
|
48
|
+
@cache.set("test_namespace", "test_key_4", "umbongo")
|
49
|
+
keys = @cache.inspect_keys
|
50
|
+
assert keys
|
51
|
+
assert keys.is_a?(Array)
|
52
|
+
assert_equal keys.size, 2
|
53
|
+
assert keys[0].is_a?(Array)
|
54
|
+
assert keys[1].is_a?(Hash)
|
55
|
+
assert_equal keys[0][0], "test_key_3"
|
56
|
+
assert keys[1].key?("test_namespace")
|
57
|
+
assert_equal keys[1]['test_namespace'][0], "test_key_4"
|
58
|
+
|
59
|
+
@cache.reset
|
60
|
+
|
61
|
+
# Test cache pull
|
62
|
+
@cache.set(nil, "test_key_4", "hello!")
|
63
|
+
assert_equal @cache.pull(nil, "test_key_4"), "hello!"
|
64
|
+
assert_equal @cache.get(nil, "test_key_4"), nil
|
65
|
+
assert_equal @cache.pull(nil, "test_key_4"), nil
|
66
|
+
|
67
|
+
# Test cache pull with namespace cleanup
|
68
|
+
@cache.set("pull_ns", "test_key_5", "hello!")
|
69
|
+
assert_equal @cache.stat_namespaces, 2
|
70
|
+
assert_equal @cache.pull("pull_ns", "test_key_5"), "hello!"
|
71
|
+
assert_equal @cache.get("pull_ns", "test_key_5"), nil
|
72
|
+
assert_equal @cache.pull("pull_ns", "test_key_5"), nil
|
73
|
+
assert_equal @cache.stat_namespaces, 1
|
74
|
+
|
75
|
+
# Test nil error add/set
|
76
|
+
assert_kind_of TypeError, @cache.set(nil, "test_key_6", nil)
|
77
|
+
assert_kind_of TypeError, @cache.add(nil, "test_key_6", nil)
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def do_cache_stats_test
|
82
|
+
|
83
|
+
stats = @cache.stat_all
|
84
|
+
assert @cache.stat_all.size == 5
|
85
|
+
assert stats[0].is_a?(Time)
|
86
|
+
assert stats[1] == 0
|
87
|
+
assert stats[2] == 0
|
88
|
+
assert stats[3] == 1
|
89
|
+
assert stats[4] == 0
|
90
|
+
|
91
|
+
assert @cache.stat_reset_at == stats[0]
|
92
|
+
assert @cache.stat_hits == stats[1]
|
93
|
+
assert @cache.stat_misses == stats[2]
|
94
|
+
assert @cache.stat_namespaces == stats[3]
|
95
|
+
assert @cache.stat_objects == stats[4]
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|