eventmachine 0.12.10-x86-mswin32-60 → 1.0.0.beta.2-x86-mswin32-60
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/.gitignore +2 -0
- data/Gemfile +1 -0
- data/README +80 -81
- data/Rakefile +7 -370
- data/docs/COPYING +60 -60
- data/docs/ChangeLog +211 -211
- data/docs/DEFERRABLES +246 -133
- data/docs/EPOLL +141 -141
- data/docs/GNU +281 -281
- data/docs/INSTALL +13 -13
- data/docs/KEYBOARD +42 -38
- data/docs/LEGAL +25 -25
- data/docs/LIGHTWEIGHT_CONCURRENCY +130 -70
- data/docs/PURE_RUBY +75 -75
- data/docs/RELEASE_NOTES +94 -94
- data/docs/SMTP +4 -2
- data/docs/SPAWNED_PROCESSES +148 -89
- data/docs/TODO +8 -8
- data/eventmachine.gemspec +19 -26
- data/examples/ex_channel.rb +42 -42
- data/examples/ex_queue.rb +2 -2
- data/examples/ex_tick_loop_array.rb +15 -0
- data/examples/ex_tick_loop_counter.rb +32 -0
- data/examples/helper.rb +1 -1
- data/ext/binder.cpp +0 -1
- data/ext/cmain.cpp +36 -25
- data/ext/ed.cpp +104 -113
- data/ext/ed.h +24 -30
- data/ext/em.cpp +349 -283
- data/ext/em.h +25 -29
- data/ext/eventmachine.h +5 -4
- data/ext/extconf.rb +58 -49
- data/ext/fastfilereader/extconf.rb +5 -3
- data/ext/fastfilereader/mapper.cpp +214 -214
- data/ext/fastfilereader/mapper.h +59 -59
- data/ext/fastfilereader/rubymain.cpp +127 -127
- data/ext/kb.cpp +1 -3
- data/ext/page.cpp +107 -107
- data/ext/page.h +51 -51
- data/ext/pipe.cpp +9 -11
- data/ext/project.h +12 -8
- data/ext/rubymain.cpp +138 -104
- data/java/.classpath +8 -8
- data/java/.project +17 -17
- data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -40
- data/lib/em/buftok.rb +138 -138
- data/lib/em/callback.rb +25 -25
- data/lib/em/channel.rb +1 -1
- data/lib/em/connection.rb +6 -1
- data/lib/em/deferrable.rb +16 -2
- data/lib/em/file_watch.rb +53 -53
- data/lib/em/future.rb +61 -61
- data/lib/em/iterator.rb +270 -0
- data/lib/em/messages.rb +66 -66
- data/lib/em/process_watch.rb +43 -43
- data/lib/em/protocols.rb +1 -1
- data/lib/em/protocols/header_and_content.rb +138 -138
- data/lib/em/protocols/httpclient.rb +267 -262
- data/lib/em/protocols/line_protocol.rb +28 -0
- data/lib/em/protocols/memcache.rb +322 -322
- data/lib/em/protocols/postgres3.rb +247 -247
- data/lib/em/protocols/saslauth.rb +175 -175
- data/lib/em/protocols/smtpserver.rb +640 -547
- data/lib/em/protocols/stomp.rb +200 -200
- data/lib/em/protocols/tcptest.rb +52 -52
- data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1013 -1022
- data/lib/em/queue.rb +1 -0
- data/lib/em/spawnable.rb +85 -85
- data/lib/em/streamer.rb +130 -130
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +2 -1
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +40 -84
- data/lib/jeventmachine.rb +2 -1
- data/lib/rubyeventmachine.rb +2 -0
- data/setup.rb +1585 -1585
- data/tasks/doc.rake +30 -0
- data/tasks/package.rake +85 -0
- data/tasks/test.rake +6 -0
- data/tests/client.crt +31 -31
- data/tests/client.key +51 -51
- data/tests/test_attach.rb +13 -3
- data/tests/test_basic.rb +60 -95
- data/tests/test_channel.rb +3 -2
- data/tests/test_defer.rb +49 -47
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_error_handler.rb +35 -35
- data/tests/test_errors.rb +82 -82
- data/tests/test_exc.rb +55 -55
- data/tests/test_file_watch.rb +49 -49
- data/tests/test_futures.rb +198 -198
- data/tests/test_handler_check.rb +36 -36
- data/tests/test_hc.rb +190 -218
- data/tests/test_httpclient.rb +227 -218
- data/tests/test_httpclient2.rb +3 -2
- data/tests/test_inactivity_timeout.rb +3 -3
- data/tests/test_kb.rb +60 -60
- data/tests/test_ltp.rb +13 -5
- data/tests/test_ltp2.rb +317 -317
- data/tests/test_next_tick.rb +1 -1
- data/tests/test_object_protocol.rb +36 -36
- data/tests/test_pending_connect_timeout.rb +2 -2
- data/tests/test_process_watch.rb +50 -48
- data/tests/test_proxy_connection.rb +52 -0
- data/tests/test_pure.rb +134 -125
- data/tests/test_queue.rb +44 -44
- data/tests/test_running.rb +42 -42
- data/tests/test_sasl.rb +72 -72
- data/tests/test_send_file.rb +251 -242
- data/tests/test_servers.rb +76 -76
- data/tests/test_smtpclient.rb +83 -83
- data/tests/test_smtpserver.rb +85 -85
- data/tests/test_spawn.rb +322 -322
- data/tests/test_ssl_methods.rb +49 -49
- data/tests/test_ssl_verify.rb +82 -82
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +13 -15
- data/tests/test_ud.rb +36 -36
- data/tests/testem.rb +31 -31
- metadata +66 -51
- data/ext/cplusplus.cpp +0 -202
- data/ext/emwin.cpp +0 -300
- data/ext/emwin.h +0 -94
- data/ext/epoll.cpp +0 -26
- data/ext/epoll.h +0 -25
- data/ext/eventmachine_cpp.h +0 -96
- data/ext/files.cpp +0 -94
- data/ext/files.h +0 -65
- data/ext/sigs.cpp +0 -89
- data/ext/sigs.h +0 -32
- data/java/src/com/rubyeventmachine/application/Application.java +0 -194
- data/java/src/com/rubyeventmachine/application/Connection.java +0 -74
- data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +0 -37
- data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +0 -46
- data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +0 -38
- data/java/src/com/rubyeventmachine/application/Timer.java +0 -54
- data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +0 -109
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +0 -148
- data/java/src/com/rubyeventmachine/tests/EMTest.java +0 -80
- data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +0 -53
- data/java/src/com/rubyeventmachine/tests/TestServers.java +0 -75
- data/java/src/com/rubyeventmachine/tests/TestTimers.java +0 -90
- data/lib/evma.rb +0 -32
- data/lib/evma/callback.rb +0 -32
- data/lib/evma/container.rb +0 -75
- data/lib/evma/factory.rb +0 -77
- data/lib/evma/protocol.rb +0 -87
- data/lib/evma/reactor.rb +0 -48
- data/web/whatis +0 -7
@@ -0,0 +1,28 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module Protocols
|
3
|
+
# LineProtocol will parse out newline terminated strings from a receive_data stream
|
4
|
+
#
|
5
|
+
# module Server
|
6
|
+
# include EM::P::LineProtocol
|
7
|
+
#
|
8
|
+
# def receive_line(line)
|
9
|
+
# send_data("you said: #{line}")
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
module LineProtocol
|
14
|
+
def receive_data data # :nodoc:
|
15
|
+
(@buf ||= '') << data
|
16
|
+
|
17
|
+
while line = @buf.slice!(/(.*)\r?\n/)
|
18
|
+
receive_line(line)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Invoked with lines received over the network
|
23
|
+
def receive_line(line)
|
24
|
+
# stub
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,323 +1,323 @@
|
|
1
|
-
module EventMachine
|
2
|
-
module Protocols
|
3
|
-
# Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).
|
4
|
-
# Requires memcached >= 1.2.4 w/ noreply support
|
5
|
-
#
|
6
|
-
# == Usage example
|
7
|
-
#
|
8
|
-
# EM.run{
|
9
|
-
# cache = EM::P::Memcache.connect 'localhost', 11211
|
10
|
-
#
|
11
|
-
# cache.set :a, 'hello'
|
12
|
-
# cache.set :b, 'hi'
|
13
|
-
# cache.set :c, 'how are you?'
|
14
|
-
# cache.set :d, ''
|
15
|
-
#
|
16
|
-
# cache.get(:a){ |v| p v }
|
17
|
-
# cache.get_hash(:a, :b, :c, :d){ |v| p v }
|
18
|
-
# cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
|
19
|
-
#
|
20
|
-
# cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }
|
21
|
-
#
|
22
|
-
# cache.get(:missing){ |m| p [:missing=, m] }
|
23
|
-
# cache.set(:missing, 'abc'){ p :stored }
|
24
|
-
# cache.get(:missing){ |m| p [:missing=, m] }
|
25
|
-
# cache.del(:missing){ p :deleted }
|
26
|
-
# cache.get(:missing){ |m| p [:missing=, m] }
|
27
|
-
# }
|
28
|
-
#
|
29
|
-
module Memcache
|
30
|
-
include EM::Deferrable
|
31
|
-
|
32
|
-
##
|
33
|
-
# constants
|
34
|
-
|
35
|
-
# :stopdoc:
|
36
|
-
unless defined? Cempty
|
37
|
-
Cstored = 'STORED'.freeze
|
38
|
-
Cend = 'END'.freeze
|
39
|
-
Cdeleted = 'DELETED'.freeze
|
40
|
-
Cunknown = 'NOT_FOUND'.freeze
|
41
|
-
Cerror = 'ERROR'.freeze
|
42
|
-
|
43
|
-
Cempty = ''.freeze
|
44
|
-
Cdelimiter = "\r\n".freeze
|
45
|
-
end
|
46
|
-
# :startdoc:
|
47
|
-
|
48
|
-
##
|
49
|
-
# commands
|
50
|
-
|
51
|
-
# Get the value associated with one or multiple keys
|
52
|
-
#
|
53
|
-
# cache.get(:a){ |v| p v }
|
54
|
-
# cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
|
55
|
-
#
|
56
|
-
def get *keys
|
57
|
-
raise ArgumentError unless block_given?
|
58
|
-
|
59
|
-
callback{
|
60
|
-
keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }
|
61
|
-
send_data "get #{keys.join(' ')}\r\n"
|
62
|
-
@get_cbs << [keys, proc{ |values|
|
63
|
-
yield *keys.map{ |k| values[k] }
|
64
|
-
}]
|
65
|
-
}
|
66
|
-
end
|
67
|
-
|
68
|
-
# Set the value for a given key
|
69
|
-
#
|
70
|
-
# cache.set :a, 'hello'
|
71
|
-
# cache.set(:missing, 'abc'){ puts "stored the value!" }
|
72
|
-
#
|
73
|
-
def set key, val, exptime = 0, &cb
|
74
|
-
callback{
|
75
|
-
val = val.to_s
|
76
|
-
send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?
|
77
|
-
send_data val
|
78
|
-
send_data Cdelimiter
|
79
|
-
@set_cbs << cb if cb
|
80
|
-
}
|
81
|
-
end
|
82
|
-
|
83
|
-
# Gets multiple values as a hash
|
84
|
-
#
|
85
|
-
# cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
|
86
|
-
#
|
87
|
-
def get_hash *keys
|
88
|
-
raise ArgumentError unless block_given?
|
89
|
-
|
90
|
-
get *keys do |*values|
|
91
|
-
yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Delete the value associated with a key
|
96
|
-
#
|
97
|
-
# cache.del :a
|
98
|
-
# cache.del(:b){ puts "deleted the value!" }
|
99
|
-
#
|
100
|
-
def delete key, expires = 0, &cb
|
101
|
-
callback{
|
102
|
-
send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
|
103
|
-
@del_cbs << cb if cb
|
104
|
-
}
|
105
|
-
end
|
106
|
-
alias del delete
|
107
|
-
|
108
|
-
# Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
|
109
|
-
def self.connect host = 'localhost', port = 11211
|
110
|
-
EM.connect host, port, self, host, port
|
111
|
-
end
|
112
|
-
|
113
|
-
# :stopdoc:
|
114
|
-
|
115
|
-
def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false # :nodoc:
|
116
|
-
send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
|
117
|
-
end
|
118
|
-
private :send_cmd
|
119
|
-
|
120
|
-
##
|
121
|
-
# errors
|
122
|
-
|
123
|
-
class ParserError < StandardError
|
124
|
-
end
|
125
|
-
|
126
|
-
##
|
127
|
-
# em hooks
|
128
|
-
|
129
|
-
def initialize host, port = 11211
|
130
|
-
@host, @port = host, port
|
131
|
-
end
|
132
|
-
|
133
|
-
def connection_completed
|
134
|
-
@get_cbs = []
|
135
|
-
@set_cbs = []
|
136
|
-
@del_cbs = []
|
137
|
-
|
138
|
-
@values = {}
|
139
|
-
|
140
|
-
@reconnecting = false
|
141
|
-
@connected = true
|
142
|
-
succeed
|
143
|
-
# set_delimiter "\r\n"
|
144
|
-
# set_line_mode
|
145
|
-
end
|
146
|
-
|
147
|
-
#--
|
148
|
-
# 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
|
149
|
-
# stack overflows when there is too much data.
|
150
|
-
# include EM::P::LineText2
|
151
|
-
def receive_data data
|
152
|
-
(@buffer||='') << data
|
153
|
-
|
154
|
-
while index = @buffer.index(Cdelimiter)
|
155
|
-
begin
|
156
|
-
line = @buffer.slice!(0,index+2)
|
157
|
-
process_cmd line
|
158
|
-
rescue ParserError
|
159
|
-
@buffer[0...0] = line
|
160
|
-
break
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
#--
|
166
|
-
# def receive_line line
|
167
|
-
def process_cmd line
|
168
|
-
case line.strip
|
169
|
-
when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
|
170
|
-
bytes = Integer($3)
|
171
|
-
# set_binary_mode bytes+2
|
172
|
-
# @cur_key = $1
|
173
|
-
if @buffer.size >= bytes + 2
|
174
|
-
@values[$1] = @buffer.slice!(0,bytes)
|
175
|
-
@buffer.slice!(0,2) # \r\n
|
176
|
-
else
|
177
|
-
raise ParserError
|
178
|
-
end
|
179
|
-
|
180
|
-
when Cend # END
|
181
|
-
if entry = @get_cbs.shift
|
182
|
-
keys, cb = entry
|
183
|
-
cb.call(@values)
|
184
|
-
end
|
185
|
-
@values = {}
|
186
|
-
|
187
|
-
when Cstored # STORED
|
188
|
-
if cb = @set_cbs.shift
|
189
|
-
cb.call(true)
|
190
|
-
end
|
191
|
-
|
192
|
-
when Cdeleted # DELETED
|
193
|
-
if cb = @del_cbs.shift
|
194
|
-
cb.call(true)
|
195
|
-
end
|
196
|
-
|
197
|
-
when Cunknown # NOT_FOUND
|
198
|
-
if cb = @del_cbs.shift
|
199
|
-
cb.call(false)
|
200
|
-
end
|
201
|
-
|
202
|
-
else
|
203
|
-
p [:MEMCACHE_UNKNOWN, line]
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
#--
|
208
|
-
# def receive_binary_data data
|
209
|
-
# @values[@cur_key] = data[0..-3]
|
210
|
-
# end
|
211
|
-
|
212
|
-
def unbind
|
213
|
-
if @connected or @reconnecting
|
214
|
-
EM.add_timer(1){ reconnect @host, @port }
|
215
|
-
@connected = false
|
216
|
-
@reconnecting = true
|
217
|
-
@deferred_status = nil
|
218
|
-
else
|
219
|
-
raise 'Unable to connect to memcached server'
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
# :startdoc:
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
if __FILE__ == $0
|
229
|
-
# ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
|
230
|
-
require 'em/spec'
|
231
|
-
|
232
|
-
class TestConnection # :nodoc:
|
233
|
-
include EM::P::Memcache
|
234
|
-
def send_data data
|
235
|
-
sent_data << data
|
236
|
-
end
|
237
|
-
def sent_data
|
238
|
-
@sent_data ||= ''
|
239
|
-
end
|
240
|
-
|
241
|
-
def initialize
|
242
|
-
connection_completed
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
EM.describe EM::Protocols::Memcache do
|
247
|
-
|
248
|
-
before{
|
249
|
-
@c = TestConnection.new
|
250
|
-
}
|
251
|
-
|
252
|
-
should 'send get requests' do
|
253
|
-
@c.get('a'){}
|
254
|
-
@c.sent_data.should == "get a\r\n"
|
255
|
-
done
|
256
|
-
end
|
257
|
-
|
258
|
-
should 'send set requests' do
|
259
|
-
@c.set('a', 1){}
|
260
|
-
@c.sent_data.should == "set a 0 0 1\r\n1\r\n"
|
261
|
-
done
|
262
|
-
end
|
263
|
-
|
264
|
-
should 'use noreply on set without block' do
|
265
|
-
@c.set('a', 1)
|
266
|
-
@c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"
|
267
|
-
done
|
268
|
-
end
|
269
|
-
|
270
|
-
should 'send delete requests' do
|
271
|
-
@c.del('a')
|
272
|
-
@c.sent_data.should == "delete a 0 noreply\r\n"
|
273
|
-
done
|
274
|
-
end
|
275
|
-
|
276
|
-
should 'work when get returns no values' do
|
277
|
-
@c.get('a'){ |a|
|
278
|
-
a.should.be.nil
|
279
|
-
done
|
280
|
-
}
|
281
|
-
|
282
|
-
@c.receive_data "END\r\n"
|
283
|
-
end
|
284
|
-
|
285
|
-
should 'invoke block on set' do
|
286
|
-
@c.set('a', 1){
|
287
|
-
done
|
288
|
-
}
|
289
|
-
|
290
|
-
@c.receive_data "STORED\r\n"
|
291
|
-
end
|
292
|
-
|
293
|
-
should 'invoke block on delete' do
|
294
|
-
@c.delete('a'){ |found|
|
295
|
-
found.should.be.false
|
296
|
-
}
|
297
|
-
@c.delete('b'){ |found|
|
298
|
-
found.should.be.true
|
299
|
-
done
|
300
|
-
}
|
301
|
-
|
302
|
-
@c.receive_data "NOT_FOUND\r\n"
|
303
|
-
@c.receive_data "DELETED\r\n"
|
304
|
-
end
|
305
|
-
|
306
|
-
should 'parse split responses' do
|
307
|
-
@c.get('a'){ |a|
|
308
|
-
a.should == 'abc'
|
309
|
-
done
|
310
|
-
}
|
311
|
-
|
312
|
-
@c.receive_data "VAL"
|
313
|
-
@c.receive_data "UE a 0 "
|
314
|
-
@c.receive_data "3\r\n"
|
315
|
-
@c.receive_data "ab"
|
316
|
-
@c.receive_data "c"
|
317
|
-
@c.receive_data "\r\n"
|
318
|
-
@c.receive_data "EN"
|
319
|
-
@c.receive_data "D\r\n"
|
320
|
-
end
|
321
|
-
|
322
|
-
end
|
1
|
+
module EventMachine
|
2
|
+
module Protocols
|
3
|
+
# Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).
|
4
|
+
# Requires memcached >= 1.2.4 w/ noreply support
|
5
|
+
#
|
6
|
+
# == Usage example
|
7
|
+
#
|
8
|
+
# EM.run{
|
9
|
+
# cache = EM::P::Memcache.connect 'localhost', 11211
|
10
|
+
#
|
11
|
+
# cache.set :a, 'hello'
|
12
|
+
# cache.set :b, 'hi'
|
13
|
+
# cache.set :c, 'how are you?'
|
14
|
+
# cache.set :d, ''
|
15
|
+
#
|
16
|
+
# cache.get(:a){ |v| p v }
|
17
|
+
# cache.get_hash(:a, :b, :c, :d){ |v| p v }
|
18
|
+
# cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
|
19
|
+
#
|
20
|
+
# cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }
|
21
|
+
#
|
22
|
+
# cache.get(:missing){ |m| p [:missing=, m] }
|
23
|
+
# cache.set(:missing, 'abc'){ p :stored }
|
24
|
+
# cache.get(:missing){ |m| p [:missing=, m] }
|
25
|
+
# cache.del(:missing){ p :deleted }
|
26
|
+
# cache.get(:missing){ |m| p [:missing=, m] }
|
27
|
+
# }
|
28
|
+
#
|
29
|
+
module Memcache
|
30
|
+
include EM::Deferrable
|
31
|
+
|
32
|
+
##
|
33
|
+
# constants
|
34
|
+
|
35
|
+
# :stopdoc:
|
36
|
+
unless defined? Cempty
|
37
|
+
Cstored = 'STORED'.freeze
|
38
|
+
Cend = 'END'.freeze
|
39
|
+
Cdeleted = 'DELETED'.freeze
|
40
|
+
Cunknown = 'NOT_FOUND'.freeze
|
41
|
+
Cerror = 'ERROR'.freeze
|
42
|
+
|
43
|
+
Cempty = ''.freeze
|
44
|
+
Cdelimiter = "\r\n".freeze
|
45
|
+
end
|
46
|
+
# :startdoc:
|
47
|
+
|
48
|
+
##
|
49
|
+
# commands
|
50
|
+
|
51
|
+
# Get the value associated with one or multiple keys
|
52
|
+
#
|
53
|
+
# cache.get(:a){ |v| p v }
|
54
|
+
# cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
|
55
|
+
#
|
56
|
+
def get *keys
|
57
|
+
raise ArgumentError unless block_given?
|
58
|
+
|
59
|
+
callback{
|
60
|
+
keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }
|
61
|
+
send_data "get #{keys.join(' ')}\r\n"
|
62
|
+
@get_cbs << [keys, proc{ |values|
|
63
|
+
yield *keys.map{ |k| values[k] }
|
64
|
+
}]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Set the value for a given key
|
69
|
+
#
|
70
|
+
# cache.set :a, 'hello'
|
71
|
+
# cache.set(:missing, 'abc'){ puts "stored the value!" }
|
72
|
+
#
|
73
|
+
def set key, val, exptime = 0, &cb
|
74
|
+
callback{
|
75
|
+
val = val.to_s
|
76
|
+
send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?
|
77
|
+
send_data val
|
78
|
+
send_data Cdelimiter
|
79
|
+
@set_cbs << cb if cb
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
# Gets multiple values as a hash
|
84
|
+
#
|
85
|
+
# cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
|
86
|
+
#
|
87
|
+
def get_hash *keys
|
88
|
+
raise ArgumentError unless block_given?
|
89
|
+
|
90
|
+
get *keys do |*values|
|
91
|
+
yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Delete the value associated with a key
|
96
|
+
#
|
97
|
+
# cache.del :a
|
98
|
+
# cache.del(:b){ puts "deleted the value!" }
|
99
|
+
#
|
100
|
+
def delete key, expires = 0, &cb
|
101
|
+
callback{
|
102
|
+
send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
|
103
|
+
@del_cbs << cb if cb
|
104
|
+
}
|
105
|
+
end
|
106
|
+
alias del delete
|
107
|
+
|
108
|
+
# Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
|
109
|
+
def self.connect host = 'localhost', port = 11211
|
110
|
+
EM.connect host, port, self, host, port
|
111
|
+
end
|
112
|
+
|
113
|
+
# :stopdoc:
|
114
|
+
|
115
|
+
def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false # :nodoc:
|
116
|
+
send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
|
117
|
+
end
|
118
|
+
private :send_cmd
|
119
|
+
|
120
|
+
##
|
121
|
+
# errors
|
122
|
+
|
123
|
+
class ParserError < StandardError
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# em hooks
|
128
|
+
|
129
|
+
def initialize host, port = 11211
|
130
|
+
@host, @port = host, port
|
131
|
+
end
|
132
|
+
|
133
|
+
def connection_completed
|
134
|
+
@get_cbs = []
|
135
|
+
@set_cbs = []
|
136
|
+
@del_cbs = []
|
137
|
+
|
138
|
+
@values = {}
|
139
|
+
|
140
|
+
@reconnecting = false
|
141
|
+
@connected = true
|
142
|
+
succeed
|
143
|
+
# set_delimiter "\r\n"
|
144
|
+
# set_line_mode
|
145
|
+
end
|
146
|
+
|
147
|
+
#--
|
148
|
+
# 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
|
149
|
+
# stack overflows when there is too much data.
|
150
|
+
# include EM::P::LineText2
|
151
|
+
def receive_data data
|
152
|
+
(@buffer||='') << data
|
153
|
+
|
154
|
+
while index = @buffer.index(Cdelimiter)
|
155
|
+
begin
|
156
|
+
line = @buffer.slice!(0,index+2)
|
157
|
+
process_cmd line
|
158
|
+
rescue ParserError
|
159
|
+
@buffer[0...0] = line
|
160
|
+
break
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
#--
|
166
|
+
# def receive_line line
|
167
|
+
def process_cmd line
|
168
|
+
case line.strip
|
169
|
+
when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
|
170
|
+
bytes = Integer($3)
|
171
|
+
# set_binary_mode bytes+2
|
172
|
+
# @cur_key = $1
|
173
|
+
if @buffer.size >= bytes + 2
|
174
|
+
@values[$1] = @buffer.slice!(0,bytes)
|
175
|
+
@buffer.slice!(0,2) # \r\n
|
176
|
+
else
|
177
|
+
raise ParserError
|
178
|
+
end
|
179
|
+
|
180
|
+
when Cend # END
|
181
|
+
if entry = @get_cbs.shift
|
182
|
+
keys, cb = entry
|
183
|
+
cb.call(@values)
|
184
|
+
end
|
185
|
+
@values = {}
|
186
|
+
|
187
|
+
when Cstored # STORED
|
188
|
+
if cb = @set_cbs.shift
|
189
|
+
cb.call(true)
|
190
|
+
end
|
191
|
+
|
192
|
+
when Cdeleted # DELETED
|
193
|
+
if cb = @del_cbs.shift
|
194
|
+
cb.call(true)
|
195
|
+
end
|
196
|
+
|
197
|
+
when Cunknown # NOT_FOUND
|
198
|
+
if cb = @del_cbs.shift
|
199
|
+
cb.call(false)
|
200
|
+
end
|
201
|
+
|
202
|
+
else
|
203
|
+
p [:MEMCACHE_UNKNOWN, line]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
#--
|
208
|
+
# def receive_binary_data data
|
209
|
+
# @values[@cur_key] = data[0..-3]
|
210
|
+
# end
|
211
|
+
|
212
|
+
def unbind
|
213
|
+
if @connected or @reconnecting
|
214
|
+
EM.add_timer(1){ reconnect @host, @port }
|
215
|
+
@connected = false
|
216
|
+
@reconnecting = true
|
217
|
+
@deferred_status = nil
|
218
|
+
else
|
219
|
+
raise 'Unable to connect to memcached server'
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# :startdoc:
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
if __FILE__ == $0
|
229
|
+
# ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
|
230
|
+
require 'em/spec'
|
231
|
+
|
232
|
+
class TestConnection # :nodoc:
|
233
|
+
include EM::P::Memcache
|
234
|
+
def send_data data
|
235
|
+
sent_data << data
|
236
|
+
end
|
237
|
+
def sent_data
|
238
|
+
@sent_data ||= ''
|
239
|
+
end
|
240
|
+
|
241
|
+
def initialize
|
242
|
+
connection_completed
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
EM.describe EM::Protocols::Memcache do
|
247
|
+
|
248
|
+
before{
|
249
|
+
@c = TestConnection.new
|
250
|
+
}
|
251
|
+
|
252
|
+
should 'send get requests' do
|
253
|
+
@c.get('a'){}
|
254
|
+
@c.sent_data.should == "get a\r\n"
|
255
|
+
done
|
256
|
+
end
|
257
|
+
|
258
|
+
should 'send set requests' do
|
259
|
+
@c.set('a', 1){}
|
260
|
+
@c.sent_data.should == "set a 0 0 1\r\n1\r\n"
|
261
|
+
done
|
262
|
+
end
|
263
|
+
|
264
|
+
should 'use noreply on set without block' do
|
265
|
+
@c.set('a', 1)
|
266
|
+
@c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"
|
267
|
+
done
|
268
|
+
end
|
269
|
+
|
270
|
+
should 'send delete requests' do
|
271
|
+
@c.del('a')
|
272
|
+
@c.sent_data.should == "delete a 0 noreply\r\n"
|
273
|
+
done
|
274
|
+
end
|
275
|
+
|
276
|
+
should 'work when get returns no values' do
|
277
|
+
@c.get('a'){ |a|
|
278
|
+
a.should.be.nil
|
279
|
+
done
|
280
|
+
}
|
281
|
+
|
282
|
+
@c.receive_data "END\r\n"
|
283
|
+
end
|
284
|
+
|
285
|
+
should 'invoke block on set' do
|
286
|
+
@c.set('a', 1){
|
287
|
+
done
|
288
|
+
}
|
289
|
+
|
290
|
+
@c.receive_data "STORED\r\n"
|
291
|
+
end
|
292
|
+
|
293
|
+
should 'invoke block on delete' do
|
294
|
+
@c.delete('a'){ |found|
|
295
|
+
found.should.be.false
|
296
|
+
}
|
297
|
+
@c.delete('b'){ |found|
|
298
|
+
found.should.be.true
|
299
|
+
done
|
300
|
+
}
|
301
|
+
|
302
|
+
@c.receive_data "NOT_FOUND\r\n"
|
303
|
+
@c.receive_data "DELETED\r\n"
|
304
|
+
end
|
305
|
+
|
306
|
+
should 'parse split responses' do
|
307
|
+
@c.get('a'){ |a|
|
308
|
+
a.should == 'abc'
|
309
|
+
done
|
310
|
+
}
|
311
|
+
|
312
|
+
@c.receive_data "VAL"
|
313
|
+
@c.receive_data "UE a 0 "
|
314
|
+
@c.receive_data "3\r\n"
|
315
|
+
@c.receive_data "ab"
|
316
|
+
@c.receive_data "c"
|
317
|
+
@c.receive_data "\r\n"
|
318
|
+
@c.receive_data "EN"
|
319
|
+
@c.receive_data "D\r\n"
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
323
|
end
|