remcached 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,268 @@
1
+ $: << File.dirname(__FILE__) + '/../lib'
2
+ require 'remcached'
3
+
4
+ describe Memcached do
5
+ def run(&block)
6
+ EM.run do
7
+ Memcached.servers = %w(127.0.0.2 localhost:11212 localhost localhost)
8
+
9
+ @timer = EM::PeriodicTimer.new(0.01) do
10
+ # at least localhost & localhost
11
+ if Memcached.usable_clients.length >= 2
12
+ @timer.cancel
13
+ block.call
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ def stop
20
+ Memcached.servers = []
21
+ EM.stop
22
+ end
23
+
24
+
25
+ context "when doing a simple operation" do
26
+ it "should add a value" do
27
+ run do
28
+ Memcached.add(:key => 'Hello',
29
+ :value => 'World') do |result|
30
+ result.should be_kind_of(Memcached::Response)
31
+ result[:status].should == Memcached::Errors::NO_ERROR
32
+ result[:cas].should_not == 0
33
+ stop
34
+ end
35
+ end
36
+ end
37
+
38
+ it "should get a value" do
39
+ run do
40
+ Memcached.get(:key => 'Hello') do |result|
41
+ result.should be_kind_of(Memcached::Response)
42
+ result[:status].should == Memcached::Errors::NO_ERROR
43
+ result[:value].should == 'World'
44
+ result[:cas].should_not == 0
45
+ @old_cas = result[:cas]
46
+ stop
47
+ end
48
+ end
49
+ end
50
+
51
+ it "should set a value" do
52
+ run do
53
+ Memcached.set(:key => 'Hello',
54
+ :value => 'Planet') do |result|
55
+ result.should be_kind_of(Memcached::Response)
56
+ result[:status].should == Memcached::Errors::NO_ERROR
57
+ result[:cas].should_not == 0
58
+ result[:cas].should_not == @old_cas
59
+ stop
60
+ end
61
+ end
62
+ end
63
+
64
+ it "should get a value" do
65
+ run do
66
+ Memcached.get(:key => 'Hello') do |result|
67
+ result.should be_kind_of(Memcached::Response)
68
+ result[:status].should == Memcached::Errors::NO_ERROR
69
+ result[:value].should == 'Planet'
70
+ result[:cas].should_not == @old_cas
71
+ stop
72
+ end
73
+ end
74
+ end
75
+
76
+ it "should delete a value" do
77
+ run do
78
+ Memcached.delete(:key => 'Hello') do |result|
79
+ result.should be_kind_of(Memcached::Response)
80
+ result[:status].should == Memcached::Errors::NO_ERROR
81
+ stop
82
+ end
83
+ end
84
+ end
85
+
86
+ it "should not get a value" do
87
+ run do
88
+ Memcached.get(:key => 'Hello') do |result|
89
+ result.should be_kind_of(Memcached::Response)
90
+ result[:status].should == Memcached::Errors::KEY_NOT_FOUND
91
+ stop
92
+ end
93
+ end
94
+ end
95
+
96
+ $n = 100
97
+ context "when incrementing a counter #{$n} times" do
98
+ it "should initialize the counter" do
99
+ run do
100
+ Memcached.set(:key => 'counter',
101
+ :value => '0') do |result|
102
+ stop
103
+ end
104
+ end
105
+ end
106
+
107
+ it "should count #{$n} times" do
108
+ @counted = 0
109
+ def count
110
+ Memcached.get(:key => 'counter') do |result|
111
+ result[:status].should == Memcached::Errors::NO_ERROR
112
+ value = result[:value].to_i
113
+ Memcached.set(:key => 'counter',
114
+ :value => (value + 1).to_s,
115
+ :cas => result[:cas]) do |result|
116
+ if result[:status] == Memcached::Errors::KEY_EXISTS
117
+ count # again
118
+ else
119
+ result[:status].should == Memcached::Errors::NO_ERROR
120
+ @counted += 1
121
+ stop if @counted >= $n
122
+ end
123
+ end
124
+ end
125
+ end
126
+ run do
127
+ $n.times { count }
128
+ end
129
+ end
130
+
131
+ it "should have counted up to #{$n}" do
132
+ run do
133
+ Memcached.get(:key => 'counter') do |result|
134
+ result[:status].should == Memcached::Errors::NO_ERROR
135
+ result[:value].to_i.should == $n
136
+ stop
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ context "when using multiple servers" do
144
+ it "should not return the same hash for the succeeding key" do
145
+ run do
146
+ Memcached.hash_key('0').should_not == Memcached.hash_key('1')
147
+ stop
148
+ end
149
+ end
150
+
151
+ it "should not return the same client for the succeeding key" do
152
+ run do
153
+ # wait for 2nd client to be connected
154
+ EM::Timer.new(0.1) do
155
+ Memcached.client_for_key('0').should_not == Memcached.client_for_key('1')
156
+ stop
157
+ end
158
+ end
159
+ end
160
+
161
+ it "should spread load (observe from outside :-)" do
162
+ run do
163
+
164
+ n = 10000
165
+ replies = 0
166
+ n.times do |i|
167
+ Memcached.set(:key => "#{i % 100}",
168
+ :value => rand(1 << 31).to_s) {
169
+ replies += 1
170
+ stop if replies >= n
171
+ }
172
+ end
173
+ end
174
+
175
+ end
176
+ end
177
+
178
+ context "when manipulating multiple records at once" do
179
+ before :all do
180
+ @n = 10
181
+ end
182
+
183
+ def key(n)
184
+ "test:item:#{n}"
185
+ end
186
+
187
+ it "should add some items" do
188
+ run do
189
+ items = []
190
+ @n.times { |i|
191
+ items << { :key => key(i),
192
+ :value => 'Foo',
193
+ :expiration => 20 } if i % 2 == 0
194
+ }
195
+ Memcached.multi_add(items) { |responses|
196
+ stop
197
+ @n.times { |i|
198
+ if i % 2 == 0 && (response_i = responses[key(i)])
199
+ response_i[:status].should == Memcached::Errors::NO_ERROR
200
+ end
201
+ }
202
+ }
203
+ end
204
+ end
205
+
206
+ it "should get all items" do
207
+ run do
208
+ items = []
209
+ @n.times { |i|
210
+ items << { :key => key(i) }
211
+ }
212
+ Memcached.multi_get(items) { |responses|
213
+ stop
214
+ @n.times { |i|
215
+ if i % 2 == 0
216
+ responses.should have_key(key(i))
217
+ responses[key(i)][:status].should == Memcached::Errors::NO_ERROR
218
+ responses[key(i)][:value].should == 'Foo'
219
+ else
220
+ # either no response because request was quiet, or not
221
+ # found in case of last response
222
+ if (response_i = responses[key(i)])
223
+ response_i[:status].should == Memcached::Errors::KEY_NOT_FOUND
224
+ end
225
+ end
226
+ }
227
+ }
228
+ end
229
+ end
230
+
231
+ it "should delete all items" do
232
+ run do
233
+ items = []
234
+ @n.times { |i|
235
+ items << { :key => key(i) }
236
+ }
237
+ Memcached.multi_delete(items) { |responses|
238
+ stop
239
+ @n.times { |i|
240
+ if i % 2 == 0
241
+ # either no response because request was quiet, or ok in
242
+ # case of last response
243
+ if (response_i = responses[key(i)])
244
+ response_i[:status].should == Memcached::Errors::NO_ERROR
245
+ end
246
+ else
247
+ responses[key(i)][:status].should == Memcached::Errors::KEY_NOT_FOUND
248
+ end
249
+ }
250
+ }
251
+ end
252
+ end
253
+
254
+ context "when the multi operation is empty" do
255
+ it "should return immediately" do
256
+ @results = []
257
+ @calls = 0
258
+ Memcached.multi_add([]) { |responses|
259
+ @results += responses
260
+ @calls += 1
261
+ }
262
+ @results.should be_empty
263
+ @calls.should == 1
264
+ end
265
+ end
266
+ end
267
+
268
+ end
@@ -0,0 +1,125 @@
1
+ $: << File.dirname(__FILE__) + '/../lib'
2
+ require 'remcached'
3
+
4
+ describe Memcached::Packet do
5
+
6
+ context "when generating a request" do
7
+ it "should set default values" do
8
+ pkt = Memcached::Request.new
9
+ pkt[:magic].should == 0x80
10
+ end
11
+
12
+ context "example 4.2.1" do
13
+ before :all do
14
+ pkt = Memcached::Request.new(:key => 'Hello')
15
+ @s = pkt.to_s
16
+ end
17
+
18
+ it "should serialize correctly" do
19
+ @s.should == "\x80\x00\x00\x05" +
20
+ "\x00\x00\x00\x00" +
21
+ "\x00\x00\x00\x05" +
22
+ "\x00\x00\x00\x00" +
23
+ "\x00\x00\x00\x00" +
24
+ "\x00\x00\x00\x00" +
25
+ "Hello"
26
+ end
27
+ end
28
+
29
+ context "example 4.3.1 (add)" do
30
+ before :all do
31
+ pkt = Memcached::Request::Add.new(:flags => 0xdeadbeef,
32
+ :expiration => 0xe10,
33
+ :key => "Hello",
34
+ :value => "World")
35
+ @s = pkt.to_s
36
+ end
37
+
38
+ it "should serialize correctly" do
39
+ @s.should == "\x80\x02\x00\x05" +
40
+ "\x08\x00\x00\x00" +
41
+ "\x00\x00\x00\x12" +
42
+ "\x00\x00\x00\x00" +
43
+ "\x00\x00\x00\x00" +
44
+ "\x00\x00\x00\x00" +
45
+ "\xde\xad\xbe\xef" +
46
+ "\x00\x00\x0e\x10" +
47
+ "Hello" +
48
+ "World"
49
+ end
50
+ end
51
+ end
52
+
53
+ context "when parsing a response" do
54
+ context "example 4.1.1" do
55
+ before :all do
56
+ s = "\x81\x00\x00\x00\x00\x00\x00\x01" +
57
+ "\x00\x00\x00\x09\x00\x00\x00\x00" +
58
+ "\x00\x00\x00\x00\x00\x00\x00\x00" +
59
+ "Not found"
60
+ @pkt = Memcached::Response.parse_header(s[0..23])
61
+ @pkt.parse_body(s[24..-1])
62
+ end
63
+
64
+ it "should return the right class according to magic & opcode" do
65
+ @pkt[:magic].should == 0x81
66
+ @pkt[:opcode].should == 0
67
+ @pkt.class.should == Memcached::Response
68
+ end
69
+ it "should return the right data type" do
70
+ @pkt[:data_type].should == 0
71
+ end
72
+ it "should return the right status" do
73
+ @pkt[:status].should == Memcached::Errors::KEY_NOT_FOUND
74
+ end
75
+ it "should return the right opaque" do
76
+ @pkt[:opaque].should == 0
77
+ end
78
+ it "should return the right CAS" do
79
+ @pkt[:cas].should == 0
80
+ end
81
+ it "should parse the body correctly" do
82
+ @pkt[:extras].should be_empty
83
+ @pkt[:key].should == ""
84
+ @pkt[:value].should == "Not found"
85
+ end
86
+ end
87
+
88
+ context "example 4.2.1" do
89
+ before :all do
90
+ s = "\x81\x00\x00\x00" +
91
+ "\x04\x00\x00\x00" +
92
+ "\x00\x00\x00\x09" +
93
+ "\x00\x00\x00\x00" +
94
+ "\x00\x00\x00\x00" +
95
+ "\x00\x00\x00\x01" +
96
+ "\xde\xad\xbe\xef" +
97
+ "World"
98
+ @pkt = Memcached::Response.parse_header(s[0..23])
99
+ @pkt.parse_body(s[24..-1])
100
+ end
101
+
102
+ it "should return the right class according to magic & opcode" do
103
+ @pkt[:magic].should == 0x81
104
+ @pkt[:opcode].should == 0
105
+ @pkt.class.should == Memcached::Response
106
+ end
107
+ it "should return the right data type" do
108
+ @pkt[:data_type].should == 0
109
+ end
110
+ it "should return the right status" do
111
+ @pkt[:status].should == Memcached::Errors::NO_ERROR
112
+ end
113
+ it "should return the right opaque" do
114
+ @pkt[:opaque].should == 0
115
+ end
116
+ it "should return the right CAS" do
117
+ @pkt[:cas].should == 1
118
+ end
119
+ it "should parse the body correctly" do
120
+ @pkt[:key].should == ""
121
+ @pkt[:value].should == "World"
122
+ end
123
+ end
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remcached
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - Stephan Maka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-12 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Ruby EventMachine memcached client
17
+ email: astro@spaceboyz.net
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rst
24
+ files:
25
+ - .gitignore
26
+ - README.rst
27
+ - Rakefile
28
+ - VERSION.yml
29
+ - examples/fill.rb
30
+ - lib/remcached.rb
31
+ - lib/remcached/client.rb
32
+ - lib/remcached/const.rb
33
+ - lib/remcached/pack_array.rb
34
+ - lib/remcached/packet.rb
35
+ - remcached.gemspec
36
+ - spec/client_spec.rb
37
+ - spec/memcached_spec.rb
38
+ - spec/packet_spec.rb
39
+ has_rdoc: true
40
+ homepage: http://github.com/astro/remcached/
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Ruby EventMachine memcached client
67
+ test_files:
68
+ - spec/client_spec.rb
69
+ - spec/memcached_spec.rb
70
+ - spec/packet_spec.rb
71
+ - examples/fill.rb