eventmachine 1.0.0.beta.3 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/.gitignore +5 -0
  2. data/.yardopts +5 -1
  3. data/{docs/GNU → GNU} +0 -0
  4. data/Gemfile +1 -0
  5. data/{docs/COPYING → LICENSE} +0 -0
  6. data/README.md +109 -0
  7. data/Rakefile +8 -0
  8. data/docs/DocumentationGuidesIndex.md +27 -0
  9. data/docs/GettingStarted.md +521 -0
  10. data/docs/{ChangeLog → old/ChangeLog} +0 -0
  11. data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
  12. data/docs/{EPOLL → old/EPOLL} +0 -0
  13. data/docs/{INSTALL → old/INSTALL} +0 -0
  14. data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
  15. data/docs/{LEGAL → old/LEGAL} +0 -0
  16. data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
  17. data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
  18. data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
  19. data/docs/{SMTP → old/SMTP} +0 -0
  20. data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
  21. data/docs/{TODO → old/TODO} +0 -0
  22. data/eventmachine.gemspec +4 -1
  23. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  24. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  25. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  26. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  27. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  28. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  29. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  30. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  31. data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
  32. data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
  33. data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
  34. data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
  35. data/examples/{helper.rb → old/helper.rb} +0 -0
  36. data/ext/cmain.cpp +3 -3
  37. data/ext/ed.cpp +90 -15
  38. data/ext/ed.h +5 -5
  39. data/ext/em.cpp +47 -55
  40. data/ext/em.h +12 -2
  41. data/ext/pipe.cpp +2 -2
  42. data/ext/project.h +1 -1
  43. data/ext/rubymain.cpp +48 -3
  44. data/ext/ssl.cpp +5 -0
  45. data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
  46. data/lib/em/buftok.rb +35 -63
  47. data/lib/em/callback.rb +43 -11
  48. data/lib/em/channel.rb +21 -14
  49. data/lib/em/completion.rb +304 -0
  50. data/lib/em/connection.rb +339 -209
  51. data/lib/em/deferrable.rb +4 -0
  52. data/lib/em/deferrable/pool.rb +2 -0
  53. data/lib/em/file_watch.rb +37 -18
  54. data/lib/em/iterator.rb +42 -42
  55. data/lib/em/pool.rb +146 -0
  56. data/lib/em/process_watch.rb +5 -4
  57. data/lib/em/processes.rb +8 -4
  58. data/lib/em/protocols/httpclient.rb +22 -11
  59. data/lib/em/protocols/httpclient2.rb +15 -5
  60. data/lib/em/protocols/line_protocol.rb +2 -1
  61. data/lib/em/protocols/memcache.rb +17 -9
  62. data/lib/em/protocols/object_protocol.rb +2 -1
  63. data/lib/em/protocols/postgres3.rb +8 -9
  64. data/lib/em/protocols/smtpclient.rb +19 -11
  65. data/lib/em/protocols/smtpserver.rb +1 -1
  66. data/lib/em/protocols/stomp.rb +8 -6
  67. data/lib/em/protocols/tcptest.rb +3 -2
  68. data/lib/em/pure_ruby.rb +212 -208
  69. data/lib/em/queue.rb +22 -13
  70. data/lib/em/resolver.rb +70 -64
  71. data/lib/em/spawnable.rb +6 -3
  72. data/lib/em/streamer.rb +33 -45
  73. data/lib/em/threaded_resource.rb +90 -0
  74. data/lib/em/timers.rb +6 -2
  75. data/lib/em/version.rb +1 -1
  76. data/lib/eventmachine.rb +538 -602
  77. data/lib/jeventmachine.rb +22 -1
  78. data/tasks/package.rake +12 -2
  79. data/tasks/test.rake +1 -0
  80. data/tests/em_test_helper.rb +12 -3
  81. data/tests/test_completion.rb +177 -0
  82. data/tests/test_epoll.rb +2 -2
  83. data/tests/test_httpclient.rb +9 -9
  84. data/tests/test_httpclient2.rb +11 -9
  85. data/tests/test_ltp.rb +2 -10
  86. data/tests/test_pool.rb +128 -0
  87. data/tests/test_processes.rb +20 -2
  88. data/tests/test_queue.rb +8 -0
  89. data/tests/test_resolver.rb +1 -1
  90. data/tests/test_set_sock_opt.rb +37 -0
  91. data/tests/test_shutdown_hooks.rb +23 -0
  92. data/tests/test_threaded_resource.rb +53 -0
  93. data/tests/test_unbind_reason.rb +31 -0
  94. metadata +262 -192
  95. data/README +0 -81
  96. data/tasks/doc.rake +0 -30
data/.gitignore CHANGED
@@ -14,3 +14,8 @@ Makefile
14
14
  *.dSYM
15
15
  java/src/.project
16
16
  *.rbc
17
+ Gemfile.lock
18
+
19
+ .yardoc/*
20
+ doc/*
21
+
data/.yardopts CHANGED
@@ -1,3 +1,7 @@
1
+ --no-private
2
+ --protected
3
+ --markup="markdown" lib/**/*.rb
4
+ --main README.md
1
5
  --exclude jeventmachine --exclude pure_ruby
2
6
  -
3
- docs/DEFERRABLES docs/EPOLL docs/KEYBOARD
7
+ docs/*.md
File without changes
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
1
  source :rubygems
2
2
  gemspec
3
+
File without changes
@@ -0,0 +1,109 @@
1
+ # About EventMachine #
2
+
3
+
4
+ ## What is EventMachine ##
5
+
6
+ EventMachine is an event-driven I/O and lightweight concurrency library for Ruby.
7
+ It provides event-driven I/O using the [Reactor pattern](http://en.wikipedia.org/wiki/Reactor_pattern),
8
+ much like [JBoss Netty](http://www.jboss.org/netty), [Apache MINA](http://mina.apache.org/),
9
+ Python's [Twisted](http://twistedmatrix.com), [Node.js](http://nodejs.org), libevent and libev.
10
+
11
+ EventMachine is designed to simultaneously meet two key needs:
12
+
13
+ * Extremely high scalability, performance and stability for the most demanding production environments.
14
+ * An API that eliminates the complexities of high-performance threaded network programming,
15
+ allowing engineers to concentrate on their application logic.
16
+
17
+ This unique combination makes EventMachine a premier choice for designers of critical networked
18
+ applications, including Web servers and proxies, email and IM production systems, authentication/authorization
19
+ processors, and many more.
20
+
21
+ EventMachine has been around since yearly 2000s and is a mature and battle tested library.
22
+
23
+
24
+ ## What EventMachine is good for? ##
25
+
26
+ * Scalable event-driven servers. Examples: [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/).
27
+ * Scalable asynchronous clients for various protocols, RESTful APIs and so on. Examples: [em-http-request](https://github.com/igrigorik/em-http-request) or [amqp gem](https://github.com/ruby-amqp/amqp).
28
+ * Efficient network proxies with custom logic. Examples: [Proxymachine](https://github.com/mojombo/proxymachine/).
29
+ * File and network monitoring tools. Examples: [eventmachine-tail](https://github.com/jordansissel/eventmachine-tail) and [logstash](https://github.com/logstash/logstash).
30
+
31
+
32
+
33
+ ## What platforms are supported by EventMachine? ##
34
+
35
+ EventMachine supports Ruby 1.8.7, 1.9.2, REE, JRuby and **works well on Windows** as well
36
+ as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors).
37
+
38
+
39
+
40
+ ## Install the gem ##
41
+
42
+ Install it with [RubyGems](https://rubygems.org/)
43
+
44
+ gem install eventmachine
45
+
46
+ or add this to your Gemfile if you use [Bundler](http://gembundler.com/):
47
+
48
+ gem "eventmachine"
49
+
50
+
51
+
52
+ ## Getting started ##
53
+
54
+ For an introduction to EventMachine, check out:
55
+
56
+ * [blog post about EventMachine by Ilya Grigorik](http://www.igvita.com/2008/05/27/ruby-eventmachine-the-speed-demon/).
57
+ * [EventMachine Introductions by Dan Sinclair](http://everburning.com/news/eventmachine-introductions/).
58
+
59
+
60
+ ### Server example: Echo server ###
61
+
62
+ Here's a fully-functional echo server written with EventMachine:
63
+
64
+ require 'eventmachine'
65
+
66
+ module EchoServer
67
+ def post_init
68
+ puts "-- someone connected to the echo server!"
69
+ end
70
+
71
+ def receive_data data
72
+ send_data ">>>you sent: #{data}"
73
+ close_connection if data =~ /quit/i
74
+ end
75
+
76
+ def unbind
77
+ puts "-- someone disconnected from the echo server!"
78
+ end
79
+ end
80
+
81
+ # Note that this will block current thread.
82
+ EventMachine.run {
83
+ EventMachine.start_server "127.0.0.1", 8081, EchoServer
84
+ }
85
+
86
+
87
+ ## EventMachine documentation ##
88
+
89
+ Currently we only have [reference documentation](http://eventmachine.rubyforge.org) and a [wiki](https://github.com/eventmachine/eventmachine/wiki).
90
+
91
+
92
+ ## Community and where to get help ##
93
+
94
+ * Join the [mailing list](http://groups.google.com/group/eventmachine) (Google Group)
95
+ * Join IRC channel #eventmachine on irc.freenode.net
96
+
97
+
98
+ ## License and copyright ##
99
+
100
+ EventMachine is copyrighted free software made available under the terms
101
+ of either the GPL or Ruby's License.
102
+
103
+ Copyright: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
104
+
105
+
106
+ ## Alternatives ##
107
+
108
+ If you are unhappy with EventMachine and want to use Ruby, check out [Cool.io](http://coolio.github.com/).
109
+ One caveat: by May 2011, it did not support JRuby and Windows.
data/Rakefile CHANGED
@@ -4,8 +4,16 @@ import *Dir['tasks/*.rake']
4
4
 
5
5
  GEMSPEC = eval(File.read(File.expand_path('../eventmachine.gemspec', __FILE__)))
6
6
 
7
+ require 'yard'
7
8
  require 'rake/clean'
8
9
  task :clobber => :clean
9
10
 
10
11
  desc "Build eventmachine, then run tests."
11
12
  task :default => [:compile, :test]
13
+
14
+ desc 'Generate documentation'
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = ['lib/**/*.rb', '-', 'docs/*.md']
17
+ t.options = ['--main', 'README.md', '--no-private']
18
+ t.options = ['--exclude', 'lib/jeventmachine', '--exclude', 'lib/pr_eventmachine']
19
+ end
@@ -0,0 +1,27 @@
1
+ # EventMachine documentation guides #
2
+
3
+ Welcome to the documentation guides for [EventMachine](http://github.com/eventmachine/eventmachine),
4
+ a fast and simple event-processing library for Ruby programs (à la JBoss Netty, Twisted, Node.js
5
+ and so on).
6
+
7
+ ## Guide list ##
8
+
9
+ * {file:docs/GettingStarted.md Getting started with EventMachine}
10
+ * {file:docs/EventDrivenServers.md Writing event-driven servers}
11
+ * {file:docs/EventDrivenClients.md Writing event-driven clients}
12
+ * {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery}
13
+ * {file:docs/TLS.md TLS (aka SSL)}
14
+ * {file:docs/Ecosystem.md EventMachine ecosystem}: Thin, Goliath, em-http-request, em-websockets, Proxymachine and beyond
15
+ * {file:docs/BlockingEventLoop.md On blocking the event loop: why it is harmful for performance and how to avoid it}
16
+ * {file:docs/LightweightConcurrency.md Lightweight concurrency with EventMachine}
17
+ * {file:docs/Deferrables.md Deferrables}
18
+ * {file:docs/ModernKernelInputOutputAPIs.md Brief introduction to epoll, kqueue, select}
19
+ * {file:docs/WorkingWithOtherIOSources.md Working with other IO sources such as the keyboard}
20
+
21
+
22
+ ## Tell us what you think! ##
23
+
24
+ Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
25
+ or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
26
+ Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
27
+ key to making documentation better.
@@ -0,0 +1,521 @@
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.8.7
38
+ * Ruby 1.9.2
39
+ * [JRuby](http://jruby.org) (we recommend 1.6)
40
+ * [Rubinius](http://rubini.us) 1.2 or higher
41
+ * [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com)
42
+
43
+ EventMachine works on Microsoft Windows™.
44
+
45
+
46
+ ### With Rubygems ###
47
+
48
+ To install the EventMachine gem do
49
+
50
+ gem install eventmachine
51
+
52
+
53
+ ### With Bundler ###
54
+
55
+ gem "eventmachine"
56
+
57
+
58
+ ### Verifying your installation ###
59
+
60
+ Lets verify your installation with this quick IRB session:
61
+
62
+ irb -rubygems
63
+
64
+ ruby-1.9.2-p180 :001 > require "eventmachine"
65
+ => true
66
+ ruby-1.9.2-p180 :002 > EventMachine::VERSION
67
+ => "1.0.0.beta.3"
68
+
69
+
70
+ ## An Echo Server Example ##
71
+
72
+ Lets begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the
73
+ same data that was provided. First, here's the code:
74
+
75
+ {include:file:examples/guides/getting\_started/01\_eventmachine\_echo_server.rb}
76
+
77
+
78
+ When run, the server binds to port 10000. We can connect using Telnet and verify it's working:
79
+
80
+ telnet localhost 10000
81
+
82
+ On my machine the output looks like:
83
+
84
+ ~ telnet localhost 10000
85
+ Trying 127.0.0.1...
86
+ Connected to localhost.
87
+ Escape character is '^]'.
88
+
89
+ Let's send something to our server. Type in "Hello, EventMachine" and hit Enter. The server will respond with
90
+ the same string:
91
+
92
+ ~ telnet localhost 10000
93
+ Trying 127.0.0.1...
94
+ Connected to localhost.
95
+ Escape character is '^]'.
96
+ Hello, EventMachine
97
+ # (here we hit Enter)
98
+ Hello, EventMachine
99
+ # (this ^^^ is our echo server reply)
100
+
101
+ It works! Congratulations, you now can tell your Node.js-loving friends that you "have done some event-driven programming, too".
102
+ Oh, and to stop Telnet, hit Control + Shift + ] and then Control + C.
103
+
104
+ Lets walk this example line by line and see what's going on. These lines
105
+
106
+ require 'rubygems' # or use Bundler.setup
107
+ require 'eventmachine'
108
+
109
+ probably look familiar: you use [RubyGems](http://rubygems.org) (or [Bundler](http://gembundler.com/)) for dependencies and then require EventMachine gem. Boring.
110
+
111
+ Next:
112
+
113
+ class EchoServer < EventMachine::Connection
114
+ def receive_data(data)
115
+ send_data(data)
116
+ end
117
+ end
118
+
119
+ Is the implementation of our echo server. We define a class that inherits from {EventMachine::Connection}
120
+ and a handler (aka callback) for one event: when we receive data from a client.
121
+
122
+ EventMachine handles the connection setup, receiving data and passing it to our handler, {EventMachine::Connection#receive_data}.
123
+
124
+ Then we implement our protocol logic, which in the case of Echo is pretty trivial: we send back whatever we receive.
125
+ To do so, we're using {EventMachine::Connection#send_data}.
126
+
127
+ Lets modify the example to recognize `exit` command:
128
+
129
+ {include:file:examples/guides/getting\_started/02\_eventmachine\_echo_server\_that\_recognizes\_exit\_command.rb}
130
+
131
+ Our `receive\_data` changed slightly and now looks like this:
132
+
133
+ def receive_data(data)
134
+ if data.strip =~ /exit$/i
135
+ EventMachine.stop_event_loop
136
+ else
137
+ send_data(data)
138
+ end
139
+ end
140
+
141
+ Because incoming data has trailing newline character, we strip it off before matching it against a simple regular
142
+ expression. If the data ends in `exit`, we stop EventMachine event loop with {EventMachine.stop_event_loop}. This unblocks
143
+ main thread and it finishes execution, and our little program exits as the result.
144
+
145
+ To summarize this first example:
146
+
147
+ * Subclass {EventMachine::Connection} and override {EventMachine::Connection#send_data} to handle incoming data.
148
+ * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}.
149
+ * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop})
150
+
151
+ Lets move on to a slightly more sophisticated example that will introduce several more features and methods
152
+ EventMachine has to offer.
153
+
154
+
155
+ ## A Simple Chat Server Example ##
156
+
157
+ Next we will write a simple chat. Initially clients will still use telnet to connect, but then we will add little
158
+ client application that will serve as a proxy between telnet and the chat server. This example is certainly longer
159
+ (~ 150 lines with whitespace and comments) so instead of looking at the final version and going through it line by line,
160
+ we will instead begin with a very simple version that only keeps track of connected clients and then add features
161
+ as we go.
162
+
163
+ To set some expectations about our example:
164
+
165
+ * It will keep track of connected clients
166
+ * It will support a couple of commands, à la IRC
167
+ * It will support direct messages using Twitter-like @usernames
168
+ * 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).
169
+
170
+ ### Step one: detecting connections and disconnectons ###
171
+
172
+ First step looks like this:
173
+
174
+ {include:file:examples/guides/getting\_started/04\_simple\_chat\_server\_step\_one.rb}
175
+
176
+ We see familiar {EventMachine.run} and {EventMachine.start_server}, but also {EventMachine::Connection#post_init} and {EventMachine::Connection#unbind} we haven't
177
+ 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
178
+ when certain events happen:
179
+
180
+ * {EventMachine#post_init} is called by the event loop immediately after the network connection has been established.
181
+ In the chat server example case, this is when a new client connects.
182
+ * {EventMachine#unbind} is called when client disconnects, connection is closed or is lost (because of a network issue, for example).
183
+
184
+ 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.
185
+
186
+
187
+ ### Step two: keep track of connected clients ###
188
+
189
+ Next iteration of the code looks like this:
190
+
191
+ {include:file:examples/guides/getting\_started/05\_simple\_chat\_server\_step\_two.rb}
192
+
193
+ While the code we added is very straightforward, we have to clarify one this first: subclasses of {EventMachine::Connection} are instantiated by
194
+ EventMachine for every new connected peer. So for 10 connected chat clients, there will be 10 separate `SimpleChatServer` instances in our
195
+ server process. Like any other objects, they can be stored in a collection, can provide public API other objects use, can instantiate or inject
196
+ dependencies and in general live a happy life all Ruby objects live until garbage collection happens.
197
+
198
+ In the example above we use a @@class_variable to keep track of connected clients. In Ruby, @@class variables are accessible from instance
199
+ methods so we can add new connections to the list from `SimpleChatServer#post_init` and remove them in `SimpleChatServer#unbind`. We can also
200
+ filter connections by some criteria, as `SimpleChatServer#other_peers demonstrates`.
201
+
202
+ 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. Lets ask our clients
203
+ to enter usernames when they connect.
204
+
205
+
206
+ ### Step three: adding usernames ##
207
+
208
+ To add usernames, we need to add a few things:
209
+
210
+ * We need to invite newly connected clients to enter their username.
211
+ * A reader (getter) method on our {EventMachine::Connection} subclass.
212
+ * An idea of connection state (keeping track of whether a particular participant had entered username before).
213
+
214
+ Here is one way to do it:
215
+
216
+ {include:file:examples/guides/getting\_started/06\_simple\_chat\_server\_step\_three.rb}
217
+
218
+ This is quite an update so lets take a look at each method individually. First, `SimpleChatServer#post_init`:
219
+
220
+ def post_init
221
+ @username = nil
222
+ puts "A client has connected..."
223
+ ask_username
224
+ end
225
+
226
+ To keep track of username we ask chat participants for, we add @username instance variable to our connection class. Connection
227
+ instances are just Ruby objects associated with a particular connected peer, so using @ivars is very natural. To make username
228
+ value accessible to other objects, we added a reader method that was not shown on the snippet above.
229
+
230
+ Lets dig into `SimpleChatServer#ask_username`:
231
+
232
+ def ask_username
233
+ self.send_line("[info] Enter your username:")
234
+ end # ask_username
235
+
236
+ # ...
237
+
238
+ def send_line(line)
239
+ self.send_data("#{line}\n")
240
+ end # send_line(line)
241
+
242
+ Nothing new here, we are using {EventMachine::Connection#send_data} which we have seen before.
243
+
244
+
245
+ In `SimpleChatServer#receive_data` we now have to check if the username was entered or we need
246
+ to ask for it:
247
+
248
+ def receive_data(data)
249
+ if entered_username?
250
+ handle_chat_message(data.strip)
251
+ else
252
+ handle_username(data.strip)
253
+ end
254
+ end
255
+
256
+ # ...
257
+
258
+ def entered_username?
259
+ !@username.nil? && !@username.empty?
260
+ end # entered_username?
261
+
262
+ Finally, handler of chat messages is not yet implemented:
263
+
264
+ def handle_chat_message(msg)
265
+ raise NotImplementedError
266
+ end
267
+
268
+ Lets try this example out using Telnet:
269
+
270
+ ~ telnet localhost 10000
271
+ Trying 127.0.0.1...
272
+ Connected to localhost.
273
+ Escape character is '^]'.
274
+ [info] Enter your username:
275
+ antares_
276
+ [info] Ohai, antares_
277
+
278
+ and the server output:
279
+
280
+ A client has connected...
281
+ antares_ has joined
282
+
283
+ This version requires you to remember how to terminate your Telnet session (Ctrl + Shift + ], then Ctrl + C).
284
+ It is annoying, so why don't we add the same `exit` command to our chat server?
285
+
286
+
287
+ ### Step four: adding exit command and delivering chat messages ####
288
+
289
+ {include:file:examples/guides/getting\_started/07\_simple\_chat\_server\_step\_four.rb}
290
+
291
+ TBD
292
+
293
+ Lets test-drive this version. Client A:
294
+
295
+ ~ telnet localhost 10000
296
+ Trying 127.0.0.1...
297
+ Connected to localhost.
298
+ Escape character is '^]'.
299
+ [info] Enter your username:
300
+ michael
301
+ [info] Ohai, michael
302
+ Hi everyone
303
+ michael: Hi everyone
304
+ joe has joined the room
305
+ # here ^^^ client B connects, lets greet him
306
+ hi joe
307
+ michael: hi joe
308
+ joe: hey michael
309
+ # ^^^ client B replies
310
+ exit
311
+ # ^^^ out command in action
312
+ Connection closed by foreign host.
313
+
314
+ Client B:
315
+
316
+ ~ telnet localhost 10000
317
+ Trying 127.0.0.1...
318
+ Connected to localhost.
319
+ Escape character is '^]'.
320
+ [info] Enter your username:
321
+ joe
322
+ [info] Ohai, joe
323
+ michael: hi joe
324
+ # ^^^ client A greets us, lets reply
325
+ hey michael
326
+ joe: hey michael
327
+ exit
328
+ # ^^^ out command in action
329
+ Connection closed by foreign host.
330
+
331
+ And finally, the server output:
332
+
333
+ A client has connected...
334
+ michael has joined
335
+ A client has connected...
336
+ _antares has joined
337
+ [info] _antares has left
338
+ [info] michael has left
339
+
340
+ Our little char server now supports usernames, sending messages and the `exit` command. Next up, private (aka direct) messages.
341
+
342
+
343
+ ### Step five: adding direct messages and one more command ###
344
+
345
+ To add direct messages, we come up with a simple convention: private messages begin with @username and may have optional colon before
346
+ message text, like this:
347
+
348
+ @joe: hey, how do you like eventmachine?
349
+
350
+ This convention makes parsing of messages simple so that we can concentrate on delivering them to a particular client connection.
351
+ Remember when we added `username` reader on our connection class? That tiny change makes this step possible: when a new direct
352
+ message comes in, we extract username and message text and then find then connection for @username in question:
353
+
354
+ #
355
+ # Message handling
356
+ #
357
+
358
+ def handle_chat_message(msg)
359
+ if command?(msg)
360
+ self.handle_command(msg)
361
+ else
362
+ if direct_message?(msg)
363
+ self.handle_direct_message(msg)
364
+ else
365
+ self.announce(msg, "#{@username}:")
366
+ end
367
+ end
368
+ end # handle_chat_message(msg)
369
+
370
+ def direct_message?(input)
371
+ input =~ DM_REGEXP
372
+ end # direct_message?(input)
373
+
374
+ def handle_direct_message(input)
375
+ username, message = parse_direct_message(input)
376
+
377
+ if connection = @@connected_clients.find { |c| c.username == username }
378
+ puts "[dm] @#{@username} => @#{username}"
379
+ connection.send_line("[dm] @#{@username}: #{message}")
380
+ else
381
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
382
+ end
383
+ end # handle_direct_message(input)
384
+
385
+ def parse_direct_message(input)
386
+ return [$1, $2] if input =~ DM_REGEXP
387
+ end # parse_direct_message(input)
388
+
389
+ This snippet demonstrates how one connection instance can obtain another connection instance and send data to it.
390
+ This is a very powerful feature, consider just a few use cases:
391
+
392
+ * Peer-to-peer protocols
393
+ * Content-aware routing
394
+ * Efficient streaming with optional filtering
395
+
396
+ Less common use cases include extending C++ core of EventMachine to provide access to hardware that streams events that
397
+ can be re-broadcasted to any interested parties connected via TCP, UDP or something like AMQP or WebSockets. With this,
398
+ sky is the limit. Actually, EventMachine has several features for efficient proxying data between connections.
399
+ We will not cover them in this guide.
400
+
401
+ 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
402
+ are there in the chat room:
403
+
404
+ #
405
+ # Commands handling
406
+ #
407
+
408
+ def command?(input)
409
+ input =~ /(exit|status)$/i
410
+ end # command?(input)
411
+
412
+ def handle_command(cmd)
413
+ case cmd
414
+ when /exit$/i then self.close_connection
415
+ 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")
416
+ end
417
+ end # handle_command(cmd)
418
+
419
+ Hopefully this piece of code is easy to follow. Try adding a few more commands, for example, the `whoishere` command that lists people
420
+ currently in the chat room.
421
+
422
+ In the end, our chat server looks like this:
423
+
424
+ {include:file:examples/guides/getting\_started/08\_simple\_chat\_server\_step\_five.rb}
425
+
426
+ We are almost done with the server but there are some closing thoughts.
427
+
428
+
429
+ ### Step six: final version ###
430
+
431
+ Just in case, here is the final version of the chat server code we have built:
432
+
433
+ {include:file:examples/guides/getting\_started/03\_simple\_chat\_server.rb}
434
+
435
+
436
+ ### Step seven: future directions and some closing thoughts ###
437
+
438
+ 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
439
+ examples never add. We did not, however, implement many other features that popular IRC clients like [Colloquy](http://colloquy.info) have:
440
+
441
+ * Chat moderation
442
+ * Multiple rooms
443
+ * Connection timeout detection
444
+
445
+ How would one go about implementing them? We thought it is worth discussing what else EventMachine has to offer and what ecosystem projects
446
+ one can use to build a really feature-rich Web-based IRC chat client.
447
+
448
+ 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
449
+ is in when you are delivering messages. There is nothing in EventMachine itself that can make the job much easier for developer.
450
+
451
+ To implement chat moderation feature you may want to do a few things:
452
+
453
+ * Work with client IP addresses. Maybe we want to consider everyone who connects from certain IPs a moderator.
454
+ * Access persistent data about usernames of moderators and their credentials.
455
+
456
+ 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
457
+ a little bit misleading and originates from low-level socket programming APIs.
458
+
459
+ #### A whirlwind tour of the EventMachine ecosystem ####
460
+
461
+ 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
462
+ 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
463
+ to some of those projects:
464
+
465
+ * For MySQL, check out [em-mysql](https://github.com/eventmachine/em-mysql) project.
466
+ * For PostgreSQL, have a look at Mike Perham's [EventMachine-based PostgreSQL driver](https://github.com/mperham/em_postgresql).
467
+ * 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
468
+ extreme performance of the official Redis C client, [hiredis](https://github.com/antirez/hiredis).
469
+ * For MongoDB, see [em-mongo](https://github.com/bcg/em-mongo)
470
+ * 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).
471
+
472
+ [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).
473
+ 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).
474
+ Also, EventMachine supports TLS (aka SSL) and works well on [JRuby](http://jruby.org) and Windows.
475
+
476
+ Learn more in our {file:docs/Ecosystem.md EventMachine ecosystem} and {file:docs/TLS.md TLS (aka SSL)} guides.
477
+
478
+
479
+ #### Connection loss detection ####
480
+
481
+ 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
482
+ 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
483
+ indicates what error (if any) caused the connection to be closed.
484
+
485
+ Learn more in our {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery} guide.
486
+
487
+
488
+ #### What the Chat Server Example doesn't demonstrate ####
489
+
490
+ 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
491
+ 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
492
+ networking programs quickly. However, {EventMachine::Connection#receive_data} does not offer any guarantees that you will be receiving "whole messages" all the time,
493
+ largely because the underlying transport (UDP or TCP) does not offer such guarantees. Many protocols, for example, AMQP, mandate that large content chunks are
494
+ 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,
495
+ 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_.
496
+ So **don't be deceived by the simplicity of this chat example**: it intentionally leaves framing out, but real world protocols usually require it.
497
+
498
+
499
+
500
+ ## A (Proxying) Chat Client Example ##
501
+
502
+ TBD
503
+
504
+
505
+ ## Wrapping up ##
506
+
507
+ This tutorial ends here. Congratulations! You have learned quite a bit about EventMachine.
508
+
509
+
510
+ ## What to read next ##
511
+
512
+ The documentation is organized as a {file:docs/DocumentationGuidesIndex.md number of guides}, covering all kinds of
513
+ topics. TBD
514
+
515
+
516
+ ## Tell us what you think! ##
517
+
518
+ Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
519
+ or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
520
+ Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
521
+ key to making documentation better.