bridge-ruby 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.
Files changed (67) hide show
  1. data/LICENSE +19 -0
  2. data/README.md +31 -0
  3. data/Rakefile +24 -0
  4. data/bridge-ruby.gemspec +24 -0
  5. data/doc/Bridge.html +276 -0
  6. data/doc/Bridge/Bridge.html +1874 -0
  7. data/doc/Bridge/Bridge/SystemService.html +396 -0
  8. data/doc/Bridge/Client.html +271 -0
  9. data/doc/Bridge/Connection.html +1180 -0
  10. data/doc/Bridge/Connection/SockBuffer.html +322 -0
  11. data/doc/Bridge/Reference.html +605 -0
  12. data/doc/Bridge/Serializer.html +405 -0
  13. data/doc/Bridge/Serializer/Callback.html +498 -0
  14. data/doc/Bridge/Tcp.html +657 -0
  15. data/doc/Bridge/Util.html +643 -0
  16. data/doc/Bridge/Util/CallbackReference.html +557 -0
  17. data/doc/OpenSSL/X509/Certificate.html +275 -0
  18. data/doc/SSLCertificateVerification.html +446 -0
  19. data/doc/_index.html +239 -0
  20. data/doc/class_list.html +53 -0
  21. data/doc/css/common.css +1 -0
  22. data/doc/css/full_list.css +57 -0
  23. data/doc/css/style.css +328 -0
  24. data/doc/file.README.html +106 -0
  25. data/doc/file_list.html +55 -0
  26. data/doc/frames.html +28 -0
  27. data/doc/index.html +106 -0
  28. data/doc/js/app.js +214 -0
  29. data/doc/js/full_list.js +173 -0
  30. data/doc/js/jquery.js +4 -0
  31. data/doc/method_list.html +772 -0
  32. data/doc/top-level-namespace.html +112 -0
  33. data/examples/channels/client-writeable.rb +24 -0
  34. data/examples/channels/client.rb +23 -0
  35. data/examples/channels/server.rb +24 -0
  36. data/examples/chat/chatclient.rb +21 -0
  37. data/examples/chat/chatserver.rb +24 -0
  38. data/examples/client-context/client.rb +21 -0
  39. data/examples/client-context/server.rb +25 -0
  40. data/examples/secure/example.rb +8 -0
  41. data/examples/simple/channels.rb +47 -0
  42. data/examples/simple/services.rb +41 -0
  43. data/include/ssl/cacert.pem +3331 -0
  44. data/lib/bridge-ruby.rb +441 -0
  45. data/lib/client.rb +14 -0
  46. data/lib/connection.rb +162 -0
  47. data/lib/reference.rb +49 -0
  48. data/lib/serializer.rb +104 -0
  49. data/lib/ssl_utils.rb +68 -0
  50. data/lib/tcp.rb +73 -0
  51. data/lib/util.rb +101 -0
  52. data/lib/version.rb +3 -0
  53. data/rakelib/package.rake +4 -0
  54. data/rakelib/test.rake +8 -0
  55. data/test/regression/reconnect.rb +48 -0
  56. data/test/regression/rpc.rb +39 -0
  57. data/test/regression/test.rb +58 -0
  58. data/test/unit/bridge_dummy.rb +26 -0
  59. data/test/unit/connection_dummy.rb +21 -0
  60. data/test/unit/reference_dummy.rb +11 -0
  61. data/test/unit/tcp_dummy.rb +12 -0
  62. data/test/unit/test.rb +20 -0
  63. data/test/unit/test_reference.rb +30 -0
  64. data/test/unit/test_serializer.rb +109 -0
  65. data/test/unit/test_tcp.rb +51 -0
  66. data/test/unit/test_util.rb +59 -0
  67. metadata +162 -0
@@ -0,0 +1,441 @@
1
+ require 'util.rb'
2
+ require 'serializer.rb'
3
+ require 'connection.rb'
4
+ require 'reference.rb'
5
+ require 'client.rb'
6
+
7
+ require 'eventmachine'
8
+
9
+ # == Bridge
10
+ #
11
+ # Bridge is a cross-language and platform framework for realtime
12
+ # communication and RPC.
13
+ #
14
+ # The Bridge ruby client is fully featured and has full compatibility with
15
+ # all other Bridge clients.
16
+ #
17
+ # Bridge::Bridge
18
+
19
+ module Bridge
20
+
21
+ @@instance = nil
22
+
23
+ def self.instance
24
+ @@instance
25
+ end
26
+
27
+ def self.instance= i
28
+ @@instance = i
29
+ end
30
+
31
+
32
+ class Bridge
33
+
34
+ attr_accessor :context, :options, :connection, :store, :is_ready #:nodoc:
35
+
36
+ # :call-seq:
37
+ # new(options={})
38
+ #
39
+ # Create an instance of the Bridge object. This object will be used for
40
+ # Bridge interactions.
41
+ #
42
+ # Bridge#connect must be called to connect to the Bridge server.
43
+ #
44
+ # === Attributes
45
+ #
46
+ # +options+:: Optional hash of arguments specified below.
47
+ #
48
+ # === Options
49
+ #
50
+ # Options hash passed to initialize to modify Bridge behavior.
51
+ #
52
+ # <tt>:redirector => 'http://redirector.getbridge.com'</tt>:: Address to
53
+ # specify Bridge redirector server. The redirector server helps route
54
+ # the client to the appropriate Bridge server,
55
+
56
+ # <tt>:reconnect => true</tt>:: Enable automatic reconnection to Bridge
57
+ # server.
58
+
59
+ # <tt>:log => 2</tt>:: An integer specifying log level. 3 => Log all,
60
+ # 2 => Log warnings, 1 => Log errors, 0 => No logging output.
61
+
62
+ # <tt>:host => nil</tt>:: The hostname of the Bridge server to connect
63
+ # to. Overrides +:redirector+ when both +:host+ and +:port+ are
64
+ # specified,
65
+
66
+ # <tt>:port => nil</tt>:: An integer specifying the port of the Bridge
67
+ # server to connect to. Overrides +:redirector+ when both +:host+ and
68
+ # +:port+ are specified.
69
+ def initialize(options = {})
70
+
71
+ # Set default options
72
+ @options = {
73
+ :redirector => 'http://redirector.getbridge.com',
74
+ :secure_redirector => 'https://redirector.getbridge.com',
75
+ :secure => false,
76
+ :reconnect => true,
77
+ :log => 2, # 0 for no output
78
+ }
79
+
80
+ @options = @options.merge(options)
81
+
82
+ if @options[:secure]
83
+ @options[:redirector] = @options[:secure_redirector]
84
+ end
85
+
86
+ Util.set_log_level(@options[:log])
87
+
88
+ @store = {}
89
+ # Initialize system service call
90
+ @store['system'] = SystemService.new(self)
91
+
92
+ # Indicates whether server is connected and handshaken
93
+ @is_ready = false
94
+
95
+ # Create connection object
96
+ @connection = Connection.new(self)
97
+
98
+ # Store event handlers
99
+ @events = {}
100
+
101
+ @context = nil
102
+ end
103
+
104
+ def execute address, args #:nodoc:
105
+ # Retrieve stored handler
106
+ obj = @store[address[2]]
107
+ # Retrieve function in handler
108
+ func = obj.method(address[3])
109
+ if func
110
+ last = args.last
111
+ # If last argument is callable and function arity is one less than
112
+ # args length, pass in as block
113
+ if (last.is_a? Proc) and func.arity == args.length - 1
114
+ args.pop
115
+ func.call *args do |*args, &blk|
116
+ args << blk if blk
117
+ last.call *args
118
+ end
119
+ else
120
+ begin
121
+ func.call *args
122
+ rescue StandardError => err
123
+ Util.error err
124
+ Util.error "Exception while calling #{address[3]}(#{args})"
125
+ end
126
+ end
127
+ else
128
+ Util.warn "Could not find object to handle, #{address}"
129
+ end
130
+ end
131
+
132
+ def store_object handler, ops #:nodoc:
133
+ # Generate random id for callback being stored
134
+ name = Util.generate_guid
135
+ @store[name] = handler
136
+ # Return reference to stored callback
137
+ Reference.new(self, ['client', @connection.client_id, name], ops)
138
+ end
139
+
140
+ # :call-seq:
141
+ # on(name) { |*args| block }
142
+ #
143
+ # Adds the given block as a handler for the event specified by
144
+ # <tt>name</tt>. Calling multiple times will result in multiple
145
+ # handlers being attached to the event
146
+ #
147
+ # === Attributes
148
+ #
149
+ # +name+:: The name of the event for the given block to listen to
150
+ #
151
+ # === Events
152
+ #
153
+ # List of events Bridge emits
154
+ #
155
+ # <tt>'ready' ()</tt>:: Bridge is connected and ready. Not emitted on
156
+ # reconnects
157
+ # <tt>'remoteError' (error_message)</tt>:: A remote error has occurred
158
+ # in Bridge. The error message is provided as a parameter
159
+ def on name, &fn
160
+ if !@events.key? name
161
+ @events[name] = [];
162
+ end
163
+ @events[name] << fn
164
+ end
165
+
166
+ def emit name, args=[] #:nodoc:
167
+ if @events.key? name
168
+ @events[name].each do |fn|
169
+ fn.call *args
170
+ end
171
+ end
172
+ end
173
+
174
+ def send args, destination #:nodoc:
175
+ @connection.send_command(:SEND,
176
+ { :args => Serializer.serialize(self, args),
177
+ :destination => destination })
178
+ end
179
+
180
+ # :call-seq:
181
+ # publish_service(name, handler) { |name| block }
182
+ #
183
+ # Publishes a ruby object or module as a Bridge service with the given
184
+ # name.
185
+ #
186
+ # If a block is given, calls the given block with the name of the
187
+ # published service upon publish success
188
+ #
189
+ # === Attributes
190
+ #
191
+ # +name+:: The name of the Bridge service the handler will be published
192
+ # with
193
+ # +handler+:: A ruby object or module to publish
194
+ #
195
+ def publish_service name, handler, &callback
196
+ if name == 'system'
197
+ Util.error("Invalid service name: #{name}")
198
+ else
199
+ @store[name] = handler
200
+ @connection.send_command(
201
+ :JOINWORKERPOOL,
202
+ { :name => name,
203
+ :callback => Serializer.serialize(self, callback)}
204
+ )
205
+ end
206
+ end
207
+
208
+ # :call-seq:
209
+ # store_service(name, handler)
210
+ #
211
+ # Stores a Ruby object or module as a Bridge service with the given
212
+ # name.
213
+ #
214
+ # === Attributes
215
+ #
216
+ # +name+:: The name of the Bridge service the handler will be stored
217
+ # under
218
+ # +handler+:: A Ruby object or module to store
219
+ #
220
+ def store_service name, handler
221
+ if name == 'system'
222
+ Util.error("Invalid service name: #{name}")
223
+ else
224
+ @store[name] = handler
225
+ end
226
+ end
227
+
228
+ # :call-seq:
229
+ # unpublish_service(name, handler) { |name| block }
230
+ #
231
+ # Stops publishing a ruby object or module as a Bridge service with the
232
+ # given name.
233
+ #
234
+ # If a block is given, calls the given block with the name of the
235
+ # unpublished service.
236
+ #
237
+ # === Attributes
238
+ #
239
+ # +name+:: The name of the Bridge service that will no longer be
240
+ # published
241
+ #
242
+ def unpublish_service name, &callback
243
+ if name == 'system'
244
+ Util.error("Invalid service name: #{name}")
245
+ else
246
+ @connection.send_command(
247
+ :LEAVEWORKERPOOL,
248
+ { :name => name,
249
+ :callback => Serializer.serialize(self, callback)}
250
+ )
251
+ end
252
+ end
253
+
254
+ # :call-seq:
255
+ # get_service(name) -> service
256
+ # get_service(name) { |service, name| block }
257
+ #
258
+ # Retrives a service published to Bridge with the given name.
259
+ #
260
+ # If multiple Bridge clients have a published a service, the service is
261
+ # retrieved from one of the publishers round-robin.
262
+ #
263
+ # Note that if the requested service does not exist, an object is still
264
+ # returned, however attempts to make method calls on the object will
265
+ # result in a remote error.
266
+ #
267
+ # Returns the requested service.
268
+ #
269
+ # If a block is given, calls the given block with the requested service
270
+ # and service name.
271
+ #
272
+ # === Attributes
273
+ #
274
+ # +name+:: The name of the Bridge service being requested
275
+ #
276
+ def get_service name, &callback
277
+ ref = Reference.new(self, ['named', name, name])
278
+ callback.call(ref, name) if callback
279
+ return ref
280
+ end
281
+
282
+ def get_client id
283
+ return Client.new(self, id)
284
+ end
285
+
286
+ # :call-seq:
287
+ # get_channel(name) -> channel
288
+ # get_channel(name) { |channel, name| block }
289
+ #
290
+ # Retrives a channel from Bridge with the given name.
291
+ #
292
+ # Calling a method on the channel object will result in the given
293
+ # method being executed on all clients that have been joined to the
294
+ # channel.
295
+ #
296
+ # Note that if the requested channel does not exist or is empty, an
297
+ # object is still returned, however attempts to make method calls on
298
+ # the object will result in a remote error.
299
+ #
300
+ # Returns the requested channel.
301
+ #
302
+ # If a block is given, calls the given block with the requested channel
303
+ # and channel name.
304
+ #
305
+ # === Attributes
306
+ #
307
+ # +name+:: The name of the Bridge channel being requested
308
+ #
309
+ def get_channel name, &callback
310
+ # Send GETCHANNEL command in order to establih link for channel if
311
+ # client is not member
312
+ @connection.send_command(:GETCHANNEL, {:name => name})
313
+ ref = Reference.new(self, ['channel', name, "channel:#{name}"])
314
+ callback.call(ref, name) if callback
315
+ return ref
316
+ end
317
+
318
+ # :call-seq:
319
+ # join_channel(name, handler) { |channel, name| block }
320
+ #
321
+ # Provides a remote object, ruby object or module as a receiver for
322
+ # methods calls on a Bridge channel.
323
+ #
324
+ # The given handler can be a remote object, in which case the Bridge
325
+ # client that created the remote object will be joined to the
326
+ # channel. Method calls to the channel will be not be proxied through
327
+ # this client but go directly to the source of the remote object.
328
+ #
329
+ # If a block is given, calls the given block with the joined channel
330
+ # and channel name.
331
+ #
332
+ # === Attributes
333
+ #
334
+ # +name+:: The name of the Bridge channel the handler will recieve
335
+ # methods calls for.
336
+
337
+ # +handler+:: A remote object, ruby object or module to handle method
338
+ # calls from the channel
339
+ #
340
+ # +writeable+:: Whether the handler's creator may write to the channel
341
+ def join_channel name, handler, writeable = true, &callback
342
+ @connection.send_command(
343
+ :JOINCHANNEL,
344
+ { :name => name,
345
+ :handler => Serializer.serialize(self, handler),
346
+ :callback => Serializer.serialize(self, callback),
347
+ :writeable => writeable
348
+ }
349
+ )
350
+ end
351
+
352
+ # :call-seq:
353
+ # leave_channel(name, handler)
354
+ # leave_channel(name, handler) { |name| block }
355
+ #
356
+ # Leaves a Bridge channel with the given name and handler object.
357
+ #
358
+ # The given handler can be a remote object, in which case the Bridge
359
+ # client that created the remote object will be removed from the
360
+ # channel.
361
+ #
362
+ # If a block is given, calls the given block with the name of the
363
+ # channel left.
364
+ #
365
+ # === Attributes
366
+ #
367
+ # +name+:: The name of the Bridge channel to leave
368
+ # +handler+:: A remote object, ruby object or module that was used to
369
+ # handle moethod calls from the channel
370
+ #
371
+ def leave_channel channel, handler, &callback
372
+ @connection.send_command(
373
+ :LEAVECHANNEL,
374
+ { :name => name,
375
+ :handler => Serializer.serialize(self, handler),
376
+ :callback => Serializer.serialize(self, callback)}
377
+ )
378
+ end
379
+
380
+ # :call-seq:
381
+ # ready { block }
382
+ #
383
+ # Calls the given block when Bridge is connected and ready.
384
+ # Calls the given block immediately if Bridge is already ready.
385
+ #
386
+ def ready &callback
387
+ if @is_ready
388
+ callback.call
389
+ else
390
+ on 'ready', &callback
391
+ end
392
+ end
393
+
394
+ # :call-seq:
395
+ # connect { block }
396
+ #
397
+ # Starts the connection to the Bridge server.
398
+ #
399
+ # If a block is given, calls the given block when Bridge is connected
400
+ # and ready.
401
+ #
402
+ def connect &callback
403
+ self.ready &callback if callback
404
+ @connection.start
405
+ return self
406
+ end
407
+
408
+ # These are internal system functions, which should only be called by
409
+ # the Erlang gateway.
410
+ class SystemService #:nodoc:
411
+ def initialize bridge
412
+ @bridge = bridge
413
+ end
414
+
415
+ def hookChannelHandler name, handler, callback = nil
416
+ # Store under channel name
417
+ @bridge.store["channel:#{name}"] = handler
418
+ # Send callback with reference to channel and handler operations
419
+ callback.call(Reference.new(@bridge,
420
+ ['channel', name, "channel:#{name}"],
421
+ Util.find_ops(handler)), name) if callback
422
+ end
423
+
424
+ def getService name, callback
425
+ if @bridge.store.key? name
426
+ callback.call(@bridge.store[name], name)
427
+ else
428
+ callback.call(nil, name)
429
+ end
430
+ end
431
+
432
+ def remoteError msg
433
+ Util.warn msg
434
+ @bridge.emit 'remote_error', [msg]
435
+ end
436
+ end
437
+
438
+
439
+ end
440
+
441
+ end
@@ -0,0 +1,14 @@
1
+ require 'serializer.rb'
2
+ require 'reference.rb'
3
+
4
+ module Bridge
5
+ class Client #:nodoc: all
6
+ def initialize(bridge, id)
7
+ @bridge, @id = bridge, id
8
+ end
9
+
10
+ def get_service(svc)
11
+ Reference.new(@bridge, ['client', @id, svc])
12
+ end
13
+ end
14
+ end