bridge-ruby 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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