eventmachine 1.0.0.beta.3-java → 1.0.0.beta.4-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.yardopts +5 -1
- data/{docs/GNU → GNU} +0 -0
- data/Gemfile +1 -0
- data/{docs/COPYING → LICENSE} +0 -0
- data/README.md +109 -0
- data/Rakefile +8 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/{ChangeLog → old/ChangeLog} +0 -0
- data/docs/{DEFERRABLES → old/DEFERRABLES} +0 -0
- data/docs/{EPOLL → old/EPOLL} +0 -0
- data/docs/{INSTALL → old/INSTALL} +0 -0
- data/docs/{KEYBOARD → old/KEYBOARD} +0 -0
- data/docs/{LEGAL → old/LEGAL} +0 -0
- data/docs/{LIGHTWEIGHT_CONCURRENCY → old/LIGHTWEIGHT_CONCURRENCY} +0 -0
- data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
- data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
- data/docs/{SMTP → old/SMTP} +0 -0
- data/docs/{SPAWNED_PROCESSES → old/SPAWNED_PROCESSES} +0 -0
- data/docs/{TODO → old/TODO} +0 -0
- data/eventmachine.gemspec +5 -2
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
- data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
- data/examples/{ex_tick_loop_array.rb → old/ex_tick_loop_array.rb} +0 -0
- data/examples/{ex_tick_loop_counter.rb → old/ex_tick_loop_counter.rb} +0 -0
- data/examples/{helper.rb → old/helper.rb} +0 -0
- data/ext/cmain.cpp +3 -3
- data/ext/ed.cpp +90 -15
- data/ext/ed.h +5 -5
- data/ext/em.cpp +59 -65
- data/ext/em.h +12 -2
- data/ext/extconf.rb +3 -3
- data/ext/pipe.cpp +2 -2
- data/ext/project.h +1 -1
- data/ext/rubymain.cpp +48 -3
- data/ext/ssl.cpp +5 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +2 -2
- data/lib/em/buftok.rb +35 -63
- data/lib/em/callback.rb +43 -11
- data/lib/em/channel.rb +21 -14
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +339 -209
- data/lib/em/deferrable.rb +4 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +37 -18
- data/lib/em/iterator.rb +42 -42
- data/lib/em/pool.rb +146 -0
- data/lib/em/process_watch.rb +5 -4
- data/lib/em/processes.rb +8 -4
- data/lib/em/protocols/httpclient.rb +22 -11
- data/lib/em/protocols/httpclient2.rb +15 -5
- data/lib/em/protocols/line_protocol.rb +2 -1
- data/lib/em/protocols/memcache.rb +17 -9
- data/lib/em/protocols/object_protocol.rb +2 -1
- data/lib/em/protocols/postgres3.rb +8 -9
- data/lib/em/protocols/smtpclient.rb +19 -11
- data/lib/em/protocols/smtpserver.rb +1 -1
- data/lib/em/protocols/stomp.rb +8 -6
- data/lib/em/protocols/tcptest.rb +3 -2
- data/lib/em/pure_ruby.rb +212 -208
- data/lib/em/queue.rb +22 -13
- data/lib/em/resolver.rb +70 -64
- data/lib/em/spawnable.rb +6 -3
- data/lib/em/streamer.rb +33 -45
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/timers.rb +6 -2
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +538 -602
- data/lib/jeventmachine.rb +22 -1
- data/tasks/package.rake +13 -3
- data/tasks/test.rake +1 -0
- data/tests/em_test_helper.rb +12 -3
- data/tests/test_completion.rb +177 -0
- data/tests/test_epoll.rb +2 -2
- data/tests/test_httpclient.rb +9 -9
- data/tests/test_httpclient2.rb +11 -9
- data/tests/test_ltp.rb +2 -10
- data/tests/test_pool.rb +128 -0
- data/tests/test_processes.rb +20 -2
- data/tests/test_queue.rb +8 -0
- data/tests/test_resolver.rb +1 -1
- data/tests/test_set_sock_opt.rb +37 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_unbind_reason.rb +31 -0
- metadata +87 -45
- data/README +0 -81
- data/tasks/doc.rake +0 -30
@@ -3,7 +3,7 @@
|
|
3
3
|
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
4
|
# Homepage:: http://rubyeventmachine.com
|
5
5
|
# Date:: 16 July 2006
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# See EventMachine and EventMachine::Connection for documentation and
|
8
8
|
# usage examples.
|
9
9
|
#
|
@@ -11,25 +11,26 @@
|
|
11
11
|
#
|
12
12
|
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
13
|
# Gmail: blackhedd
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# This program is free software; you can redistribute it and/or modify
|
16
16
|
# it under the terms of either: 1) the GNU General Public License
|
17
17
|
# as published by the Free Software Foundation; either version 2 of the
|
18
18
|
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# See the file COPYING for complete licensing information.
|
21
21
|
#
|
22
22
|
#---------------------------------------------------------------------------
|
23
23
|
#
|
24
|
-
#
|
24
|
+
#
|
25
25
|
|
26
26
|
|
27
27
|
|
28
28
|
module EventMachine
|
29
29
|
module Protocols
|
30
30
|
|
31
|
-
#
|
31
|
+
# <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
|
32
32
|
#
|
33
|
+
# @example
|
33
34
|
# EventMachine.run {
|
34
35
|
# http = EventMachine::Protocols::HttpClient.request(
|
35
36
|
# :host => server,
|
@@ -62,11 +63,21 @@ module EventMachine
|
|
62
63
|
|
63
64
|
MaxPostContentLength = 20 * 1024 * 1024
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
66
|
+
def initialize
|
67
|
+
warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead."
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param args [Hash] The request arguments
|
71
|
+
# @option args [String] :host The host IP/DNS name
|
72
|
+
# @option args [Integer] :port The port to connect too
|
73
|
+
# @option args [String] :verb The request type [GET | POST | DELETE | PUT]
|
74
|
+
# @option args [String] :request The request path
|
75
|
+
# @option args [Hash] :basic_auth The basic auth credentials (:username and :password)
|
76
|
+
# @option args [String] :content The request content
|
77
|
+
# @option args [String] :contenttype The content type (e.g. text/plain)
|
78
|
+
# @option args [String] :query_string The query string
|
79
|
+
# @option args [String] :host_header The host header to set
|
80
|
+
# @option args [String] :cookie Cookies to set
|
70
81
|
def self.request( args = {} )
|
71
82
|
args[:port] ||= 80
|
72
83
|
EventMachine.connect( args[:host], args[:port], self ) {|c|
|
@@ -265,4 +276,4 @@ module EventMachine
|
|
265
276
|
end
|
266
277
|
|
267
278
|
end
|
268
|
-
end
|
279
|
+
end
|
@@ -26,6 +26,8 @@
|
|
26
26
|
module EventMachine
|
27
27
|
module Protocols
|
28
28
|
|
29
|
+
# <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
|
30
|
+
#
|
29
31
|
# === Usage
|
30
32
|
#
|
31
33
|
# EM.run{
|
@@ -42,12 +44,15 @@ module EventMachine
|
|
42
44
|
include LineText2
|
43
45
|
|
44
46
|
def initialize
|
47
|
+
warn "HttpClient2 is deprecated and will be removed. EM-Http-Request should be used instead."
|
48
|
+
|
45
49
|
@authorization = nil
|
46
50
|
@closed = nil
|
47
51
|
@requests = nil
|
48
52
|
end
|
49
53
|
|
50
|
-
|
54
|
+
# @private
|
55
|
+
class Request
|
51
56
|
include Deferrable
|
52
57
|
|
53
58
|
attr_reader :version
|
@@ -279,12 +284,12 @@ module EventMachine
|
|
279
284
|
request args
|
280
285
|
end
|
281
286
|
|
282
|
-
# :stopdoc:
|
283
287
|
|
284
288
|
#--
|
285
289
|
# Compute and remember a string to be used as the host header in HTTP requests
|
286
290
|
# unless the user overrides it with an argument to #request.
|
287
291
|
#
|
292
|
+
# @private
|
288
293
|
def set_default_host_header host, port, ssl
|
289
294
|
if (ssl and port != 443) or (!ssl and port != 80)
|
290
295
|
@host_header = "#{host}:#{port}"
|
@@ -294,11 +299,13 @@ module EventMachine
|
|
294
299
|
end
|
295
300
|
|
296
301
|
|
302
|
+
# @private
|
297
303
|
def post_init
|
298
304
|
super
|
299
305
|
@connected = EM::DefaultDeferrable.new
|
300
306
|
end
|
301
307
|
|
308
|
+
# @private
|
302
309
|
def connection_completed
|
303
310
|
super
|
304
311
|
@connected.succeed
|
@@ -316,12 +323,14 @@ module EventMachine
|
|
316
323
|
# Set and remember a flag (@closed) so we can immediately fail any
|
317
324
|
# subsequent requests.
|
318
325
|
#
|
326
|
+
# @private
|
319
327
|
def unbind
|
320
328
|
super
|
321
329
|
@closed = true
|
322
330
|
(@requests || []).each {|r| r.fail}
|
323
331
|
end
|
324
332
|
|
333
|
+
# @private
|
325
334
|
def request args
|
326
335
|
args[:host_header] = @host_header unless args.has_key?(:host_header)
|
327
336
|
args[:authorization] = @authorization unless args.has_key?(:authorization)
|
@@ -335,6 +344,7 @@ module EventMachine
|
|
335
344
|
r
|
336
345
|
end
|
337
346
|
|
347
|
+
# @private
|
338
348
|
def receive_line ln
|
339
349
|
if req = @requests.last
|
340
350
|
req.receive_line ln
|
@@ -342,8 +352,9 @@ module EventMachine
|
|
342
352
|
p "??????????"
|
343
353
|
p ln
|
344
354
|
end
|
345
|
-
|
346
355
|
end
|
356
|
+
|
357
|
+
# @private
|
347
358
|
def receive_binary_data text
|
348
359
|
@requests.last.receive_text text
|
349
360
|
end
|
@@ -351,11 +362,10 @@ module EventMachine
|
|
351
362
|
#--
|
352
363
|
# Called by a Request object when it completes.
|
353
364
|
#
|
365
|
+
# @private
|
354
366
|
def pop_request
|
355
367
|
@requests.pop
|
356
368
|
end
|
357
|
-
|
358
|
-
# :startdoc:
|
359
369
|
end
|
360
370
|
|
361
371
|
|
@@ -32,18 +32,23 @@ module EventMachine
|
|
32
32
|
##
|
33
33
|
# constants
|
34
34
|
|
35
|
-
# :stopdoc:
|
36
35
|
unless defined? Cempty
|
36
|
+
# @private
|
37
37
|
Cstored = 'STORED'.freeze
|
38
|
+
# @private
|
38
39
|
Cend = 'END'.freeze
|
40
|
+
# @private
|
39
41
|
Cdeleted = 'DELETED'.freeze
|
42
|
+
# @private
|
40
43
|
Cunknown = 'NOT_FOUND'.freeze
|
44
|
+
# @private
|
41
45
|
Cerror = 'ERROR'.freeze
|
42
46
|
|
47
|
+
# @private
|
43
48
|
Cempty = ''.freeze
|
49
|
+
# @private
|
44
50
|
Cdelimiter = "\r\n".freeze
|
45
51
|
end
|
46
|
-
# :startdoc:
|
47
52
|
|
48
53
|
##
|
49
54
|
# commands
|
@@ -110,9 +115,7 @@ module EventMachine
|
|
110
115
|
EM.connect host, port, self, host, port
|
111
116
|
end
|
112
117
|
|
113
|
-
|
114
|
-
|
115
|
-
def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false # :nodoc:
|
118
|
+
def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false
|
116
119
|
send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
|
117
120
|
end
|
118
121
|
private :send_cmd
|
@@ -120,16 +123,19 @@ module EventMachine
|
|
120
123
|
##
|
121
124
|
# errors
|
122
125
|
|
126
|
+
# @private
|
123
127
|
class ParserError < StandardError
|
124
128
|
end
|
125
129
|
|
126
130
|
##
|
127
131
|
# em hooks
|
128
132
|
|
133
|
+
# @private
|
129
134
|
def initialize host, port = 11211
|
130
135
|
@host, @port = host, port
|
131
136
|
end
|
132
137
|
|
138
|
+
# @private
|
133
139
|
def connection_completed
|
134
140
|
@get_cbs = []
|
135
141
|
@set_cbs = []
|
@@ -148,6 +154,7 @@ module EventMachine
|
|
148
154
|
# 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
|
149
155
|
# stack overflows when there is too much data.
|
150
156
|
# include EM::P::LineText2
|
157
|
+
# @private
|
151
158
|
def receive_data data
|
152
159
|
(@buffer||='') << data
|
153
160
|
|
@@ -164,6 +171,7 @@ module EventMachine
|
|
164
171
|
|
165
172
|
#--
|
166
173
|
# def receive_line line
|
174
|
+
# @private
|
167
175
|
def process_cmd line
|
168
176
|
case line.strip
|
169
177
|
when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
|
@@ -209,6 +217,7 @@ module EventMachine
|
|
209
217
|
# @values[@cur_key] = data[0..-3]
|
210
218
|
# end
|
211
219
|
|
220
|
+
# @private
|
212
221
|
def unbind
|
213
222
|
if @connected or @reconnecting
|
214
223
|
EM.add_timer(1){ reconnect @host, @port }
|
@@ -219,8 +228,6 @@ module EventMachine
|
|
219
228
|
raise 'Unable to connect to memcached server'
|
220
229
|
end
|
221
230
|
end
|
222
|
-
|
223
|
-
# :startdoc:
|
224
231
|
end
|
225
232
|
end
|
226
233
|
end
|
@@ -229,7 +236,8 @@ if __FILE__ == $0
|
|
229
236
|
# ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
|
230
237
|
require 'em/spec'
|
231
238
|
|
232
|
-
|
239
|
+
# @private
|
240
|
+
class TestConnection
|
233
241
|
include EM::P::Memcache
|
234
242
|
def send_data data
|
235
243
|
sent_data << data
|
@@ -320,4 +328,4 @@ if __FILE__ == $0
|
|
320
328
|
end
|
321
329
|
|
322
330
|
end
|
323
|
-
end
|
331
|
+
end
|
@@ -24,25 +24,24 @@
|
|
24
24
|
#
|
25
25
|
#
|
26
26
|
|
27
|
-
require 'readbytes'
|
28
27
|
require 'postgres-pr/message'
|
29
28
|
require 'postgres-pr/connection'
|
30
29
|
require 'stringio'
|
31
30
|
|
32
|
-
|
31
|
+
# @private
|
32
|
+
class StringIO
|
33
33
|
# Reads exactly +n+ bytes.
|
34
34
|
#
|
35
35
|
# If the data read is nil an EOFError is raised.
|
36
36
|
#
|
37
|
-
# If the data read is too short
|
38
|
-
# data is obtainable via its #data method.
|
37
|
+
# If the data read is too short an IOError is raised
|
39
38
|
def readbytes(n)
|
40
39
|
str = read(n)
|
41
40
|
if str == nil
|
42
41
|
raise EOFError, "End of file reached"
|
43
42
|
end
|
44
43
|
if str.size < n
|
45
|
-
raise
|
44
|
+
raise IOError, "data truncated"
|
46
45
|
end
|
47
46
|
str
|
48
47
|
end
|
@@ -67,15 +66,15 @@ module EventMachine
|
|
67
66
|
# in basically a production-ready state, and the wire protocol isn't that complicated
|
68
67
|
# anyway.
|
69
68
|
#
|
70
|
-
# We need to monkeypatch StringIO because it lacks the #readbytes method needed
|
71
|
-
# by postgres-pr.
|
72
|
-
#
|
73
69
|
# We're tucking in a bunch of require statements that may not be present in garden-variety
|
74
70
|
# EM installations. Until we find a good way to only require these if a program
|
75
71
|
# requires postgres, this file will need to be required explicitly.
|
76
72
|
#
|
77
|
-
#
|
73
|
+
# We need to monkeypatch StringIO because it lacks the #readbytes method needed
|
74
|
+
# by postgres-pr.
|
75
|
+
# The StringIO monkeypatch is lifted from the standard library readbytes.rb,
|
78
76
|
# which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.
|
77
|
+
# It is modified to raise an IOError instead of TruncatedDataException since the exception is unused.
|
79
78
|
#
|
80
79
|
# We cloned the handling of postgres messages from lib/postgres-pr/connection.rb
|
81
80
|
# in the postgres-pr library, and modified it for event-handling.
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
4
|
# Homepage:: http://rubyeventmachine.com
|
5
5
|
# Date:: 16 July 2006
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# See EventMachine and EventMachine::Connection for documentation and
|
8
8
|
# usage examples.
|
9
9
|
#
|
@@ -11,17 +11,17 @@
|
|
11
11
|
#
|
12
12
|
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
13
|
# Gmail: blackhedd
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# This program is free software; you can redistribute it and/or modify
|
16
16
|
# it under the terms of either: 1) the GNU General Public License
|
17
17
|
# as published by the Free Software Foundation; either version 2 of the
|
18
18
|
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# See the file COPYING for complete licensing information.
|
21
21
|
#
|
22
22
|
#---------------------------------------------------------------------------
|
23
23
|
#
|
24
|
-
#
|
24
|
+
#
|
25
25
|
|
26
26
|
require 'ostruct'
|
27
27
|
|
@@ -30,6 +30,7 @@ module EventMachine
|
|
30
30
|
|
31
31
|
# Simple SMTP client
|
32
32
|
#
|
33
|
+
# @example
|
33
34
|
# email = EM::Protocols::SmtpClient.send(
|
34
35
|
# :domain=>"example.com",
|
35
36
|
# :host=>'localhost',
|
@@ -66,7 +67,7 @@ module EventMachine
|
|
66
67
|
class SmtpClient < Connection
|
67
68
|
include EventMachine::Deferrable
|
68
69
|
include EventMachine::Protocols::LineText2
|
69
|
-
|
70
|
+
|
70
71
|
def initialize
|
71
72
|
@succeeded = nil
|
72
73
|
@responder = nil
|
@@ -91,7 +92,10 @@ module EventMachine
|
|
91
92
|
# depending on the type.
|
92
93
|
# Currently only :type => :plain is supported. Pass additional parameters :username (String),
|
93
94
|
# and :password (either a String or a Proc that will be called at auth-time).
|
94
|
-
#
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
|
98
|
+
#
|
95
99
|
# :from => required String
|
96
100
|
# Specifies the sender of the message. Will be passed as the argument
|
97
101
|
# to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
|
@@ -109,7 +113,9 @@ module EventMachine
|
|
109
113
|
# containing the header values. TODO, support Arrays of header values, which would cause us to
|
110
114
|
# send that specific header line more than once.
|
111
115
|
#
|
112
|
-
#
|
116
|
+
# @example
|
117
|
+
# :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
|
118
|
+
#
|
113
119
|
# :body => Optional string, defaults blank.
|
114
120
|
# This will be passed as the body of the email message.
|
115
121
|
# TODO, this needs to be significantly beefed up. As currently written, this requires the caller
|
@@ -156,15 +162,15 @@ module EventMachine
|
|
156
162
|
}
|
157
163
|
end
|
158
164
|
|
159
|
-
# :stopdoc:
|
160
|
-
|
161
165
|
attr_writer :args
|
162
166
|
|
167
|
+
# @private
|
163
168
|
def post_init
|
164
169
|
@return_values = OpenStruct.new
|
165
170
|
@return_values.start_time = Time.now
|
166
171
|
end
|
167
172
|
|
173
|
+
# @private
|
168
174
|
def connection_completed
|
169
175
|
@responder = :receive_signon
|
170
176
|
@msg = []
|
@@ -175,6 +181,7 @@ module EventMachine
|
|
175
181
|
# set a deferred success because the caller will already have done it
|
176
182
|
# (no need to wait until the connection closes to invoke the callbacks).
|
177
183
|
#
|
184
|
+
# @private
|
178
185
|
def unbind
|
179
186
|
unless @succeeded
|
180
187
|
@return_values.elapsed_time = Time.now - @return_values.start_time
|
@@ -185,6 +192,7 @@ module EventMachine
|
|
185
192
|
end
|
186
193
|
end
|
187
194
|
|
195
|
+
# @private
|
188
196
|
def receive_line ln
|
189
197
|
$>.puts ln if @args[:verbose]
|
190
198
|
@range = ln[0...1].to_i
|
@@ -197,6 +205,8 @@ module EventMachine
|
|
197
205
|
end
|
198
206
|
end
|
199
207
|
|
208
|
+
private
|
209
|
+
|
200
210
|
# We encountered an error from the server and will close the connection.
|
201
211
|
# Use the error and message the server returned.
|
202
212
|
#
|
@@ -350,8 +360,6 @@ module EventMachine
|
|
350
360
|
@return_values.message = @msg
|
351
361
|
set_deferred_status :succeeded, @return_values
|
352
362
|
end
|
353
|
-
|
354
|
-
# :startdoc:
|
355
363
|
end
|
356
364
|
end
|
357
365
|
end
|