evented-memcache-client 1.0.0

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.
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