ezmobius-nanite 0.4.0 → 0.4.1.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/README.rdoc +70 -20
- data/Rakefile +1 -1
- data/bin/nanite-agent +34 -8
- data/bin/nanite-mapper +18 -8
- data/lib/nanite.rb +71 -0
- data/lib/nanite/actor.rb +60 -0
- data/lib/nanite/actor_registry.rb +24 -0
- data/lib/nanite/admin.rb +138 -0
- data/lib/nanite/agent.rb +250 -0
- data/lib/nanite/amqp.rb +47 -0
- data/lib/nanite/cluster.rb +203 -0
- data/lib/nanite/config.rb +102 -0
- data/lib/nanite/console.rb +39 -0
- data/lib/nanite/daemonize.rb +13 -0
- data/lib/nanite/dispatcher.rb +90 -0
- data/lib/nanite/identity.rb +16 -0
- data/lib/nanite/job.rb +104 -0
- data/lib/nanite/local_state.rb +34 -0
- data/lib/nanite/log.rb +64 -0
- data/lib/nanite/log/formatter.rb +39 -0
- data/lib/nanite/mapper.rb +277 -0
- data/lib/nanite/mapper_proxy.rb +56 -0
- data/lib/nanite/packets.rb +231 -0
- data/lib/nanite/pid_file.rb +52 -0
- data/lib/nanite/reaper.rb +38 -0
- data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
- data/lib/nanite/security/certificate.rb +55 -0
- data/lib/nanite/security/certificate_cache.rb +66 -0
- data/lib/nanite/security/distinguished_name.rb +34 -0
- data/lib/nanite/security/encrypted_document.rb +46 -0
- data/lib/nanite/security/rsa_key_pair.rb +53 -0
- data/lib/nanite/security/secure_serializer.rb +67 -0
- data/lib/nanite/security/signature.rb +40 -0
- data/lib/nanite/security/static_certificate_store.rb +35 -0
- data/lib/nanite/security_provider.rb +47 -0
- data/lib/nanite/serializer.rb +52 -0
- data/lib/nanite/state.rb +164 -0
- data/lib/nanite/streaming.rb +125 -0
- data/lib/nanite/util.rb +51 -0
- data/spec/actor_registry_spec.rb +62 -0
- data/spec/actor_spec.rb +59 -0
- data/spec/agent_spec.rb +235 -0
- data/spec/cached_certificate_store_proxy_spec.rb +34 -0
- data/spec/certificate_cache_spec.rb +49 -0
- data/spec/certificate_spec.rb +27 -0
- data/spec/cluster_spec.rb +300 -0
- data/spec/dispatcher_spec.rb +136 -0
- data/spec/distinguished_name_spec.rb +24 -0
- data/spec/encrypted_document_spec.rb +21 -0
- data/spec/job_spec.rb +219 -0
- data/spec/local_state_spec.rb +112 -0
- data/spec/packet_spec.rb +218 -0
- data/spec/rsa_key_pair_spec.rb +33 -0
- data/spec/secure_serializer_spec.rb +41 -0
- data/spec/serializer_spec.rb +107 -0
- data/spec/signature_spec.rb +30 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/static_certificate_store_spec.rb +30 -0
- data/spec/util_spec.rb +63 -0
- metadata +63 -2
data/lib/nanite/util.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
class String
|
2
|
+
##
|
3
|
+
# Convert to snake case.
|
4
|
+
#
|
5
|
+
# "FooBar".snake_case #=> "foo_bar"
|
6
|
+
# "HeadlineCNNNews".snake_case #=> "headline_cnn_news"
|
7
|
+
# "CNN".snake_case #=> "cnn"
|
8
|
+
#
|
9
|
+
# @return [String] Receiver converted to snake case.
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
def snake_case
|
13
|
+
return self.downcase if self =~ /^[A-Z]+$/
|
14
|
+
self.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
|
15
|
+
return $+.downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Convert a constant name to a path, assuming a conventional structure.
|
20
|
+
#
|
21
|
+
# "FooBar::Baz".to_const_path # => "foo_bar/baz"
|
22
|
+
#
|
23
|
+
# @return [String] Path to the file containing the constant named by receiver
|
24
|
+
# (constantized string), assuming a conventional structure.
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def to_const_path
|
28
|
+
snake_case.gsub(/::/, "/")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Object
|
33
|
+
module InstanceExecHelper; end
|
34
|
+
include InstanceExecHelper
|
35
|
+
def instance_exec(*args, &block)
|
36
|
+
begin
|
37
|
+
old_critical, Thread.critical = Thread.critical, true
|
38
|
+
n = 0
|
39
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
40
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
41
|
+
ensure
|
42
|
+
Thread.critical = old_critical
|
43
|
+
end
|
44
|
+
begin
|
45
|
+
ret = send(mname, *args)
|
46
|
+
ensure
|
47
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
48
|
+
end
|
49
|
+
ret
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::ActorRegistry do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
class WebDocumentImporter
|
7
|
+
include Nanite::Actor
|
8
|
+
expose :import, :cancel
|
9
|
+
|
10
|
+
def import
|
11
|
+
1
|
12
|
+
end
|
13
|
+
def cancel
|
14
|
+
0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Actors
|
19
|
+
class ComedyActor
|
20
|
+
include Nanite::Actor
|
21
|
+
expose :fun_tricks
|
22
|
+
def fun_tricks
|
23
|
+
:rabbit_in_the_hat
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
before(:each) do
|
30
|
+
Nanite::Log.stub!(:info)
|
31
|
+
@registry = Nanite::ActorRegistry.new
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should know about all services" do
|
35
|
+
@registry.register(WebDocumentImporter.new, nil)
|
36
|
+
@registry.register(Actors::ComedyActor.new, nil)
|
37
|
+
@registry.services.sort.should == ["/actors/comedy_actor/fun_tricks", "/web_document_importer/cancel", "/web_document_importer/import"]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not register anything except Nanite::Actor" do
|
41
|
+
lambda { @registry.register(String.new, nil) }.should raise_error(ArgumentError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should register an actor" do
|
45
|
+
importer = WebDocumentImporter.new
|
46
|
+
@registry.register(importer, nil)
|
47
|
+
@registry.actors['web_document_importer'].should == importer
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should log info message that actor was registered" do
|
51
|
+
importer = WebDocumentImporter.new
|
52
|
+
Nanite::Log.should_receive(:info).with("Registering #{importer.inspect} with prefix nil")
|
53
|
+
@registry.register(importer, nil)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should handle actors registered with a custom prefix" do
|
57
|
+
importer = WebDocumentImporter.new
|
58
|
+
@registry.register(importer, 'monkey')
|
59
|
+
@registry.actors['monkey'].should == importer
|
60
|
+
end
|
61
|
+
|
62
|
+
end # Nanite::ActorRegistry
|
data/spec/actor_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
class WebDocumentImporter
|
4
|
+
include Nanite::Actor
|
5
|
+
expose :import, :cancel
|
6
|
+
|
7
|
+
def import
|
8
|
+
1
|
9
|
+
end
|
10
|
+
def cancel
|
11
|
+
0
|
12
|
+
end
|
13
|
+
def continue
|
14
|
+
1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Actors
|
19
|
+
class ComedyActor
|
20
|
+
include Nanite::Actor
|
21
|
+
expose :fun_tricks
|
22
|
+
def fun_tricks
|
23
|
+
:rabbit_in_the_hat
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe Nanite::Actor do
|
29
|
+
|
30
|
+
describe ".expose" do
|
31
|
+
it "should single expose method only once" do
|
32
|
+
3.times { WebDocumentImporter.expose(:continue) }
|
33
|
+
WebDocumentImporter.provides_for("webfiles").should == ["/webfiles/import", "/webfiles/cancel", "/webfiles/continue"]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".default_prefix" do
|
38
|
+
it "is calculated as default prefix as const path of class name" do
|
39
|
+
Actors::ComedyActor.default_prefix.should == "actors/comedy_actor"
|
40
|
+
WebDocumentImporter.default_prefix.should == "web_document_importer"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".provides_for(prefix)" do
|
45
|
+
before :each do
|
46
|
+
@provides = Actors::ComedyActor.provides_for("money")
|
47
|
+
end
|
48
|
+
it "returns an array" do
|
49
|
+
@provides.should be_kind_of(Array)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "maps exposed service methods to prefix" do
|
53
|
+
@provides.should == ["/money/fun_tricks"]
|
54
|
+
wdi_provides = WebDocumentImporter.provides_for("webfiles")
|
55
|
+
wdi_provides.should include("/webfiles/import")
|
56
|
+
wdi_provides.should include("/webfiles/cancel")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/agent_spec.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Agent:" do
|
4
|
+
|
5
|
+
describe "Default Option" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
EM.stub!(:add_periodic_timer)
|
9
|
+
AMQP.stub!(:connect)
|
10
|
+
@amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}), :fanout => mock("fanout", :publish => nil))
|
11
|
+
MQ.stub!(:new).and_return(@amq)
|
12
|
+
@agent = Nanite::Agent.start
|
13
|
+
end
|
14
|
+
|
15
|
+
it "for daemonize is false" do
|
16
|
+
@agent.options.should include(:daemonize)
|
17
|
+
@agent.options[:daemonize].should == false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "for format is marshal" do
|
21
|
+
@agent.options.should include(:format)
|
22
|
+
@agent.options[:format].should == :marshal
|
23
|
+
end
|
24
|
+
|
25
|
+
it "for console is false" do
|
26
|
+
@agent.options.should include(:console)
|
27
|
+
@agent.options[:console].should == false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "for user is nanite" do
|
31
|
+
@agent.options.should include(:user)
|
32
|
+
@agent.options[:user].should == "nanite"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "for pass(word) is testing" do
|
36
|
+
@agent.options.should include(:pass)
|
37
|
+
@agent.options[:pass].should == "testing"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "for secure is false" do
|
41
|
+
@agent.options.should include(:secure)
|
42
|
+
@agent.options[:secure].should == false
|
43
|
+
end
|
44
|
+
|
45
|
+
it "for host is 0.0.0.0" do
|
46
|
+
@agent.options.should include(:host)
|
47
|
+
@agent.options[:host].should == "0.0.0.0"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "for log_level is info" do
|
51
|
+
@agent.options.should include(:log_level)
|
52
|
+
@agent.options[:log_level].should == :info
|
53
|
+
end
|
54
|
+
|
55
|
+
it "for vhost is /nanite" do
|
56
|
+
@agent.options.should include(:vhost)
|
57
|
+
@agent.options[:vhost].should == "/nanite"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "for ping_time is 15" do
|
61
|
+
@agent.options.should include(:ping_time)
|
62
|
+
@agent.options[:ping_time].should == 15
|
63
|
+
end
|
64
|
+
|
65
|
+
it "for default_services is []" do
|
66
|
+
@agent.options.should include(:default_services)
|
67
|
+
@agent.options[:default_services].should == []
|
68
|
+
end
|
69
|
+
|
70
|
+
it "for root is #{File.expand_path(File.join(File.dirname(__FILE__), '..'))}" do
|
71
|
+
@agent.options.should include(:root)
|
72
|
+
@agent.options[:root].should == File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
73
|
+
end
|
74
|
+
|
75
|
+
it "for file_root is #{File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))}" do
|
76
|
+
@agent.options.should include(:file_root)
|
77
|
+
@agent.options[:file_root].should == File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "Options from config.yml" do
|
83
|
+
|
84
|
+
before(:all) do
|
85
|
+
@agent = Nanite::Agent.start
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "Passed in Options" do
|
91
|
+
|
92
|
+
before(:each) do
|
93
|
+
EM.stub!(:add_periodic_timer)
|
94
|
+
AMQP.stub!(:connect)
|
95
|
+
@amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}), :fanout => mock("fanout", :publish => nil))
|
96
|
+
MQ.stub!(:new).and_return(@amq)
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO figure out how to stub call to daemonize
|
100
|
+
# it "for daemonize should override default (false)" do
|
101
|
+
# agent = Nanite::Agent.start(:daemonize => true)
|
102
|
+
# agent.options.should include(:daemonize)
|
103
|
+
# agent.options[:daemonize].should == true
|
104
|
+
# end
|
105
|
+
|
106
|
+
it "for format should override default (marshal)" do
|
107
|
+
agent = Nanite::Agent.start(:format => :json)
|
108
|
+
agent.options.should include(:format)
|
109
|
+
agent.options[:format].should == :json
|
110
|
+
end
|
111
|
+
|
112
|
+
# TODO figure out how to avoid console output
|
113
|
+
# it "for console should override default (false)" do
|
114
|
+
# agent = Nanite::Agent.start(:console => true)
|
115
|
+
# agent.options.should include(:console)
|
116
|
+
# agent.options[:console].should == true
|
117
|
+
# end
|
118
|
+
|
119
|
+
it "for user should override default (nanite)" do
|
120
|
+
agent = Nanite::Agent.start(:user => "me")
|
121
|
+
agent.options.should include(:user)
|
122
|
+
agent.options[:user].should == "me"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "for pass(word) should override default (testing)" do
|
126
|
+
agent = Nanite::Agent.start(:pass => "secret")
|
127
|
+
agent.options.should include(:pass)
|
128
|
+
agent.options[:pass].should == "secret"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "for secure should override default (false)" do
|
132
|
+
agent = Nanite::Agent.start(:secure => true)
|
133
|
+
agent.options.should include(:secure)
|
134
|
+
agent.options[:secure].should == true
|
135
|
+
end
|
136
|
+
|
137
|
+
it "for host should override default (0.0.0.0)" do
|
138
|
+
agent = Nanite::Agent.start(:host => "127.0.0.1")
|
139
|
+
agent.options.should include(:host)
|
140
|
+
agent.options[:host].should == "127.0.0.1"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "for log_level should override default (info)" do
|
144
|
+
agent = Nanite::Agent.start(:log_level => :debug)
|
145
|
+
agent.options.should include(:log_level)
|
146
|
+
agent.options[:log_level].should == :debug
|
147
|
+
end
|
148
|
+
|
149
|
+
it "for vhost should override default (/nanite)" do
|
150
|
+
agent = Nanite::Agent.start(:vhost => "/virtual_host")
|
151
|
+
agent.options.should include(:vhost)
|
152
|
+
agent.options[:vhost].should == "/virtual_host"
|
153
|
+
end
|
154
|
+
|
155
|
+
it "for ping_time should override default (15)" do
|
156
|
+
agent = Nanite::Agent.start(:ping_time => 5)
|
157
|
+
agent.options.should include(:ping_time)
|
158
|
+
agent.options[:ping_time].should == 5
|
159
|
+
end
|
160
|
+
|
161
|
+
it "for default_services should override default ([])" do
|
162
|
+
agent = Nanite::Agent.start(:default_services => [:test])
|
163
|
+
agent.options.should include(:default_services)
|
164
|
+
agent.options[:default_services].should == [:test]
|
165
|
+
end
|
166
|
+
|
167
|
+
it "for root should override default (#{File.expand_path(File.join(File.dirname(__FILE__), '..'))})" do
|
168
|
+
agent = Nanite::Agent.start(:root => File.expand_path(File.dirname(__FILE__)))
|
169
|
+
agent.options.should include(:root)
|
170
|
+
agent.options[:root].should == File.expand_path(File.dirname(__FILE__))
|
171
|
+
end
|
172
|
+
|
173
|
+
it "for file_root should override default (#{File.expand_path(File.join(File.dirname(__FILE__), '..', 'files'))})" do
|
174
|
+
agent = Nanite::Agent.start(:file_root => File.expand_path(File.dirname(__FILE__)))
|
175
|
+
agent.options.should include(:file_root)
|
176
|
+
agent.options[:file_root].should == File.expand_path(File.dirname(__FILE__))
|
177
|
+
end
|
178
|
+
|
179
|
+
it "for a single tag should result in the agent's tags being set" do
|
180
|
+
agent = Nanite::Agent.start(:tag => "sample_tag")
|
181
|
+
agent.tags.should include("sample_tag")
|
182
|
+
end
|
183
|
+
|
184
|
+
it "for multiple tags should result in the agent's tags being set" do
|
185
|
+
agent = Nanite::Agent.start(:tag => ["sample_tag_1", "sample_tag_2"])
|
186
|
+
agent.tags.should include("sample_tag_1")
|
187
|
+
agent.tags.should include("sample_tag_2")
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "Security" do
|
193
|
+
|
194
|
+
before(:each) do
|
195
|
+
EM.stub!(:add_periodic_timer)
|
196
|
+
AMQP.stub!(:connect)
|
197
|
+
@amq = mock("AMQueue", :queue => mock("queue", :subscribe => {}, :publish => {}), :fanout => mock("fanout", :publish => nil))
|
198
|
+
MQ.stub!(:new).and_return(@amq)
|
199
|
+
serializer = Nanite::Serializer.new
|
200
|
+
@request = Nanite::Request.new('/foo/bar', '')
|
201
|
+
@push = Nanite::Push.new('/foo/bar', '')
|
202
|
+
@agent = Nanite::Agent.start
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should correctly deny requests' do
|
206
|
+
security = mock("Security")
|
207
|
+
@agent.register_security(security)
|
208
|
+
|
209
|
+
security.should_receive(:authorize).twice.and_return(false)
|
210
|
+
@agent.dispatcher.should_not_receive(:dispatch)
|
211
|
+
@agent.__send__(:receive, @request)
|
212
|
+
@agent.__send__(:receive, @push)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should correctly authorize requests' do
|
216
|
+
security = mock("Security")
|
217
|
+
@agent.register_security(security)
|
218
|
+
|
219
|
+
security.should_receive(:authorize).twice.and_return(true)
|
220
|
+
@agent.dispatcher.stub!(:dispatch)
|
221
|
+
@agent.dispatcher.should_receive(:dispatch).twice
|
222
|
+
@agent.__send__(:receive, @request)
|
223
|
+
@agent.__send__(:receive, @push)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should be ignored when not specified' do
|
227
|
+
@agent.dispatcher.stub!(:dispatch)
|
228
|
+
@agent.dispatcher.should_receive(:dispatch).twice
|
229
|
+
@agent.__send__(:receive, @request)
|
230
|
+
@agent.__send__(:receive, @push)
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::CachedCertificateStoreProxy do
|
4
|
+
|
5
|
+
include SpecHelpers
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@signer, key = issue_cert
|
9
|
+
@recipient, key = issue_cert
|
10
|
+
@store = mock("Store")
|
11
|
+
@proxy = Nanite::CachedCertificateStoreProxy.new(@store)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should not raise and return nil for non existent certificates' do
|
15
|
+
res = nil
|
16
|
+
@store.should_receive(:get_recipients).with(nil).and_return(nil)
|
17
|
+
lambda { res = @proxy.get_recipients(nil) }.should_not raise_error
|
18
|
+
res.should == nil
|
19
|
+
@store.should_receive(:get_signer).with(nil).and_return(nil)
|
20
|
+
lambda { res = @proxy.get_signer(nil) }.should_not raise_error
|
21
|
+
res.should == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return recipient certificates' do
|
25
|
+
@store.should_receive(:get_recipients).with('anything').and_return(@recipient)
|
26
|
+
@proxy.get_recipients('anything').should == @recipient
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should return signer certificates' do
|
30
|
+
@store.should_receive(:get_signer).with('anything').and_return(@signer)
|
31
|
+
@proxy.get_signer('anything').should == @signer
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe Nanite::CertificateCache do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@cache = Nanite::CertificateCache.new(2)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should allow storing and retrieving objects' do
|
10
|
+
@cache['some_id'].should be_nil
|
11
|
+
@cache['some_id'] = 'some_value'
|
12
|
+
@cache['some_id'].should == 'some_value'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should not store more than required' do
|
16
|
+
@cache[1] = 'oldest'
|
17
|
+
@cache[2] = 'older'
|
18
|
+
@cache[1].should == 'oldest'
|
19
|
+
@cache[2].should == 'older'
|
20
|
+
|
21
|
+
@cache[3] = 'new'
|
22
|
+
@cache[3].should == 'new'
|
23
|
+
|
24
|
+
@cache[1].should be_nil
|
25
|
+
@cache[2].should == 'older'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should use LRU to remove entries' do
|
29
|
+
@cache[1] = 'oldest'
|
30
|
+
@cache[2] = 'older'
|
31
|
+
@cache[1].should == 'oldest'
|
32
|
+
@cache[2].should == 'older'
|
33
|
+
|
34
|
+
@cache[1] = 'new'
|
35
|
+
@cache[3] = 'newer'
|
36
|
+
@cache[1].should == 'new'
|
37
|
+
@cache[3].should == 'newer'
|
38
|
+
|
39
|
+
@cache[2].should be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should store items returned by block' do
|
43
|
+
@cache[1].should be_nil
|
44
|
+
item = @cache.get(1) { 'item' }
|
45
|
+
item.should == 'item'
|
46
|
+
@cache[1].should == 'item'
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|