eventmachine-mkroman 1.3.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +179 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +110 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +520 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +52 -0
  35. data/ext/cmain.cpp +1046 -0
  36. data/ext/ed.cpp +2243 -0
  37. data/ext/ed.h +463 -0
  38. data/ext/em.cpp +2378 -0
  39. data/ext/em.h +266 -0
  40. data/ext/eventmachine.h +152 -0
  41. data/ext/extconf.rb +291 -0
  42. data/ext/fastfilereader/extconf.rb +120 -0
  43. data/ext/fastfilereader/mapper.cpp +214 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +126 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +174 -0
  51. data/ext/rubymain.cpp +1643 -0
  52. data/ext/ssl.cpp +701 -0
  53. data/ext/ssl.h +103 -0
  54. data/ext/wait_for_single_fd.h +36 -0
  55. data/java/.classpath +8 -0
  56. data/java/.project +17 -0
  57. data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
  58. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  59. data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
  60. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  61. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  62. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  63. data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
  64. data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
  65. data/lib/em/buftok.rb +59 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +69 -0
  68. data/lib/em/completion.rb +307 -0
  69. data/lib/em/connection.rb +802 -0
  70. data/lib/em/deferrable/pool.rb +2 -0
  71. data/lib/em/deferrable.rb +210 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/io_streamer.rb +68 -0
  75. data/lib/em/iterator.rb +252 -0
  76. data/lib/em/messages.rb +66 -0
  77. data/lib/em/pool.rb +151 -0
  78. data/lib/em/process_watch.rb +45 -0
  79. data/lib/em/processes.rb +123 -0
  80. data/lib/em/protocols/header_and_content.rb +138 -0
  81. data/lib/em/protocols/httpclient.rb +303 -0
  82. data/lib/em/protocols/httpclient2.rb +602 -0
  83. data/lib/em/protocols/line_and_text.rb +125 -0
  84. data/lib/em/protocols/line_protocol.rb +33 -0
  85. data/lib/em/protocols/linetext2.rb +179 -0
  86. data/lib/em/protocols/memcache.rb +331 -0
  87. data/lib/em/protocols/object_protocol.rb +46 -0
  88. data/lib/em/protocols/postgres3.rb +246 -0
  89. data/lib/em/protocols/saslauth.rb +175 -0
  90. data/lib/em/protocols/smtpclient.rb +394 -0
  91. data/lib/em/protocols/smtpserver.rb +666 -0
  92. data/lib/em/protocols/socks4.rb +66 -0
  93. data/lib/em/protocols/stomp.rb +205 -0
  94. data/lib/em/protocols/tcptest.rb +54 -0
  95. data/lib/em/protocols.rb +37 -0
  96. data/lib/em/pure_ruby.rb +1300 -0
  97. data/lib/em/queue.rb +80 -0
  98. data/lib/em/resolver.rb +232 -0
  99. data/lib/em/spawnable.rb +84 -0
  100. data/lib/em/streamer.rb +118 -0
  101. data/lib/em/threaded_resource.rb +90 -0
  102. data/lib/em/tick_loop.rb +85 -0
  103. data/lib/em/timers.rb +61 -0
  104. data/lib/em/version.rb +3 -0
  105. data/lib/eventmachine.rb +1602 -0
  106. data/lib/jeventmachine.rb +319 -0
  107. data/rakelib/package.rake +120 -0
  108. data/rakelib/test.rake +6 -0
  109. data/rakelib/test_pure.rake +11 -0
  110. data/tests/client.crt +31 -0
  111. data/tests/client.key +51 -0
  112. data/tests/dhparam.pem +13 -0
  113. data/tests/em_ssl_handlers.rb +165 -0
  114. data/tests/em_test_helper.rb +198 -0
  115. data/tests/encoded_client.key +54 -0
  116. data/tests/jruby/test_jeventmachine.rb +38 -0
  117. data/tests/test_attach.rb +199 -0
  118. data/tests/test_basic.rb +321 -0
  119. data/tests/test_channel.rb +75 -0
  120. data/tests/test_completion.rb +178 -0
  121. data/tests/test_connection_count.rb +83 -0
  122. data/tests/test_connection_write.rb +35 -0
  123. data/tests/test_defer.rb +35 -0
  124. data/tests/test_deferrable.rb +35 -0
  125. data/tests/test_epoll.rb +141 -0
  126. data/tests/test_error_handler.rb +38 -0
  127. data/tests/test_exc.rb +37 -0
  128. data/tests/test_file_watch.rb +86 -0
  129. data/tests/test_fork.rb +75 -0
  130. data/tests/test_futures.rb +170 -0
  131. data/tests/test_handler_check.rb +35 -0
  132. data/tests/test_hc.rb +155 -0
  133. data/tests/test_httpclient.rb +238 -0
  134. data/tests/test_httpclient2.rb +132 -0
  135. data/tests/test_idle_connection.rb +31 -0
  136. data/tests/test_inactivity_timeout.rb +102 -0
  137. data/tests/test_io_streamer.rb +48 -0
  138. data/tests/test_ipv4.rb +96 -0
  139. data/tests/test_ipv6.rb +107 -0
  140. data/tests/test_iterator.rb +122 -0
  141. data/tests/test_kb.rb +28 -0
  142. data/tests/test_keepalive.rb +113 -0
  143. data/tests/test_line_protocol.rb +33 -0
  144. data/tests/test_ltp.rb +155 -0
  145. data/tests/test_ltp2.rb +332 -0
  146. data/tests/test_many_fds.rb +21 -0
  147. data/tests/test_next_tick.rb +104 -0
  148. data/tests/test_object_protocol.rb +36 -0
  149. data/tests/test_pause.rb +109 -0
  150. data/tests/test_pending_connect_timeout.rb +52 -0
  151. data/tests/test_pool.rb +196 -0
  152. data/tests/test_process_watch.rb +50 -0
  153. data/tests/test_processes.rb +147 -0
  154. data/tests/test_proxy_connection.rb +180 -0
  155. data/tests/test_pure.rb +156 -0
  156. data/tests/test_queue.rb +64 -0
  157. data/tests/test_resolver.rb +129 -0
  158. data/tests/test_running.rb +14 -0
  159. data/tests/test_sasl.rb +46 -0
  160. data/tests/test_send_file.rb +217 -0
  161. data/tests/test_servers.rb +32 -0
  162. data/tests/test_shutdown_hooks.rb +23 -0
  163. data/tests/test_smtpclient.rb +75 -0
  164. data/tests/test_smtpserver.rb +90 -0
  165. data/tests/test_sock_opt.rb +53 -0
  166. data/tests/test_spawn.rb +290 -0
  167. data/tests/test_ssl_args.rb +70 -0
  168. data/tests/test_ssl_dhparam.rb +57 -0
  169. data/tests/test_ssl_ecdh_curve.rb +57 -0
  170. data/tests/test_ssl_extensions.rb +24 -0
  171. data/tests/test_ssl_inline_cert.rb +222 -0
  172. data/tests/test_ssl_methods.rb +31 -0
  173. data/tests/test_ssl_protocols.rb +190 -0
  174. data/tests/test_ssl_verify.rb +108 -0
  175. data/tests/test_stomp.rb +38 -0
  176. data/tests/test_system.rb +46 -0
  177. data/tests/test_threaded_resource.rb +68 -0
  178. data/tests/test_tick_loop.rb +58 -0
  179. data/tests/test_timers.rb +150 -0
  180. data/tests/test_ud.rb +8 -0
  181. data/tests/test_unbind_reason.rb +40 -0
  182. metadata +389 -0
@@ -0,0 +1,520 @@
1
+ # @title Getting Started with Ruby EventMachine
2
+ # @markup markdown
3
+ # @author Michael S. Klishin, Dan Sinclair
4
+
5
+ # Getting started with Ruby EventMachine #
6
+
7
+
8
+ ## About this guide ##
9
+
10
+ This guide is a quick tutorial that helps you to get started with EventMachine for writing event-driven
11
+ servers, clients and using it as a lightweight concurrency library.
12
+ It should take about 20 minutes to read and study the provided code examples. This guide covers
13
+
14
+ * Installing EventMachine via [Rubygems](http://rubygems.org) and [Bundler](http://gembundler.com).
15
+ * Building an Echo server, the "Hello, world"-like code example of network servers.
16
+ * Building a simple chat, both server and client.
17
+ * Building a very small asynchronous Websockets client.
18
+
19
+
20
+ ## Covered versions ##
21
+
22
+ This guide covers EventMachine v0.12.10 and 1.0 (including betas).
23
+
24
+
25
+ ## Level ##
26
+
27
+ This guide assumes you are comfortable (but not necessary a guru) with the command line. On Microsoft Windows™,
28
+ we recommend you to use [JRuby](http://jruby.org) when running these examples.
29
+
30
+
31
+ ## Installing EventMachine ##
32
+
33
+ ### Make sure you have Ruby installed ###
34
+
35
+ This guide assumes you have one of the supported Ruby implementations installed:
36
+
37
+ * Ruby 1.9.2
38
+ * [JRuby](http://jruby.org) (we recommend 1.6)
39
+ * [Rubinius](http://rubini.us) 1.2 or higher
40
+ * [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com)
41
+
42
+ EventMachine works on Microsoft Windows™.
43
+
44
+
45
+ ### With Rubygems ###
46
+
47
+ To install the EventMachine gem do
48
+
49
+ gem install eventmachine
50
+
51
+
52
+ ### With Bundler ###
53
+
54
+ gem "eventmachine"
55
+
56
+
57
+ ### Verifying your installation ###
58
+
59
+ Let's verify your installation with this quick IRB session:
60
+
61
+ irb -rubygems
62
+
63
+ ruby-1.9.2-p180 :001 > require "eventmachine"
64
+ => true
65
+ ruby-1.9.2-p180 :002 > EventMachine::VERSION
66
+ => "1.0.0.beta.3"
67
+
68
+
69
+ ## An Echo Server Example ##
70
+
71
+ Let's begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the
72
+ same data that was provided. First, here's the code:
73
+
74
+ {include:file:examples/guides/getting\_started/01\_eventmachine\_echo_server.rb}
75
+
76
+
77
+ When run, the server binds to port 10000. We can connect using Telnet and verify it's working:
78
+
79
+ telnet localhost 10000
80
+
81
+ On my machine the output looks like:
82
+
83
+ ~ telnet localhost 10000
84
+ Trying 127.0.0.1...
85
+ Connected to localhost.
86
+ Escape character is '^]'.
87
+
88
+ Let's send something to our server. Type in "Hello, EventMachine" and hit Enter. The server will respond with
89
+ the same string:
90
+
91
+ ~ telnet localhost 10000
92
+ Trying 127.0.0.1...
93
+ Connected to localhost.
94
+ Escape character is '^]'.
95
+ Hello, EventMachine
96
+ # (here we hit Enter)
97
+ Hello, EventMachine
98
+ # (this ^^^ is our echo server reply)
99
+
100
+ It works! Congratulations, you now can tell your Node.js-loving friends that you "have done some event-driven programming, too".
101
+ Oh, and to stop Telnet, hit Control + Shift + ] and then Control + C.
102
+
103
+ Let's walk this example line by line and see what's going on. These lines
104
+
105
+ require 'rubygems' # or use Bundler.setup
106
+ require 'eventmachine'
107
+
108
+ probably look familiar: you use [RubyGems](http://rubygems.org) (or [Bundler](http://gembundler.com/)) for dependencies and then require EventMachine gem. Boring.
109
+
110
+ Next:
111
+
112
+ class EchoServer < EventMachine::Connection
113
+ def receive_data(data)
114
+ send_data(data)
115
+ end
116
+ end
117
+
118
+ are the implementation of our echo server. We define a class that inherits from {EventMachine::Connection}
119
+ and a handler (aka callback) for one event: when we receive data from a client.
120
+
121
+ EventMachine handles the connection setup, receiving data and passing it to our handler, {EventMachine::Connection#receive_data}.
122
+
123
+ Then we implement our protocol logic, which in the case of Echo is pretty trivial: we send back whatever we receive.
124
+ To do so, we're using {EventMachine::Connection#send_data}.
125
+
126
+ Let's modify the example to recognize `exit` command:
127
+
128
+ {include:file:examples/guides/getting\_started/02\_eventmachine\_echo_server\_that\_recognizes\_exit\_command.rb}
129
+
130
+ Our `receive\_data` changed slightly and now looks like this:
131
+
132
+ def receive_data(data)
133
+ if data.strip =~ /exit$/i
134
+ EventMachine.stop_event_loop
135
+ else
136
+ send_data(data)
137
+ end
138
+ end
139
+
140
+ Because incoming data has trailing newline character, we strip it off before matching it against a simple regular
141
+ expression. If the data ends in `exit`, we stop EventMachine event loop with {EventMachine.stop_event_loop}. This unblocks
142
+ main thread and it finishes execution, and our little program exits as the result.
143
+
144
+ To summarize this first example:
145
+
146
+ * Subclass {EventMachine::Connection} and override {EventMachine::Connection#receive_data} to handle incoming data.
147
+ * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}.
148
+ * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop})
149
+
150
+ Let's move on to a slightly more sophisticated example that will introduce several more features and methods
151
+ EventMachine has to offer.
152
+
153
+
154
+ ## A Simple Chat Server Example ##
155
+
156
+ Next we will write a simple chat. Initially clients will still use telnet to connect, but then we will add little
157
+ client application that will serve as a proxy between telnet and the chat server. This example is certainly longer
158
+ (~ 150 lines with whitespace and comments) so instead of looking at the final version and going through it line by line,
159
+ we will instead begin with a very simple version that only keeps track of connected clients and then add features
160
+ as we go.
161
+
162
+ To set some expectations about our example:
163
+
164
+ * It will keep track of connected clients
165
+ * It will support a couple of commands, à la IRC
166
+ * It will support direct messages using Twitter-like @usernames
167
+ * It won't use MongoDB, fibers or distributed map/reduce for anything but will be totally [Web Scale™](http://bit.ly/webscaletm) nonetheless. Maybe even [ROFLscale](http://bit.ly/roflscalevideo).
168
+
169
+ ### Step one: detecting connections and disconnectons ###
170
+
171
+ First step looks like this:
172
+
173
+ {include:file:examples/guides/getting\_started/04\_simple\_chat\_server\_step\_one.rb}
174
+
175
+ We see familiar {EventMachine.run} and {EventMachine.start_server}, but also {EventMachine::Connection#post_init} and {EventMachine::Connection#unbind} we haven't
176
+ met yet. We don't use them in this code, so when are they run? Like {EventMachine::Connection#receive_data}, these methods are callbacks. EventMachine calls them
177
+ when certain events happen:
178
+
179
+ * {EventMachine#post_init} is called by the event loop immediately after the network connection has been established.
180
+ In the chat server example case, this is when a new client connects.
181
+ * {EventMachine#unbind} is called when client disconnects, connection is closed or is lost (because of a network issue, for example).
182
+
183
+ All our chat server does so far is logging connections or disconnections. What we want it to do next is to keep track of connected clients.
184
+
185
+
186
+ ### Step two: keep track of connected clients ###
187
+
188
+ Next iteration of the code looks like this:
189
+
190
+ {include:file:examples/guides/getting\_started/05\_simple\_chat\_server\_step\_two.rb}
191
+
192
+ While the code we added is very straightforward, we have to clarify one this first: subclasses of {EventMachine::Connection} are instantiated by
193
+ EventMachine for every new connected peer. So for 10 connected chat clients, there will be 10 separate `SimpleChatServer` instances in our
194
+ server process. Like any other objects, they can be stored in a collection, can provide public API other objects use, can instantiate or inject
195
+ dependencies and in general live a happy life all Ruby objects live until garbage collection happens.
196
+
197
+ In the example above we use a @@class_variable to keep track of connected clients. In Ruby, @@class variables are accessible from instance
198
+ methods so we can add new connections to the list from `SimpleChatServer#post_init` and remove them in `SimpleChatServer#unbind`. We can also
199
+ filter connections by some criteria, as `SimpleChatServer#other_peers demonstrates`.
200
+
201
+ So, we keep track of connections but how do we identify them? For a chat app, it's pretty common to use usernames for that. Let's ask our clients
202
+ to enter usernames when they connect.
203
+
204
+
205
+ ### Step three: adding usernames ##
206
+
207
+ To add usernames, we need to add a few things:
208
+
209
+ * We need to invite newly connected clients to enter their username.
210
+ * A reader (getter) method on our {EventMachine::Connection} subclass.
211
+ * An idea of connection state (keeping track of whether a particular participant had entered username before).
212
+
213
+ Here is one way to do it:
214
+
215
+ {include:file:examples/guides/getting\_started/06\_simple\_chat\_server\_step\_three.rb}
216
+
217
+ This is quite an update so let's take a look at each method individually. First, `SimpleChatServer#post_init`:
218
+
219
+ def post_init
220
+ @username = nil
221
+ puts "A client has connected..."
222
+ ask_username
223
+ end
224
+
225
+ To keep track of username we ask chat participants for, we add @username instance variable to our connection class. Connection
226
+ instances are just Ruby objects associated with a particular connected peer, so using @ivars is very natural. To make username
227
+ value accessible to other objects, we added a reader method that was not shown on the snippet above.
228
+
229
+ Let's dig into `SimpleChatServer#ask_username`:
230
+
231
+ def ask_username
232
+ self.send_line("[info] Enter your username:")
233
+ end # ask_username
234
+
235
+ # ...
236
+
237
+ def send_line(line)
238
+ self.send_data("#{line}\n")
239
+ end # send_line(line)
240
+
241
+ Nothing new here, we are using {EventMachine::Connection#send_data} which we have seen before.
242
+
243
+
244
+ In `SimpleChatServer#receive_data` we now have to check if the username was entered or we need
245
+ to ask for it:
246
+
247
+ def receive_data(data)
248
+ if entered_username?
249
+ handle_chat_message(data.strip)
250
+ else
251
+ handle_username(data.strip)
252
+ end
253
+ end
254
+
255
+ # ...
256
+
257
+ def entered_username?
258
+ !@username.nil? && !@username.empty?
259
+ end # entered_username?
260
+
261
+ Finally, handler of chat messages is not yet implemented:
262
+
263
+ def handle_chat_message(msg)
264
+ raise NotImplementedError
265
+ end
266
+
267
+ Let's try this example out using Telnet:
268
+
269
+ ~ telnet localhost 10000
270
+ Trying 127.0.0.1...
271
+ Connected to localhost.
272
+ Escape character is '^]'.
273
+ [info] Enter your username:
274
+ antares_
275
+ [info] Ohai, antares_
276
+
277
+ and the server output:
278
+
279
+ A client has connected...
280
+ antares_ has joined
281
+
282
+ This version requires you to remember how to terminate your Telnet session (Ctrl + Shift + ], then Ctrl + C).
283
+ It is annoying, so why don't we add the same `exit` command to our chat server?
284
+
285
+
286
+ ### Step four: adding exit command and delivering chat messages ####
287
+
288
+ {include:file:examples/guides/getting\_started/07\_simple\_chat\_server\_step\_four.rb}
289
+
290
+ TBD
291
+
292
+ Let's test-drive this version. Client A:
293
+
294
+ ~ telnet localhost 10000
295
+ Trying 127.0.0.1...
296
+ Connected to localhost.
297
+ Escape character is '^]'.
298
+ [info] Enter your username:
299
+ michael
300
+ [info] Ohai, michael
301
+ Hi everyone
302
+ michael: Hi everyone
303
+ joe has joined the room
304
+ # here ^^^ client B connects, lets greet him
305
+ hi joe
306
+ michael: hi joe
307
+ joe: hey michael
308
+ # ^^^ client B replies
309
+ exit
310
+ # ^^^ out command in action
311
+ Connection closed by foreign host.
312
+
313
+ Client B:
314
+
315
+ ~ telnet localhost 10000
316
+ Trying 127.0.0.1...
317
+ Connected to localhost.
318
+ Escape character is '^]'.
319
+ [info] Enter your username:
320
+ joe
321
+ [info] Ohai, joe
322
+ michael: hi joe
323
+ # ^^^ client A greets us, lets reply
324
+ hey michael
325
+ joe: hey michael
326
+ exit
327
+ # ^^^ out command in action
328
+ Connection closed by foreign host.
329
+
330
+ And finally, the server output:
331
+
332
+ A client has connected...
333
+ michael has joined
334
+ A client has connected...
335
+ _antares has joined
336
+ [info] _antares has left
337
+ [info] michael has left
338
+
339
+ Our little char server now supports usernames, sending messages and the `exit` command. Next up, private (aka direct) messages.
340
+
341
+
342
+ ### Step five: adding direct messages and one more command ###
343
+
344
+ To add direct messages, we come up with a simple convention: private messages begin with @username and may have optional colon before
345
+ message text, like this:
346
+
347
+ @joe: hey, how do you like eventmachine?
348
+
349
+ This convention makes parsing of messages simple so that we can concentrate on delivering them to a particular client connection.
350
+ Remember when we added `username` reader on our connection class? That tiny change makes this step possible: when a new direct
351
+ message comes in, we extract username and message text and then find then connection for @username in question:
352
+
353
+ #
354
+ # Message handling
355
+ #
356
+
357
+ def handle_chat_message(msg)
358
+ if command?(msg)
359
+ self.handle_command(msg)
360
+ else
361
+ if direct_message?(msg)
362
+ self.handle_direct_message(msg)
363
+ else
364
+ self.announce(msg, "#{@username}:")
365
+ end
366
+ end
367
+ end # handle_chat_message(msg)
368
+
369
+ def direct_message?(input)
370
+ input =~ DM_REGEXP
371
+ end # direct_message?(input)
372
+
373
+ def handle_direct_message(input)
374
+ username, message = parse_direct_message(input)
375
+
376
+ if connection = @@connected_clients.find { |c| c.username == username }
377
+ puts "[dm] @#{@username} => @#{username}"
378
+ connection.send_line("[dm] @#{@username}: #{message}")
379
+ else
380
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
381
+ end
382
+ end # handle_direct_message(input)
383
+
384
+ def parse_direct_message(input)
385
+ return [$1, $2] if input =~ DM_REGEXP
386
+ end # parse_direct_message(input)
387
+
388
+ This snippet demonstrates how one connection instance can obtain another connection instance and send data to it.
389
+ This is a very powerful feature, consider just a few use cases:
390
+
391
+ * Peer-to-peer protocols
392
+ * Content-aware routing
393
+ * Efficient streaming with optional filtering
394
+
395
+ Less common use cases include extending C++ core of EventMachine to provide access to hardware that streams events that
396
+ can be re-broadcasted to any interested parties connected via TCP, UDP or something like AMQP or WebSockets. With this,
397
+ sky is the limit. Actually, EventMachine has several features for efficient proxying data between connections.
398
+ We will not cover them in this guide.
399
+
400
+ One last feature that we are going to add to our chat server is the `status` command that tells you current server time and how many people
401
+ are there in the chat room:
402
+
403
+ #
404
+ # Commands handling
405
+ #
406
+
407
+ def command?(input)
408
+ input =~ /(exit|status)$/i
409
+ end # command?(input)
410
+
411
+ def handle_command(cmd)
412
+ case cmd
413
+ when /exit$/i then self.close_connection
414
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
415
+ end
416
+ end # handle_command(cmd)
417
+
418
+ Hopefully this piece of code is easy to follow. Try adding a few more commands, for example, the `whoishere` command that lists people
419
+ currently in the chat room.
420
+
421
+ In the end, our chat server looks like this:
422
+
423
+ {include:file:examples/guides/getting\_started/08\_simple\_chat\_server\_step\_five.rb}
424
+
425
+ We are almost done with the server but there are some closing thoughts.
426
+
427
+
428
+ ### Step six: final version ###
429
+
430
+ Just in case, here is the final version of the chat server code we have built:
431
+
432
+ {include:file:examples/guides/getting\_started/03\_simple\_chat\_server.rb}
433
+
434
+
435
+ ### Step seven: future directions and some closing thoughts ###
436
+
437
+ The chat server is just about 150 lines of Ruby including empty lines and comments, yet it has a few features most of chat server
438
+ examples never add. We did not, however, implement many other features that popular IRC clients like [Colloquy](http://colloquy.info) have:
439
+
440
+ * Chat moderation
441
+ * Multiple rooms
442
+ * Connection timeout detection
443
+
444
+ How would one go about implementing them? We thought it is worth discussing what else EventMachine has to offer and what ecosystem projects
445
+ one can use to build a really feature-rich Web-based IRC chat client.
446
+
447
+ With multiple rooms it's more or less straightforward, just add one more hash and a bunch of commands and use the information about which rooms participant
448
+ is in when you are delivering messages. There is nothing in EventMachine itself that can make the job much easier for developer.
449
+
450
+ To implement chat moderation feature you may want to do a few things:
451
+
452
+ * Work with client IP addresses. Maybe we want to consider everyone who connects from certain IPs a moderator.
453
+ * Access persistent data about usernames of moderators and their credentials.
454
+
455
+ Does EventMachine have anything to offer here? It does. To obtain peer IP address, take a look at {EventMachine::Connection#get_peername}. The name of this method is
456
+ a little bit misleading and originates from low-level socket programming APIs.
457
+
458
+ #### A whirlwind tour of the EventMachine ecosystem ####
459
+
460
+ To work with data stores you can use several database drivers that ship with EventMachine itself, however, quite often there are some 3rd party projects in
461
+ the EventMachine ecosystem that have more features, are faster or just better maintained. So we figured it will be helpful to provide a few pointers
462
+ to some of those projects:
463
+
464
+ * For MySQL, check out [em-mysql](https://github.com/eventmachine/em-mysql) project.
465
+ * For PostgreSQL, have a look at Mike Perham's [EventMachine-based PostgreSQL driver](https://github.com/mperham/em_postgresql).
466
+ * For Redis, there is a young but already popular [em-hiredis](https://github.com/mloughran/em-hiredis) library that combines EventMachine's non-blocking I/O with
467
+ extreme performance of the official Redis C client, [hiredis](https://github.com/antirez/hiredis).
468
+ * For MongoDB, see [em-mongo](https://github.com/bcg/em-mongo)
469
+ * For Cassandra, Mike Perham [added transport agnosticism feature](http://www.mikeperham.com/2010/02/09/cassandra-and-eventmachine/) to the [cassandra gem](https://rubygems.org/gems/cassandra).
470
+
471
+ [Riak](http://www.basho.com/products_riak_overview.php) and CouchDB talk HTTP so it's possible to use [em-http-request](https://github.com/igrigorik/em-http-request).
472
+ If you are aware of EventMachine-based non-blocking drivers for these databases, as well as for HBase, let us know on the [EventMachine mailing list](http://groups.google.com/group/eventmachine).
473
+ Also, EventMachine supports TLS (aka SSL) and works well on [JRuby](http://jruby.org) and Windows.
474
+
475
+ Learn more in our {file:docs/Ecosystem.md EventMachine ecosystem} and {file:docs/TLS.md TLS (aka SSL)} guides.
476
+
477
+
478
+ #### Connection loss detection ####
479
+
480
+ Finally, connection loss detection. When our chat participant closes her laptop lid, how do we know that she is no longer active? The answer is, when EventMachine
481
+ detects TCP connectin closure, it calls {EventMachine::Connection#unbind}. Version 1.0.beta3 and later also pass an optional argument to that method. The argument
482
+ indicates what error (if any) caused the connection to be closed.
483
+
484
+ Learn more in our {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery} guide.
485
+
486
+
487
+ #### What the Chat Server Example doesn't demonstrate ####
488
+
489
+ This chat server also leaves out something production quality clients and servers must take care of: buffering. We intentionally did not include any buffering in
490
+ our chat server example: it would only distract you from learning what you really came here to learn: how to use EventMachine to build blazing fast asynchronous
491
+ networking programs quickly. However, {EventMachine::Connection#receive_data} does not offer any guarantees that you will be receiving "whole messages" all the time,
492
+ largely because the underlying transport (UDP or TCP) does not offer such guarantees. Many protocols, for example, AMQP, mandate that large content chunks are
493
+ split into smaller _frames_ of certain size. This means that [amq-client](https://github.com/ruby-amqp/amq-client) library, for instance, that has EventMachine-based driver,
494
+ has to deal with figuring out when exactly we received "the whole message". To do so, it uses buffering and employs various checks to detect _frame boundaries_.
495
+ So **don't be deceived by the simplicity of this chat example**: it intentionally leaves framing out, but real world protocols usually require it.
496
+
497
+
498
+
499
+ ## A (Proxying) Chat Client Example ##
500
+
501
+ TBD
502
+
503
+
504
+ ## Wrapping up ##
505
+
506
+ This tutorial ends here. Congratulations! You have learned quite a bit about EventMachine.
507
+
508
+
509
+ ## What to read next ##
510
+
511
+ The documentation is organized as a {file:docs/DocumentationGuidesIndex.md number of guides}, covering all kinds of
512
+ topics. TBD
513
+
514
+
515
+ ## Tell us what you think! ##
516
+
517
+ Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
518
+ or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
519
+ Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
520
+ key to making documentation better.