evented-memcache-client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGELOG +113 -0
  2. data/COPYING +348 -0
  3. data/README +75 -0
  4. data/Rakefile +123 -0
  5. data/doc/classes/EventMachine.html +146 -0
  6. data/doc/classes/EventMachine/Protocols.html +135 -0
  7. data/doc/classes/EventMachine/Protocols/Memcache.html +153 -0
  8. data/doc/classes/EventMachine/Protocols/Memcache/Client.html +121 -0
  9. data/doc/classes/EventMachine/Protocols/Memcache/Connectable.html +615 -0
  10. data/doc/classes/EventMachine/Protocols/Memcache/Connection.html +192 -0
  11. data/doc/classes/EventMachine/Protocols/Memcache/Sender.html +389 -0
  12. data/doc/classes/EventMachine/Protocols/Memcache/Server.html +463 -0
  13. data/doc/classes/EventMachine/Protocols/Memcache/TenaciousMC.html +277 -0
  14. data/doc/classes/MyHandler.html +197 -0
  15. data/doc/created.rid +1 -0
  16. data/doc/files/COPYING.html +473 -0
  17. data/doc/files/README.html +187 -0
  18. data/doc/files/extras/consumer_rb.html +110 -0
  19. data/doc/files/extras/producer_rb.html +110 -0
  20. data/doc/files/extras/server_rb.html +110 -0
  21. data/doc/files/lib/evented-memcache-client_rb.html +125 -0
  22. data/doc/files/lib/evented_memcache_client/client_rb.html +120 -0
  23. data/doc/files/lib/evented_memcache_client/connectable_rb.html +120 -0
  24. data/doc/files/lib/evented_memcache_client/connection_rb.html +120 -0
  25. data/doc/files/lib/evented_memcache_client/sender_rb.html +112 -0
  26. data/doc/files/lib/evented_memcache_client/server_rb.html +120 -0
  27. data/doc/files/lib/evented_memcache_client/tenacious_rb.html +120 -0
  28. data/doc/fr_class_index.html +36 -0
  29. data/doc/fr_file_index.html +38 -0
  30. data/doc/fr_method_index.html +66 -0
  31. data/doc/index.html +24 -0
  32. data/doc/rdoc-style.css +208 -0
  33. data/extras/consumer.rb +41 -0
  34. data/extras/producer.rb +22 -0
  35. data/extras/server.rb +33 -0
  36. data/lib/evented-memcache-client.rb +37 -0
  37. data/lib/evented_memcache_client/client.rb +36 -0
  38. data/lib/evented_memcache_client/connectable.rb +324 -0
  39. data/lib/evented_memcache_client/connection.rb +73 -0
  40. data/lib/evented_memcache_client/sender.rb +184 -0
  41. data/lib/evented_memcache_client/server.rb +118 -0
  42. data/lib/evented_memcache_client/tenacious.rb +106 -0
  43. metadata +115 -0
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ Evented Memcache Client
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>Evented Memcache Client</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/README.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'eventmachine'
5
+ require 'evented-memcache-client'
6
+
7
+ module MyHandler
8
+ def handle_open(conn)
9
+ @opened_at = Time.now
10
+ puts 'Opened connection. Fetching...'
11
+ end
12
+ def handle_close(conn)
13
+ if @opened_at
14
+ puts 'Lost connection to memcached.'
15
+ else
16
+ puts 'Failed to open connection to memcached, retrying.'
17
+ end
18
+ end
19
+ def handle_value(conn, data, args)
20
+ key = args[0]
21
+ flags = args[1]
22
+ puts "Received message: '#{data}'"
23
+ puts "Fetching more... #{@key}."
24
+ conn.get(:key => key)
25
+ end
26
+ end
27
+
28
+ host = 'localhost'
29
+ port = 12345
30
+ EventMachine::run {
31
+ EventMachine::connect(host,
32
+ port,
33
+ EventMachine::Protocols::Memcache::TenaciousMC,
34
+ MyHandler,
35
+ {
36
+ :host => host,
37
+ :port => port,
38
+ :eager => true,
39
+ :key => 'x'
40
+ })
41
+ }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'eventmachine'
5
+ require 'evented-memcache-client'
6
+
7
+ hash = {
8
+ :open => Proc.new { |conn|
9
+ p 'open'
10
+ conn.set(:key => 'x', :data => Time.now.to_s)
11
+ },
12
+ :stored => Proc.new { |conn, data, args|
13
+ puts "Stored a value"
14
+ EventMachine::stop
15
+ },
16
+ }
17
+ EventMachine::run {
18
+ EventMachine::connect('127.0.0.1',
19
+ 12345,
20
+ EventMachine::Protocols::Memcache::Client,
21
+ hash)
22
+ }
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'eventmachine'
5
+ require 'evented-memcache-client'
6
+
7
+ # This is an example dummy server implemented using
8
+ # EventMachine::Protocols::Memcache::Server.
9
+ #
10
+ # Run it without command line arguments. It will accept client
11
+ # connections, and only handles the memcache protocol message 'set',
12
+ # which will cause it to print out the first 20 bytes of the payload
13
+ # sent by the client. It responds to the client connection with a
14
+ # 'stored'.
15
+
16
+ server = EventMachine::Protocols::Memcache::Server.new
17
+ class << server
18
+ def set(conn, data, args)
19
+ puts "set: #{data[0..20]}"
20
+ conn.stored
21
+ end
22
+ def get(conn, data, args)
23
+ data = 'You betcha'
24
+ conn.value(:key=>args[0],
25
+ :flags => 0,
26
+ :bytes => data.length,
27
+ :data => data)
28
+ conn.end
29
+ end
30
+ end
31
+ EventMachine::run {
32
+ server.start
33
+ }
@@ -0,0 +1,37 @@
1
+ #############################################################################
2
+ #
3
+ # Author:: Colin Steele (colin@colinsteele.org)
4
+ # Homepage::
5
+ #
6
+ #----------------------------------------------------------------------------
7
+ #
8
+ # Copyright (C) 2008 by Colin Steele. All Rights Reserved.
9
+ # colin@colinsteele.org
10
+ #
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of either: 1) the GNU General Public License
13
+ # as published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version; or 2) Ruby's License.
15
+ #
16
+ # See the file COPYING for complete licensing information.
17
+ #
18
+ #---------------------------------------------------------------------------
19
+ #
20
+ #
21
+ #############################################################################
22
+
23
+ require 'strscan'
24
+ require 'evented_memcache_client/sender'
25
+ require 'evented_memcache_client/connectable'
26
+ require 'evented_memcache_client/connection'
27
+ require 'evented_memcache_client/client'
28
+ require 'evented_memcache_client/tenacious'
29
+ require 'evented_memcache_client/server'
30
+
31
+ module EventMachine
32
+ module Protocols
33
+ module Memcache
34
+ VERSION = "1.0.0"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ #############################################################################
2
+ #
3
+ # Author:: Colin Steele (colin@colinsteele.org)
4
+ # Homepage::
5
+ #
6
+ #----------------------------------------------------------------------------
7
+ #
8
+ # Copyright (C) 2008 by Colin Steele. All Rights Reserved.
9
+ # colin@colinsteele.org
10
+ #
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of either: 1) the GNU General Public License
13
+ # as published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version; or 2) Ruby's License.
15
+ #
16
+ # See the file COPYING for complete licensing information.
17
+ #
18
+ #---------------------------------------------------------------------------
19
+ #
20
+ #
21
+ #############################################################################
22
+
23
+ require 'rubygems'
24
+ require 'eventmachine'
25
+
26
+ module EventMachine
27
+ module Protocols
28
+ module Memcache
29
+ # A functional (though simple) client-side implementation of the
30
+ # memcache protocol for EventMachine-based systems. See
31
+ # extras/producer.rb and extras/consumer.rb for examples.
32
+ class Client < Connection
33
+ end # Client
34
+ end # Memcache
35
+ end # module Protocols
36
+ end # module EventMachine
@@ -0,0 +1,324 @@
1
+ #############################################################################
2
+ #
3
+ # Author:: Colin Steele (colin@colinsteele.org)
4
+ # Homepage::
5
+ #
6
+ #----------------------------------------------------------------------------
7
+ #
8
+ # Copyright (C) 2008 by Colin Steele. All Rights Reserved.
9
+ # colin@colinsteele.org
10
+ #
11
+ # This program is free software; you can redistribute it and/or modify
12
+ # it under the terms of either: 1) the GNU General Public License
13
+ # as published by the Free Software Foundation; either version 2 of the
14
+ # License, or (at your option) any later version; or 2) Ruby's License.
15
+ #
16
+ # See the file COPYING for complete licensing information.
17
+ #
18
+ #---------------------------------------------------------------------------
19
+ #
20
+ #
21
+ #############################################################################
22
+
23
+ require 'rubygems'
24
+ require 'eventmachine'
25
+
26
+ module EventMachine
27
+ module Protocols
28
+ module Memcache
29
+
30
+ # Included by Connection to implement the interface to
31
+ # EventMachine::Connection; handles the opening/closing of the
32
+ # connection, and when data is received from the remote
33
+ # endpoint. Parses messages and delivers callbacks to higher
34
+ # level application code.
35
+
36
+ module Connectable
37
+
38
+ HEADER_REGEX = /([^\r\n]+?)\r\n/m
39
+
40
+ include Sender
41
+
42
+ # Install callbacks.
43
+ #
44
+ # +handler+ may be a Hash of Procs, a Module, or an object.
45
+ # If it's a hash of Procs, each key is a callback like :open,
46
+ # :close, :unknown_message, and :foo, where foo is a memcache
47
+ # protocol message (eg, :stored).
48
+ #
49
+ # If +handler+ is a Module, it is assumed that it defines a
50
+ # number of methods like handle_open, handle_close, and
51
+ # handle_foo, where foo is a is a memcache protocol message
52
+ # (eg, handle_stored). +handler+ is then +include+'d, making
53
+ # its methods available as callbacks.
54
+ #
55
+ # If +handler+ is an object, Connectable will invoke methods
56
+ # on it corresponding to the abovementioned. Eg., :open,
57
+ # :closed, :unknown_message, and :<memcache_message>, like
58
+ # :stored, :value, :end, etc.
59
+ #
60
+ def set_handler(handler)
61
+ @handler = nil
62
+ @callbacks = {}
63
+ if handler.respond_to?(:keys)
64
+ @callbacks = handler
65
+ elsif handler.is_a?(Module)
66
+ self.class.class_eval {
67
+ include handler
68
+ }
69
+ else
70
+ @handler = handler
71
+ end
72
+ end
73
+
74
+ # Called first(?) reactor spin after instantiated.
75
+ def post_init
76
+ @data = ""
77
+
78
+ # Stats
79
+
80
+ @rcvs = 0
81
+ @msgs_in = 0
82
+ @msgs_out = 0
83
+ @rcv_stats = {}
84
+ @snd_stats = {}
85
+ @opened_at = nil
86
+ @closed_at = nil
87
+ end
88
+
89
+ # Called after connection established. NB: not called for
90
+ # passive (server) connections
91
+ def connection_completed
92
+ @opened_at = Time.now
93
+ handle(:open, self)
94
+ end
95
+
96
+ # Called from EM::Connection when we have data
97
+ def receive_data(data)
98
+ @rcvs += 1
99
+ @data << data
100
+ parse_incoming_data
101
+ end
102
+
103
+ # Called from EM::Connection when the connection is dead.
104
+ def unbind
105
+ @closed_at = Time.now
106
+ @data = nil
107
+ handle(:close, self)
108
+ end
109
+
110
+ ######################################################################
111
+ # Message parsing routines
112
+ ######################################################################
113
+
114
+ # See if we can pull a header out of the incoming data buffer
115
+ def parse_incoming_data
116
+ @scanner = StringScanner.new(@data)
117
+ while (header = @scanner.scan(HEADER_REGEX)) do
118
+ len = @data.length
119
+ parse_header(header)
120
+ if len == @data.length
121
+ break # it didn't take anything out, so there's not enough
122
+ end
123
+ @msgs_in += 1
124
+ @scanner = StringScanner.new(@data)
125
+ end
126
+ end
127
+
128
+ # Figure out what the header is, and call the appropriate
129
+ # parsing command.
130
+ def parse_header(header_data)
131
+ elements = header_data.split(/\s/)
132
+ command_name = elements[0].downcase
133
+ if self.respond_to?("parse_#{command_name}")
134
+ self.send("parse_#{command_name}", elements[1..-1])
135
+ else
136
+ handle(:unknown_message, self, command_name, nil, nil)
137
+ @data.slice!(0, @scanner.pos)
138
+ end
139
+ end
140
+
141
+ NO_ARG_MESSAGES = [
142
+ 'error',
143
+ 'stored',
144
+ 'not_stored',
145
+ 'exists',
146
+ 'not_found',
147
+ 'end',
148
+ 'deleted',
149
+ 'ok',
150
+ ]
151
+ NO_ARG_MESSAGES.each { |method_name|
152
+ class_eval do
153
+ define_method "parse_#{method_name}" do |args|
154
+ receive_message(method_name, nil, nil)
155
+ @data.slice!(0, @scanner.pos)
156
+ end
157
+ end
158
+ }
159
+
160
+ ARG_MESSAGES = [
161
+ 'client_error',
162
+ 'server_error',
163
+ 'stat',
164
+ 'version',
165
+ 'get',
166
+ 'gets',
167
+ ]
168
+ ARG_MESSAGES.each { |method_name|
169
+ class_eval do
170
+ define_method "parse_#{method_name}" do |args|
171
+ receive_message(method_name, nil, args)
172
+ @data.slice!(0, @scanner.pos)
173
+ end
174
+ end
175
+ }
176
+
177
+ BODY_MESSAGES = []
178
+ BODY_MESSAGES.each { |method_name|
179
+ class_eval do
180
+ define_method "parse_#{method_name}" do |args|
181
+ body_length = args[0].to_i
182
+ # Add two bytes for delimiting "\r\n"
183
+ if @scanner.rest_size >= (body_length + 2)
184
+ receive_message(method_name,
185
+ @scanner.rest[0..(body_length - 1)])
186
+ @scanner.pos = @scanner.pos + body_length + 2
187
+ @data.slice!(0, @scanner.pos)
188
+ end
189
+ end
190
+ end
191
+ }
192
+
193
+ ARG_AND_BODY_MESSAGES = ['reserved']
194
+ ARG_AND_BODY_MESSAGES.each { |method_name|
195
+ class_eval do
196
+ define_method "parse_#{method_name}" do |args|
197
+ job_id = args[0]
198
+ body_length = args[1].to_i
199
+ # Add two bytes for delimiting "\r\n"
200
+ if @scanner.rest_size >= (body_length + 2)
201
+ receive_message(method_name,
202
+ @scanner.rest[0..(body_length - 1)])
203
+ @scanner.pos = @scanner.pos + body_length + 2
204
+ @data.slice!(0, @scanner.pos)
205
+ end
206
+ end
207
+ end
208
+ }
209
+
210
+ ARGS_AND_BODY_MESSAGES = [
211
+ 'value',
212
+ 'set',
213
+ 'add',
214
+ 'replace',
215
+ 'append',
216
+ 'prepend',
217
+ ]
218
+ ARGS_AND_BODY_MESSAGES.each { |method_name|
219
+ class_eval do
220
+ define_method "parse_#{method_name}" do |args|
221
+ body_length = args.last.to_i
222
+ if method_name == 'value'
223
+ # If there are 4 args, the last one is the cas value
224
+ if args.length > 3
225
+ body_length = args[-2].to_i
226
+ end
227
+ end
228
+ # Add two bytes for delimiting "\r\n"
229
+ if @scanner.rest_size >= (body_length + 2)
230
+ pos = @scanner.pos
231
+ endpos = pos + (body_length - 1)
232
+ receive_message(method_name,
233
+ @data[pos..endpos],
234
+ args)
235
+ @data.slice!(0, endpos + 3) # endpos is an index; plus "\r\n"
236
+ end
237
+ end
238
+ end
239
+ }
240
+
241
+ ALL_MESSAGES =
242
+ NO_ARG_MESSAGES + ARG_MESSAGES +
243
+ BODY_MESSAGES + ARG_AND_BODY_MESSAGES + ARGS_AND_BODY_MESSAGES
244
+
245
+ # Generic "You've got mail"... Pass it off to the specific
246
+ # handler method.
247
+ def receive_message(message_name, data, args=nil)
248
+ @rcv_stats[message_name] ||= 0
249
+ @rcv_stats[message_name] += 1
250
+ if self.respond_to?("receive_#{message_name}")
251
+ self.send("receive_#{message_name}", data, args)
252
+ else
253
+ handle(:unknown_message, self, message_name, data, args)
254
+ end
255
+ end
256
+
257
+ ######################################################################
258
+ # Message interface - receiving
259
+ ######################################################################
260
+
261
+ # Call the user-supplied callback method for the message we got.
262
+ ALL_MESSAGES.each{ |method_name|
263
+ class_eval do
264
+ define_method "receive_#{method_name}" do |data, args|
265
+ handle(method_name.to_sym, self, data, args)
266
+ end
267
+ end
268
+ }
269
+
270
+ ######################################################################
271
+ # Other
272
+ ######################################################################
273
+
274
+ # Turns this sucker into a human-readable string with some
275
+ # semi-useful information.
276
+ def to_s
277
+ str = ''
278
+ str << "EM signature: #{signature} peer: #{remote_endpoint}\n"
279
+ str << "opened: #{@opened_at} (#{Time.now - @opened_at}s)\n"
280
+ str << "rcvs: #{@rcvs} in: #{@msgs_in} out: #{@msgs_out}\n"
281
+ @rcv_stats.each {|msg_name,count|
282
+ str << "#{msg_name} in: #{count}\n"
283
+ }
284
+ @snd_stats.each {|msg_name,count|
285
+ str << "#{msg_name} out: #{count}\n"
286
+ }
287
+ str << "I+O/sec: #{'%6.4f' % io_per_sec}."
288
+ str
289
+ end
290
+
291
+ # Begin shutting the connection down.
292
+ def start_closing
293
+ close_connection_after_writing
294
+ end
295
+
296
+ # How fast did we go? Returns a float.
297
+ def io_per_sec
298
+ total = @msgs_in + @msgs_out
299
+ (total / ((@closed_at || Time.now) - @opened_at)).to_f
300
+ end
301
+
302
+ # Call the user-supplied callback method for the message we got.
303
+ def handle(*args)
304
+ if @callbacks[args[0]]
305
+ @callbacks[args[0]].call(*args[1..-1])
306
+ elsif @handler.respond_to?(args[0])
307
+ @handler.send(*args)
308
+ elsif self.respond_to?("handle_#{args[0]}".to_sym)
309
+ self.send("handle_#{args[0]}".to_sym, *args[1..-1])
310
+ end
311
+ end
312
+
313
+ # Returns a nice string showing something like "192.168.1.1:32122"
314
+ def remote_endpoint
315
+ if !@peer && (peername = get_peername)
316
+ @peer = Socket.unpack_sockaddr_in(peername)
317
+ end
318
+ @peer ? "#{@peer[1]}:#{@peer[0]}" : '?:?'
319
+ end
320
+
321
+ end # Connectable
322
+ end # Memcache
323
+ end # module Protocols
324
+ end # module EventMachine