emdrb 0.1.2 → 0.2.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.
data/History.txt CHANGED
@@ -1,7 +1,17 @@
1
- == 0.1.0 / 2008-10-23
1
+ == 0.2.0 / 2009-01-23
2
2
 
3
- * First public release
3
+ * DRb client implementation, including asynchronous calls.
4
+ * Changes so that DRb functionality is as untouched as possible.
5
+
6
+ == 0.1.2 / 2009-01-21
7
+
8
+ * Maintenance release
4
9
 
5
10
  == 0.1.1 / 2008-12-02
6
11
 
7
12
  * Basic unit tests
13
+
14
+ == 0.1.0 / 2008-10-23
15
+
16
+ * First public release
17
+
data/LEGAL CHANGED
@@ -1,7 +1,7 @@
1
1
  LEGAL NOTICE INFORMATION
2
2
  ------------------------
3
3
 
4
- EMDRb is Copyright (C) 2008 by Rafael Sevilla.
4
+ EMDRb is Copyright © 2008, 2009 by Rafael Sevilla.
5
5
 
6
6
  EMDRb is copyrighted software owned by Rafael Sevilla (dido
7
7
  ... imperium.ph). You may redistribute and/or modify this software as
data/Manifest.txt CHANGED
@@ -7,6 +7,11 @@ Rakefile
7
7
  lib/emdrb.rb
8
8
  lib/emdrb/emdrb.rb
9
9
  lib/emdrb/version.rb
10
+ spec/client_spec.rb
11
+ spec/drbserver.rb
12
+ spec/server_spec.rb
13
+ spec/spec_common.rb
14
+ spec/spec_helper.rb
10
15
  tasks/ann.rake
11
16
  tasks/bones.rake
12
17
  tasks/gem.rake
@@ -20,4 +25,3 @@ tasks/setup.rb
20
25
  tasks/spec.rake
21
26
  tasks/svn.rake
22
27
  tasks/test.rake
23
- test/test_emdrb.rb
data/README.txt CHANGED
@@ -10,20 +10,19 @@ available in the Ruby standard library.
10
10
 
11
11
  == FEATURES/PROBLEMS:
12
12
 
13
- This is a simple but working DRb server implementation that uses
14
- EventMachine as its basis, rather than the default implementation that
15
- uses traditional Ruby sockets. This should at the very least play
16
- better with other programs that have an EventMachine event loop, and
17
- hopefully provide somewhat better scalability.
13
+ This is a simple but working DRb client/server implementation that
14
+ uses EventMachine as its basis, rather than the default implementation
15
+ that uses traditional Ruby sockets. This should at the very least
16
+ play better with other programs that have an EventMachine event loop,
17
+ and hopefully provide somewhat better scalability.
18
18
 
19
19
  Obviously, this is a quick and dirty release, just to get something
20
- out there, and of course it has a number of limitations.
20
+ out there, and of course it has a number of limitations:
21
21
 
22
- * We still don't have a DRb client. We use the client implementation
23
- of the standard DRb.
24
22
  * No SSL support.
25
23
  * No support for ACLs.
26
- * No unit tests so it probably still has a lot of bugs.
24
+ * No support for DRb over Unix domain sockets.
25
+ * RSpec tests are very basic, and need a lot more comprehensive work.
27
26
  * Many standard configuration options for DRb still unsupported
28
27
 
29
28
  These and many other problems are scheduled to be addressed in the
@@ -31,8 +30,11 @@ next release.
31
30
 
32
31
  == SYNOPSIS:
33
32
 
34
- Creating a server using EMDRb has been made as close as possible to
35
- making one with the standard library DRb:
33
+ EMDRb basically reopens several classes and adds methods and overrides
34
+ some methods in the basic distributed Ruby implementation to make it
35
+ use EventMachine's infrastructure instead of the normal networking
36
+ layer. One could do the following, which is practically identical to
37
+ one of the examples for distributed Ruby:
36
38
 
37
39
  require 'emdrb'
38
40
 
@@ -45,10 +47,35 @@ making one with the standard library DRb:
45
47
  end
46
48
 
47
49
  $SAFE=1
48
- EMDRb.start_service(URI, TimeServer.new)
49
- EMDRb.thread.join
50
+ DRb.start_service(URI, TimeServer.new)
51
+ DRb.thread.join
50
52
 
51
- It is also possible to create
53
+ The corresponding client code could be made nearly identical:
54
+
55
+ require 'emdrb'
56
+
57
+ SERVER_URI="druby://localhost:8787"
58
+
59
+ DRb.start_service
60
+
61
+ timeserver = DRbObject.new_with_uri(SERVER_URI)
62
+ puts timeserver.get_current_time
63
+
64
+ Or it could be written to use of asynchronous calls:
65
+
66
+ require 'emdrb'
67
+
68
+ SERVER_URI="druby://localhost:8787"
69
+
70
+ DRb.start_service
71
+
72
+ timeserver = DRbObject.new_with_uri(SERVER_URI)
73
+
74
+ EventMachine::next_tick do
75
+ timeserver.async_call(:get_current_time).callback do |res|
76
+ puts res
77
+ end
78
+ end
52
79
 
53
80
  == REQUIREMENTS:
54
81
 
@@ -58,11 +85,16 @@ It is also possible to create
58
85
 
59
86
  * Standard gem installation: 'sudo gem install' ought to do the trick.
60
87
 
61
- == LICENSE:
88
+ Note that you will need the daemons gem ('sudo gem install daemons')
89
+ if you would like to run the rspec tests that are included with
90
+ EMDRb. Daemons is not required to use EMDRb otherwise, and as such it
91
+ is not listed as a hard dependency in the gem install.
62
92
 
63
- Copyright (c) 2008 Rafael R. Sevilla. You can redistribute it and/or
64
- modify it under the same terms as Ruby. Please see the file COPYING for
65
- more details.
93
+ == LICENSE:
66
94
 
95
+ Copyright © 2008, 2009 Rafael R. Sevilla. You can redistribute it
96
+ and/or modify it under the same terms as Ruby. Please see the file
97
+ COPYING for more details.
67
98
 
99
+ $Id: README.txt 63 2009-01-23 09:15:55Z dido $
68
100
 
data/Rakefile CHANGED
@@ -1,12 +1,31 @@
1
1
  # -*- Ruby -*-
2
- # $Id: Rakefile 18 2008-12-02 03:58:53Z dido $
2
+ #
3
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
4
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
5
+ # Homepage:: http://emdrb.rubyforge.org/
6
+ # License:: GNU General Public License / Ruby License
7
+ #
8
+ # $Id: Rakefile 61 2009-01-23 09:11:43Z dido $
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright © 2008, 2009 Rafael Sevilla
13
+ # This file is part of EMDRb
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #----------------------------------------------------------------------------
3
22
  #
4
23
  load 'tasks/setup.rb'
5
24
 
6
25
  ensure_in_path 'lib'
7
26
  require 'emdrb/version'
8
27
 
9
- task :default => 'test:run'
28
+ task :default => 'spec:run'
10
29
 
11
30
  PROJ.name = 'emdrb'
12
31
  PROJ.authors = 'dido@imperium.ph'
@@ -15,7 +34,6 @@ PROJ.url = 'http://emdrb.rubyforge.org'
15
34
  PROJ.rubyforge.name = 'emdrb'
16
35
  PROJ.version = EMDRb::Version::STRING
17
36
  PROJ.dependencies = ["eventmachine"]
18
- PROJ.rcov.opts += ["-Ilib"] # Why is this necessary?
19
37
 
20
38
  PROJ.spec.opts << '--color'
21
39
 
data/lib/emdrb/emdrb.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  #
2
2
  # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
- # Copyright:: Copyright (c) 2008 Rafael R. Sevilla
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
4
  # Homepage:: http://emdrb.rubyforge.org/
5
5
  # License:: GNU General Public License / Ruby License
6
6
  #
7
- # $Id: emdrb.rb 28 2009-01-22 05:13:01Z dido $
7
+ # $Id: emdrb.rb 49 2009-01-23 08:51:47Z dido $
8
8
  #
9
9
  #----------------------------------------------------------------------------
10
10
  #
11
- # Copyright (C) 2008 Rafael Sevilla
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
12
  # This file is part of EMDRb
13
13
  #
14
14
  # This program is free software; you can redistribute it and/or modify
@@ -20,9 +20,10 @@
20
20
  #----------------------------------------------------------------------------
21
21
  #
22
22
  require 'eventmachine'
23
+ require 'thread'
23
24
  require 'drb'
24
25
 
25
- module EMDRb
26
+ module DRb
26
27
  DEFAULT_ARGC_LIMIT = 256
27
28
  DEFAULT_LOAD_LIMIT = 256 * 102400
28
29
  DEFAULT_SAFE_LEVEL = 0
@@ -40,7 +41,7 @@ module EMDRb
40
41
  # byte order.
41
42
  #
42
43
  def dump(obj, error=false)
43
- if obj.kind_of? DRb::DRbUndumped
44
+ if obj.kind_of? DRbUndumped
44
45
  obj = make_proxy(obj, error)
45
46
  end
46
47
  begin
@@ -55,22 +56,21 @@ module EMDRb
55
56
  # Create a proxy for +obj+ that is declared to be undumpable.
56
57
  #
57
58
  def make_proxy(obj, error=false)
58
- return(error ? DRb::DRbRemoteError.new(obj) : DRb::DRbObject.new(obj))
59
+ return(error ? DRbRemoteError.new(obj) : DRbObject.new(obj))
59
60
  end
60
61
 
61
62
  ##
62
63
  # Receive data from the caller. This basically receives packets
63
64
  # containing objects marshalled using Ruby's Marshal::dump prefixed
64
65
  # by a length. These objects are unmarshalled and processed by the
65
- # internal object request state machine (DRbServerProtocol#receive_obj
66
- # below). If an error of any kind occurs herein, the exception is
67
- # propagated to the caller.
68
- def receive_data(data)
66
+ # internal object request state machine which should be represented by
67
+ # a receive_obj method from within the mixin.
68
+ def receive_data_raw(data)
69
69
  @msgbuffer << data
70
70
  while @msgbuffer.length > 4
71
71
  length = @msgbuffer.unpack("N")[0]
72
72
  if length > @load_limit
73
- raise DRb::DRbConnError, "too large packet #{length}"
73
+ raise DRbConnError, "too large packet #{length}"
74
74
  end
75
75
 
76
76
  if @msgbuffer.length < length - 4
@@ -89,7 +89,7 @@ module EMDRb
89
89
  begin
90
90
  return(Marshal::load(message))
91
91
  rescue NameError, ArgumentError
92
- return(DRb::DRbUnknown.new($!, message))
92
+ return(DRbUnknown.new($!, message))
93
93
  end
94
94
  end
95
95
  end
@@ -161,79 +161,111 @@ module EMDRb
161
161
 
162
162
  ##
163
163
  # This method will perform a method action if a block is not specified.
164
- #
164
+ # For symmetry with perform_with_block, this method returns a deferrable
165
+ # instead of the actual value or exception as the case may be. It will
166
+ # also further execute the method call in its own independent thread
167
+ # and safe level invocations are also taken care of herein.
165
168
  def perform_without_block
166
- if Proc == @front && @request[:msg] == :__drb_yield
167
- ary = (@request[:argv].size == 1) ? @request[:argv] : [@request[:argv]]
168
- return(ary.collect(&@front)[0])
169
+ df = EventMachine::DefaultDeferrable.new
170
+ info = Thread.current['DRb']
171
+ req = @request
172
+ Thread.new do
173
+ Thread.current['DRb'] = info
174
+ if $SAFE < @safe_level
175
+ $SAFE = @safe_level
176
+ end
177
+ begin
178
+ if Proc == req[:ro] && req[:msg] == :__drb_yield
179
+ ary = (req[:argv].size == 1) ? req[:argv] :
180
+ [req[:argv]]
181
+ EventMachine::next_tick do
182
+ df.set_deferred_status(:succeeded, ary.collect(&@front)[0])
183
+ end
184
+ else
185
+ r = req[:ro].__send__(req[:msg], *req[:argv])
186
+ EventMachine::next_tick do
187
+ df.set_deferred_status(:succeeded, r)
188
+ end
189
+ end
190
+ rescue
191
+ p $!
192
+ EventMachine::next_tick do
193
+ df.set_deferred_status(:failed, $!)
194
+ end
195
+ end
169
196
  end
170
- return(@front.__send__(@request[:msg], *@request[:argv]))
197
+ return(df)
171
198
  end
172
199
 
173
200
  ##
174
201
  # block_yield method lifted almost verbatim from InvokeMethod18Mixin
175
202
  # from the standard distributed Ruby. Obviously, since EventMachine
176
203
  # doesn't work with Ruby 1.6.x, we don't care about the 1.6 version...
177
- #
178
- def block_yield(x)
204
+ # Since this performs a synchronous DRb call, we need to execute this
205
+ # within a thread of its own.
206
+ def block_yield(req, x)
179
207
  if x.size == 1 && x[0].class == Array
180
- x[0] = DRb::DRbArray.new(x[0])
208
+ x[0] = DRbArray.new(x[0])
181
209
  end
182
- block_value = @request[:block].call(*x)
210
+ block_value = req[:block].call(*x)
211
+ return(block_value)
183
212
  end
184
213
 
185
214
  ##
186
- # Perform with a method action with a specified block.
187
- #
215
+ # Perform with a method action with a specified block. We have to
216
+ # do the action within a thread of its own in order to avoid deadlock
217
+ # due to the call in block_yield above, which uses synchronous calls.
218
+ # I suppose there must be a way to do it without using threads (possibly
219
+ # by using call/cc perhaps?), but I suppose this should be okay.
188
220
  def perform_with_block
189
- @front.__send__(@request[:msg], *@request[:argv]) do |*x|
190
- jump_error = nil
191
- begin
192
- block_value = block_yield(x)
193
- rescue LocalJumpError
194
- jump_error = $!
221
+ df = EventMachine::DefaultDeferrable.new
222
+ info = Thread.current['DRb']
223
+ req = @request
224
+ Thread.new do
225
+ Thread.current['DRb'] = info
226
+ if $SAFE < @safe_level
227
+ $SAFE = @safe_level
195
228
  end
196
- if jump_error
197
- case jump_error.reason
198
- when :retry
199
- retry
200
- when :break
201
- break(jump_error.exit_value)
202
- else
203
- raise jump_error
229
+ begin
230
+ r = req[:ro].__send__(req[:msg], *req[:argv]) { |*x|
231
+ jump_error = nil
232
+ block_value = nil
233
+ begin
234
+ block_value = block_yield(req, x)
235
+ rescue LocalJumpError
236
+ jump_error = $!
237
+ end
238
+ if jump_error
239
+ case jump_error.reason
240
+ when :retry
241
+ retry
242
+ when :break
243
+ break(jump_error.exit_value)
244
+ else
245
+ raise jump_error
246
+ end
247
+ end
248
+ block_value
249
+ }
250
+ EventMachine::next_tick { df.set_deferred_status(:succeeded, r) }
251
+ rescue Exception => e
252
+ EventMachine::next_tick do
253
+ df.set_deferred_status(:failed, e)
204
254
  end
205
255
  end
206
- block_value
207
256
  end
257
+ return(df)
208
258
  end
209
259
 
210
260
  ##
211
- # Perform a method action. This handles the safe level invocations.
261
+ # Perform a method action. This returns a deferrable that gets
262
+ # posted to succeeded or failed depending on whether the method
263
+ # call did not raise or raised an exception while it was being
264
+ # executed.
212
265
  #
213
266
  def perform
214
- result = nil
215
- succ = false
216
- begin
217
- @server.check_insecure_method(@front, @request[:msg])
218
- if $SAFE < @safe_level
219
- info = Thread.current['DRb']
220
- result = Thread.new {
221
- Thread.current['DRb'] = info
222
- $SAFE = @safe_level
223
- (@request[:block]) ? perform_with_block : perform_without_block
224
- }.value
225
- else
226
- result = (@request[:block]) ? perform_with_block :
227
- perform_without_block
228
- succ = true
229
- if @request[:msg] == :to_ary && result.class == Array
230
- result = DRb::DRbArray.new(result)
231
- end
232
- end
233
- rescue StandardError, ScriptError, Interrupt
234
- result = $!
235
- end
236
- return([succ, result])
267
+ @server.check_insecure_method(@request[:ro], @request[:msg])
268
+ return((@request[:block]) ? perform_with_block : perform_without_block)
237
269
  end
238
270
 
239
271
  def to_obj(ref)
@@ -283,7 +315,16 @@ module EMDRb
283
315
  when :block
284
316
  @request[:argv] = @argv
285
317
  @state = :ref
286
- send_reply(*perform)
318
+ df = perform
319
+ df.callback do |result|
320
+ if @request[:msg] == :to_ary && result.class == Array
321
+ result = DRb::DRbArray.new(result)
322
+ end
323
+ send_reply(true, result)
324
+ end
325
+ df.errback do |error|
326
+ send_reply(false, error)
327
+ end
287
328
  @request = {}
288
329
  @argc = @argv = nil
289
330
  else
@@ -291,6 +332,18 @@ module EMDRb
291
332
  end
292
333
  end
293
334
 
335
+ ##
336
+ # This version of receive_data will propagate any exceptions thrown
337
+ # by receive_data_raw back to the caller. This includes load limit
338
+ # errors and other miscellanea.
339
+ def receive_data(data)
340
+ begin
341
+ return(receive_data_raw(data))
342
+ rescue Exception => e
343
+ return(send_reply(false, e))
344
+ end
345
+ end
346
+
294
347
  end
295
348
 
296
349
  ##
@@ -298,7 +351,7 @@ module EMDRb
298
351
  # for brevity. DRbServer instances are normally created indirectly using
299
352
  # either EMDRb.start service (which emulates DRb.start_service) or via
300
353
  # EMDRb.start_drbserver (designed to be called from within an event loop).
301
- class DRbServer < DRb::DRbServer
354
+ class DRbServer
302
355
  def initialize(uri=nil, front=nil, config_or_acl=nil)
303
356
  if Hash === config_or_acl
304
357
  config = config_or_acl.dup
@@ -314,6 +367,9 @@ module EMDRb
314
367
  @front = front
315
368
  @idconv = @config[:idconv]
316
369
  @safe_level = @config[:safe_level]
370
+ EventMachine::next_tick do
371
+ @thread = Thread.current
372
+ end
317
373
  end
318
374
 
319
375
  private
@@ -348,11 +404,11 @@ module EMDRb
348
404
  #
349
405
  def start_drb_server
350
406
  @thread = Thread.current
351
- host, port, opt = EMDRb::parse_uri(@uri)
407
+ host, port, opt = DRb::parse_uri_drb(@uri)
352
408
  if host.size == 0
353
409
  host = self.class.host_inaddr_any
354
410
  end
355
- EventMachine::start_server(host, port, DRbServerProtocol) do |conn|
411
+ r = EventMachine::start_server(host, port, DRbServerProtocol) do |conn|
356
412
  Thread.current['DRb'] = { 'client' => conn, 'server' => self }
357
413
  conn.front = @front
358
414
  conn.load_limit = @config[:load_limit]
@@ -361,11 +417,18 @@ module EMDRb
361
417
  conn.server = self
362
418
  conn.safe_level = self.safe_level
363
419
  end
420
+ # NOTE: This is an undocumented method in EventMachine. Revise
421
+ # as necessary when we receive feedback from the EventMachine
422
+ # developers on the canonical way to determine the real port number
423
+ # if port 0 was specified in start_server.
424
+ addr = Socket.unpack_sockaddr_in(EventMachine.get_sockname(r))
425
+ port = addr[0] if port == 0
426
+ @uri = "druby://#{host}:#{port}"
364
427
  end
365
428
 
366
429
  end
367
430
 
368
- def parse_uri(uri)
431
+ def parse_uri_drb(uri)
369
432
  if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
370
433
  host = $1
371
434
  port = $2.to_i
@@ -378,9 +441,8 @@ module EMDRb
378
441
  raise DRb::DRbBadURI.new('can\'t parse uri:' + uri)
379
442
  end
380
443
  end
381
- module_function :parse_uri
444
+ module_function :parse_uri_drb
382
445
 
383
- @primary_server = nil
384
446
  @eventloop = nil
385
447
 
386
448
  ##
@@ -411,52 +473,11 @@ module EMDRb
411
473
  end
412
474
  serv = queue.shift
413
475
  @primary_server = serv
414
- EMDRb.regist_server(serv)
476
+ DRb.regist_server(serv)
415
477
  return(serv)
416
478
  end
417
479
  module_function :start_service
418
480
 
419
- attr_accessor :primary_server
420
- module_function :primary_server=, :primary_server
421
-
422
- @server = {}
423
- def regist_server(server)
424
- @server[server.uri] = server
425
- Thread.exclusive do
426
- @primary_server = server unless @primary_server
427
- end
428
- end
429
- module_function :regist_server
430
-
431
- ##
432
- # Get the 'current' server.
433
- #
434
- # In the context of execution taking place within the main
435
- # thread of a dRuby server (typically, as a result of a remote
436
- # call on the server or one of its objects), the current
437
- # server is that server. Otherwise, the current server is
438
- # the primary server.
439
- #
440
- # If the above rule fails to find a server, a DRbServerNotFound
441
- # error is raised.
442
- def current_server
443
- drb = Thread.current['DRb']
444
- server = (drb && drb['server']) ? drb['server'] : @primary_server
445
- raise DRb::DRbServerNotFound unless server
446
- return server
447
- end
448
- module_function :current_server
449
-
450
- ##
451
- # Get the thread of the primary server.
452
- #
453
- # This returns nil if there is no primary server. See #primary_server.
454
- def thread
455
- @primary_server ? @primary_server.thread : nil
456
- end
457
- module_function :thread
458
-
459
-
460
481
  ##
461
482
  # Client protocol module
462
483
  module DRbClientProtocol
@@ -470,7 +491,7 @@ module EMDRb
470
491
 
471
492
  def post_init
472
493
  @msgbuffer = ""
473
- @idconv = DRb::DRbIdConv.new
494
+ @idconv = DRbIdConv.new
474
495
  @load_limit = DEFAULT_LOAD_LIMIT
475
496
  end
476
497
 
@@ -506,6 +527,10 @@ module EMDRb
506
527
  close_connection
507
528
  end
508
529
  end
530
+
531
+ def receive_data(data)
532
+ return(receive_data_raw(data))
533
+ end
509
534
  end
510
535
 
511
536
  ##
@@ -513,7 +538,7 @@ module EMDRb
513
538
  #
514
539
  # Method calls on this object are relayed to the remote object
515
540
  # that this object is a stub for.
516
- class DRbObject < DRb::DRbObject
541
+ class DRbObject
517
542
  def initialize(obj, uri=nil)
518
543
  @uri = nil
519
544
  @ref = nil
@@ -521,18 +546,22 @@ module EMDRb
521
546
  return if uri.nil?
522
547
  @uri = uri
523
548
  ref = nil
524
- @host, @port, @opt = EMDRb::parse_uri(@uri)
549
+ @host, @port, @opt = DRb::parse_uri_drb(@uri)
525
550
  else
526
- @ref = obj
551
+ @uri = uri ? uri : (DRb.uri rescue nil)
552
+ @ref = obj ? DRb.to_id(obj) : nil
527
553
  end
528
554
  end
529
555
 
530
556
  ##
531
557
  # Perform an asynchronous call to the remote object. This can only
532
- # be used from within an event loop. It returns a deferrable to which
558
+ # be used from within the event loop. It returns a deferrable to which
533
559
  # callbacks can be attached.
534
560
  def send_async(msg_id, *a, &b)
535
561
  df = EventMachine::DefaultDeferrable.new
562
+ if @host.nil? || @port.nil?
563
+ @host, @port, @opt = DRb::parse_uri_drb(@uri)
564
+ end
536
565
  EventMachine.connect(@host, @port, DRbClientProtocol) do |c|
537
566
  c.ref = self
538
567
  c.msg_id = msg_id
@@ -543,6 +572,37 @@ module EMDRb
543
572
  return(df)
544
573
  end
545
574
 
575
+ ##
576
+ # Route method calls to the referenced object. This synchronizes
577
+ # an asynchronous call by using a Queue to synchronize the DRb
578
+ # event thread with the calling thread, so use of this mechanism,
579
+ # to make method calls within an event loop will thus result in a
580
+ # threading deadlock! Use the send_async method if you want to
581
+ # use EMDRb from within an event loop.
582
+ def method_missing(msg_id, *a, &b)
583
+ if DRb.here?(@uri)
584
+ obj = DRb.to_obj(@ref)
585
+ DRb.current_server.check_insecure_method(obj, msg_id)
586
+ return obj.__send__(msg_id, *a, &b)
587
+ end
588
+
589
+ q = Queue.new
590
+ EventMachine::next_tick do
591
+ df = self.send_async(msg_id, *a, &b)
592
+ df.callback { |data| q << data }
593
+ end
594
+ succ, result = q.shift
595
+ if succ
596
+ return result
597
+ elsif DRbUnknown === result
598
+ raise result
599
+ else
600
+ bt = self.class.prepare_backtrace(@uri, result)
601
+ result.set_backtrace(bt + caller)
602
+ raise result
603
+ end
604
+ end
605
+
546
606
  end
547
607
 
548
608
  end
data/lib/emdrb/version.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # Homepage:: http://emdrb.rubyforge.org/
5
5
  # License:: GNU Lesser General Public License / Ruby License
6
6
  #
7
- # $Id: version.rb 26 2009-01-22 04:48:43Z dido $
7
+ # $Id: version.rb 57 2009-01-23 09:07:54Z dido $
8
8
  #
9
9
  #----------------------------------------------------------------------------
10
10
  #
@@ -25,8 +25,8 @@ module EMDRb
25
25
  module Version
26
26
 
27
27
  MAJOR = 0
28
- MINOR = 1
29
- TINY = 2
28
+ MINOR = 2
29
+ TINY = 0
30
30
 
31
31
  # The version of EMDRb in use.
32
32
  STRING = [ MAJOR, MINOR, TINY ].join(".")
@@ -0,0 +1,61 @@
1
+ #
2
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
+ # Homepage:: http://emdrb.rubyforge.org/
5
+ # License:: GNU General Public License / Ruby License
6
+ #
7
+ # $Id: client_spec.rb 50 2009-01-23 08:52:21Z dido $
8
+ #
9
+ #----------------------------------------------------------------------------
10
+ #
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
+ # This file is part of EMDRb
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #----------------------------------------------------------------------------
21
+ #
22
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
23
+ require File.join(File.dirname(__FILE__), %w[spec_common])
24
+
25
+ Thread.abort_on_exception = true
26
+
27
+ describe EMDRb, "Client" do
28
+ it_should_behave_like "DRb basics"
29
+
30
+ before(:all) do
31
+ system(File.join(File.dirname(__FILE__), "drbserver.rb drb"))
32
+ DRb.start_service
33
+ @obj = DRb::DRbObject.new(nil, "druby://localhost:12345")
34
+ end
35
+
36
+ after(:all) do
37
+ pid = File.open(File.join(File.dirname(__FILE__), "drbserver.pid")) { |fp| fp.read.to_i }
38
+ Process.kill("SIGTERM", pid)
39
+ end
40
+
41
+ it "should be able to perform asynchronous method calls" do
42
+ EventMachine::next_tick do
43
+ @obj.send_async(:identity, 1).callback do |data|
44
+ data[0].should be_true
45
+ data[1].should == 1
46
+ end
47
+ end
48
+ end
49
+
50
+ it "should be able to perform asynchronous method calls with a passed block" do
51
+ EventMachine::next_tick do
52
+ val = 1
53
+ df = @obj.send_async(:blockyield, 1,2,3,4,5,6,7) { |x| val *= x; val }
54
+ df.callback do |data|
55
+ data[0].should be_true
56
+ val.should == 5040
57
+ end
58
+ end
59
+ end
60
+
61
+ end
data/spec/drbserver.rb ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
+ # Homepage:: http://emdrb.rubyforge.org/
5
+ # License:: GNU General Public License / Ruby License
6
+ #
7
+ # $Id: drbserver.rb 46 2009-01-23 08:10:42Z dido $
8
+ #
9
+ #----------------------------------------------------------------------------
10
+ #
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
+ # This file is part of EMDRb
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #----------------------------------------------------------------------------
21
+ #
22
+ # This is the DRb server that should be run by the specs, and can execute
23
+ # using either the standard DRb or EMDRb depending on what is being tested.
24
+ #
25
+ require 'daemons'
26
+ Thread.abort_on_exception = true
27
+ if ARGV[0] == "emdrb"
28
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
29
+ require 'emdrb'
30
+ elsif ARGV[0] == "drb"
31
+ require 'drb'
32
+ else
33
+ raise "specify emdrb or drb on the command line"
34
+ end
35
+
36
+ if ARGV[1].nil?
37
+ pidfile = File.expand_path(File.join(File.dirname(__FILE__), "drbserver.pid"))
38
+ if File.exist?(pidfile)
39
+ exit(0)
40
+ end
41
+ # logfile = File.expand_path(File.join(File.dirname(__FILE__), "drbserver.log"))
42
+ Daemonize.daemonize
43
+ pid = Process.pid
44
+ File.open(pidfile, "w") { |fp| fp.write(pid.to_s) }
45
+
46
+ handler = lambda do
47
+ File.delete(pidfile)
48
+ exit(0)
49
+ end
50
+
51
+ trap("SIGTERM", handler)
52
+ trap("SIGINT", handler)
53
+ end
54
+
55
+ class TestServer
56
+ def identity(x)
57
+ return(x)
58
+ end
59
+
60
+ def addtwo(x, y)
61
+ return(x+y)
62
+ end
63
+
64
+ def sum(*vals)
65
+ return(vals.inject(0) { |x,y| x + y })
66
+ end
67
+
68
+ def blockyield(*vals)
69
+ vals.each do |x|
70
+ yield x
71
+ end
72
+ end
73
+ end
74
+
75
+
76
+
77
+ DRb.start_service("druby://:12345", TestServer.new)
78
+ DRb.thread.join
@@ -0,0 +1,51 @@
1
+ #
2
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
+ # Homepage:: http://emdrb.rubyforge.org/
5
+ # License:: GNU General Public License / Ruby License
6
+ #
7
+ # $Id: server_spec.rb 48 2009-01-23 08:48:43Z dido $
8
+ #
9
+ #----------------------------------------------------------------------------
10
+ #
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
+ # This file is part of EMDRb
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #----------------------------------------------------------------------------
21
+ #
22
+ # This tests the EMDRb server implementation against the standard DRb
23
+ # client.
24
+ #
25
+ #require File.join(File.dirname(__FILE__), %w[spec_helper])
26
+ require File.join(File.dirname(__FILE__), %w[spec_common])
27
+ require 'drb' # yes, that's right, we use the STANDARD DRb here!
28
+ require 'thread'
29
+
30
+ Thread.abort_on_exception = true
31
+
32
+ describe "EMDRb Server" do
33
+ it_should_behave_like "DRb basics"
34
+
35
+ before(:all) do
36
+ # but we start the *server* with EMDRb
37
+ system(File.join(File.dirname(__FILE__), "drbserver.rb emdrb"))
38
+ DRb.start_service
39
+ @obj = DRbObject.new_with_uri("druby://localhost:12345")
40
+ end
41
+
42
+ after(:all) do
43
+ pid = File.open(File.join(File.dirname(__FILE__), "drbserver.pid")) { |fp| fp.read.to_i }
44
+ Process.kill("SIGTERM", pid)
45
+ end
46
+
47
+ it "should work with variadic methods" do
48
+ @obj.sum(1,2,3,4,5).should == 15
49
+ end
50
+
51
+ end
@@ -0,0 +1,34 @@
1
+ #
2
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
+ # Homepage:: http://emdrb.rubyforge.org/
5
+ # License:: GNU General Public License / Ruby License
6
+ #
7
+ # $Id: spec_common.rb 44 2009-01-23 06:40:54Z dido $
8
+ #
9
+ #----------------------------------------------------------------------------
10
+ #
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
+ # This file is part of EMDRb
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #----------------------------------------------------------------------------
21
+ #
22
+ describe "DRb basics", :shared => true do
23
+ it "should be able to perform simple synchronous method calls" do
24
+ @obj.identity(1).should == 1
25
+ @obj.addtwo(1, 2).should == 3
26
+ end
27
+
28
+ it "should be able to perform synchronous method calls with a block" do
29
+ val = 1
30
+ @obj.blockyield(1,2,3,4,5,6,7) { |x| val *= x }
31
+ val.should == 5040
32
+ end
33
+
34
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
+ # Copyright:: Copyright © 2008, 2009 Rafael R. Sevilla
4
+ # Homepage:: http://emdrb.rubyforge.org/
5
+ # License:: GNU General Public License / Ruby License
6
+ #
7
+ # $Id: spec_helper.rb 60 2009-01-23 09:11:11Z dido $
8
+ #
9
+ #----------------------------------------------------------------------------
10
+ #
11
+ # Copyright © 2008, 2009 Rafael Sevilla
12
+ # This file is part of EMDRb
13
+ #
14
+ # This program is free software; you can redistribute it and/or modify
15
+ # it under the terms of either: 1) the GNU General Public License
16
+ # as published by the Free Software Foundation; either version 2 of the
17
+ # License, or (at your option) any later version; or 2) Ruby's License.
18
+ #
19
+ # See the file COPYING for complete licensing information.
20
+ #----------------------------------------------------------------------------
21
+ #
22
+ require File.expand_path(
23
+ File.join(File.dirname(__FILE__), %w[.. lib emdrb]))
24
+
25
+ Spec::Runner.configure do |config|
26
+ # == Mock Framework
27
+ #
28
+ # RSpec uses it's own mocking framework by default. If you prefer to
29
+ # use mocha, flexmock or RR, uncomment the appropriate line:
30
+ #
31
+ # config.mock_with :mocha
32
+ # config.mock_with :flexmock
33
+ # config.mock_with :rr
34
+ end
35
+
36
+ # EOF
data/tasks/ann.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: ann.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  begin
4
4
  require 'bones/smtp_tls'
data/tasks/bones.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: bones.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  if HAVE_BONES
4
4
 
data/tasks/gem.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: gem.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  require 'rake/gempackagetask'
4
4
 
data/tasks/git.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: git.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  if HAVE_GIT
4
4
 
data/tasks/manifest.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: manifest.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  require 'find'
4
4
 
data/tasks/notes.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: notes.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  if HAVE_BONES
4
4
 
data/tasks/post_load.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: post_load.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  # This file does not define any rake tasks. It is used to load some project
4
4
  # settings if they are not defined by the user.
data/tasks/rdoc.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: rdoc.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  require 'rake/rdoctask'
4
4
 
data/tasks/setup.rb CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: setup.rb 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  require 'rubygems'
4
4
  require 'rake'
data/tasks/spec.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: spec.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  if HAVE_SPEC_RAKE_SPECTASK
4
4
  require 'spec/rake/verify_rcov'
data/tasks/svn.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: svn.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  if HAVE_SVN
4
4
 
data/tasks/test.rake CHANGED
@@ -1,4 +1,4 @@
1
- # $Id$
1
+ # $Id: test.rake 59 2009-01-23 09:10:01Z dido $
2
2
 
3
3
  require 'rake/testtask'
4
4
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emdrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dido@imperium.ph
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-22 00:00:00 +08:00
12
+ date: 2009-01-23 00:00:00 +08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -32,6 +32,11 @@ files:
32
32
  - lib/emdrb.rb
33
33
  - lib/emdrb/emdrb.rb
34
34
  - lib/emdrb/version.rb
35
+ - spec/client_spec.rb
36
+ - spec/drbserver.rb
37
+ - spec/server_spec.rb
38
+ - spec/spec_common.rb
39
+ - spec/spec_helper.rb
35
40
  - tasks/ann.rake
36
41
  - tasks/bones.rake
37
42
  - tasks/gem.rake
@@ -45,7 +50,6 @@ files:
45
50
  - tasks/spec.rake
46
51
  - tasks/svn.rake
47
52
  - tasks/test.rake
48
- - test/test_emdrb.rb
49
53
  has_rdoc: true
50
54
  homepage: http://emdrb.rubyforge.org
51
55
  post_install_message:
@@ -73,5 +77,5 @@ rubygems_version: 1.2.0
73
77
  signing_key:
74
78
  specification_version: 2
75
79
  summary: This is a distributed Ruby client and server which should work as a drop-in replacement for the standard distributed Ruby implementation available in the Ruby standard library
76
- test_files:
77
- - test/test_emdrb.rb
80
+ test_files: []
81
+
data/test/test_emdrb.rb DELETED
@@ -1,96 +0,0 @@
1
- #
2
- # Author:: Rafael R. Sevilla (mailto:dido@imperium.ph)
3
- # Copyright:: Copyright (c) 2008 Rafael R. Sevilla
4
- # Homepage:: http://emdrb.rubyforge.org/
5
- # License:: GNU General Public License / Ruby License
6
- #
7
- # $Id: test_emdrb.rb 27 2009-01-22 05:11:56Z dido $
8
- #
9
- #----------------------------------------------------------------------------
10
- #
11
- # Copyright (C) 2008 Rafael Sevilla
12
- # This file is part of EMDRb
13
- #
14
- # This program is free software; you can redistribute it and/or modify
15
- # it under the terms of either: 1) the GNU General Public License
16
- # as published by the Free Software Foundation; either version 2 of the
17
- # License, or (at your option) any later version; or 2) Ruby's License.
18
- #
19
- # See the file COPYING for complete licensing information.
20
- #----------------------------------------------------------------------------
21
- #
22
- require 'test/unit'
23
- require 'emdrb/emdrb'
24
- require 'drb'
25
-
26
- Thread.abort_on_exception = true
27
-
28
- class TestServer
29
- def identity(x)
30
- return(x)
31
- end
32
-
33
- def addtwo(x, y)
34
- return(x+y)
35
- end
36
-
37
- def sum(*vals)
38
- return(vals.inject(0) { |x,y| x + y })
39
- end
40
-
41
- def blockyield(*vals)
42
- vals.each do |x|
43
- yield x
44
- end
45
- end
46
- end
47
-
48
- class EMDRbTest < Test::Unit::TestCase
49
- def setup
50
- EMDRb.start_service("druby://:12345", TestServer.new)
51
- end
52
-
53
- def teardown
54
- EMDRb.thread.kill
55
- end
56
-
57
- def test_server
58
- o = DRbObject.new_with_uri("druby://localhost:12345")
59
- DRb.start_service
60
- assert_equal(1, o.identity(1))
61
- assert_equal(3, o.addtwo(1, 2))
62
- assert_raises(ArgumentError) do
63
- o.addtwo(1)
64
- end
65
- assert_equal(15, o.sum(1,2,3,4,5))
66
- val = 1
67
- o.blockyield(1,2,3,4,5,6,7) { |x| val *= x }
68
- assert_equal(5040, val)
69
- end
70
-
71
- def test_client
72
- o = EMDRb::DRbObject.new(nil, "druby://localhost:12345")
73
- q = Queue.new
74
- EventMachine::next_tick do
75
- o.send_async(:identity, 1).callback do |data|
76
- assert(data[0])
77
- assert_equal(1, data[1])
78
- q << data
79
- end
80
- end
81
- q.shift
82
-
83
- # EventMachine::next_tick do
84
- # val = 1
85
- # df = o.send_async(:blockyield, 1,2,3,4,5,6,7) { |x| val *= x; val }
86
- # df.callback do |data|
87
- # assert(data[0])
88
- # assert_equal(5040, data[1])
89
- # q << data
90
- # end
91
- # end
92
- # q.shift
93
-
94
- end
95
-
96
- end