wamp_client 0.0.1

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.
@@ -0,0 +1,776 @@
1
+ require 'wamp_client/transport'
2
+ require 'wamp_client/message'
3
+ require 'wamp_client/check'
4
+ require 'wamp_client/version'
5
+
6
+ module WampClient
7
+
8
+ WAMP_FEATURES = {
9
+ caller: {
10
+ features: {
11
+ caller_identification: true,
12
+ ##call_timeout: true,
13
+ ##call_canceling: true,
14
+ progressive_call_results: true
15
+ }
16
+ },
17
+ callee: {
18
+ features: {
19
+ caller_identification: true,
20
+ ##call_trustlevels: true,
21
+ pattern_based_registration: true,
22
+ shared_registration: true,
23
+ ##call_timeout: true,
24
+ ##call_canceling: true,
25
+ # progressive_call_results: true,
26
+ registration_revocation: true
27
+ }
28
+ },
29
+ publisher: {
30
+ features: {
31
+ publisher_identification: true,
32
+ subscriber_blackwhite_listing: true,
33
+ publisher_exclusion: true
34
+ }
35
+ },
36
+ subscriber: {
37
+ features: {
38
+ publisher_identification: true,
39
+ ##publication_trustlevels: true,
40
+ pattern_based_subscription: true,
41
+ subscription_revocation: true
42
+ ##event_history: true,
43
+ }
44
+ }
45
+ }
46
+
47
+ class CallResult
48
+ attr_accessor :args, :kwargs
49
+
50
+ def initialize(args=nil, kwargs=nil)
51
+ self.args = args
52
+ self.kwargs = kwargs
53
+ end
54
+ end
55
+
56
+ class Subscription
57
+ attr_accessor :topic, :handler, :options, :session, :id
58
+
59
+ def initialize(topic, handler, options, session, id)
60
+ self.topic = topic
61
+ self.handler = handler
62
+ self.options = options
63
+ self.session = session
64
+ self.id = id
65
+ end
66
+
67
+ def unsubscribe
68
+ self.session.unsubscribe(self)
69
+ end
70
+
71
+ end
72
+
73
+ class Registration
74
+ attr_accessor :procedure, :handler, :options, :session, :id
75
+
76
+ def initialize(procedure, handler, options, session, id)
77
+ self.procedure = procedure
78
+ self.handler = handler
79
+ self.options = options
80
+ self.session = session
81
+ self.id = id
82
+ end
83
+
84
+ def unregister
85
+ self.session.unregister(self)
86
+ end
87
+
88
+ end
89
+
90
+
91
+ class Session
92
+ include WampClient::Check
93
+
94
+ # on_join callback is called when the session joins the router. It has the following parameters
95
+ # @param details [Hash] Object containing information about the joined session
96
+ @on_join
97
+ def on_join(&on_join)
98
+ @on_join = on_join
99
+ end
100
+
101
+ # on_leave callback is called when the session leaves the router. It has the following attributes
102
+ # @param reason [String] The reason the session left the router
103
+ # @param details [Hash] Object containing information about the left session
104
+ @on_leave
105
+ def on_leave(&on_leave)
106
+ @on_leave = on_leave
107
+ end
108
+
109
+ # on_challenge callback is called when an authentication challenge is received from the router. It has the
110
+ # following attributes
111
+ # @param authmethod [String] The type of auth being requested
112
+ # @param extra [Hash] Hash containing additional information
113
+ # @return signature, extras
114
+ @on_challenge
115
+ def on_challenge(&on_challenge)
116
+ @on_challenge = on_challenge
117
+ end
118
+
119
+ attr_accessor :id, :realm, :transport, :verbose, :options
120
+
121
+ # Private attributes
122
+ attr_accessor :_goodbye_sent, :_requests, :_subscriptions, :_registrations
123
+
124
+ # Constructor
125
+ # @param transport [WampClient::Transport::Base] The transport that the session will use
126
+ # @param options [Hash] Hash containing different session options
127
+ # @option options [String] :authid The authentication ID
128
+ # @option options [Array] :authmethods Different auth methods that this client supports
129
+ def initialize(transport, options={})
130
+
131
+ # Parameters
132
+ self.id = nil
133
+ self.realm = nil
134
+ self.verbose = false
135
+ self.options = options || {}
136
+
137
+ # Outstanding Requests
138
+ self._requests = {
139
+ publish: {},
140
+ subscribe: {},
141
+ unsubscribe: {},
142
+ call: {},
143
+ register: {},
144
+ unregister: {}
145
+ }
146
+
147
+ # Init Subs and Regs in place
148
+ self._subscriptions = {}
149
+ self._registrations = {}
150
+
151
+ # Setup Transport
152
+ self.transport = transport
153
+ self.transport.on_message do |msg|
154
+ self._receive_message(msg)
155
+ end
156
+
157
+ # Other parameters
158
+ self._goodbye_sent = false
159
+
160
+ # Setup session callbacks
161
+ @on_join = nil
162
+ @on_leave = nil
163
+
164
+ end
165
+
166
+ # Returns 'true' if the session is open
167
+ def is_open?
168
+ !self.id.nil?
169
+ end
170
+
171
+ # Joins the WAMP Router
172
+ # @param realm [String] The name of the realm
173
+ def join(realm)
174
+ if is_open?
175
+ raise RuntimeError, "Session must be closed to call 'join'"
176
+ end
177
+
178
+ self.class.check_uri('realm', realm)
179
+
180
+ self.realm = realm
181
+
182
+ details = {}
183
+ details[:roles] = WAMP_FEATURES
184
+ details[:agent] = "Ruby-WampClient-#{WampClient::VERSION}"
185
+ details[:authid] = self.options[:authid]
186
+ details[:authmethods] = self.options[:authmethods]
187
+
188
+ # Send Hello message
189
+ hello = WampClient::Message::Hello.new(realm, details)
190
+ self._send_message(hello)
191
+ end
192
+
193
+ # Leaves the WAMP Router
194
+ # @param reason [String] URI signalling the reason for leaving
195
+ def leave(reason='wamp.close.normal', message='user initiated')
196
+ unless is_open?
197
+ raise RuntimeError, "Session must be opened to call 'leave'"
198
+ end
199
+
200
+ self.class.check_uri('reason', reason, true)
201
+ self.class.check_string('message', message, true)
202
+
203
+ details = {}
204
+ details[:message] = message
205
+
206
+ # Send Goodbye message
207
+ goodbye = WampClient::Message::Goodbye.new(details, reason)
208
+ self._send_message(goodbye)
209
+ self._goodbye_sent = true
210
+ end
211
+
212
+ # Generates an ID according to the specification (Section 5.1.2)
213
+ def _generate_id
214
+ rand(0..9007199254740992)
215
+ end
216
+
217
+ # Converts and error message to a hash
218
+ # @param msg [WampClient::Message::Error]
219
+ def _error_to_hash(msg)
220
+ {
221
+ error: msg.error,
222
+ args: msg.arguments,
223
+ kwargs: msg.argumentskw
224
+ }
225
+ end
226
+
227
+ # Sends a message
228
+ # @param msg [WampClient::Message::Base]
229
+ def _send_message(msg)
230
+ puts 'TX: ' + msg.to_s if self.verbose
231
+ self.transport.send_message(msg.payload)
232
+ end
233
+
234
+ # Processes received messages
235
+ # @param msg [Array]
236
+ def _receive_message(msg)
237
+
238
+ message = WampClient::Message::Base.parse(msg)
239
+
240
+ puts 'RX: ' + message.to_s if self.verbose
241
+
242
+ # WAMP Session is not open
243
+ if self.id.nil?
244
+
245
+ # Parse the welcome message
246
+ if message.is_a? WampClient::Message::Welcome
247
+ self.id = message.session
248
+ @on_join.call(message.details) unless @on_join.nil?
249
+ elsif message.is_a? WampClient::Message::Challenge
250
+
251
+ if @on_challenge
252
+ signature, extra = @on_challenge.call(message.authmethod, message.extra)
253
+ else
254
+ signature = nil
255
+ extra = nil
256
+ end
257
+
258
+ signature ||= ''
259
+ extra ||= {}
260
+
261
+ authenticate = WampClient::Message::Authenticate.new(signature, extra)
262
+ self._send_message(authenticate)
263
+
264
+ elsif message.is_a? WampClient::Message::Abort
265
+ @on_leave.call(message.reason, message.details) unless @on_leave.nil?
266
+ end
267
+
268
+ # Wamp Session is open
269
+ else
270
+
271
+ # If goodbye, close the session
272
+ if message.is_a? WampClient::Message::Goodbye
273
+
274
+ # If we didn't send the goodbye, respond
275
+ unless self._goodbye_sent
276
+ goodbye = WampClient::Message::Goodbye.new({}, 'wamp.error.goodbye_and_out')
277
+ self._send_message(goodbye)
278
+ end
279
+
280
+ # Close out session
281
+ self.id = nil
282
+ self.realm = nil
283
+ self._goodbye_sent = false
284
+ @on_leave.call(message.reason, message.details) unless @on_leave.nil?
285
+
286
+ else
287
+
288
+ # Process Errors
289
+ if message.is_a? WampClient::Message::Error
290
+ if message.request_type == WampClient::Message::Types::SUBSCRIBE
291
+ self._process_SUBSCRIBE_error(message)
292
+ elsif message.request_type == WampClient::Message::Types::UNSUBSCRIBE
293
+ self._process_UNSUBSCRIBE_error(message)
294
+ elsif message.request_type == WampClient::Message::Types::PUBLISH
295
+ self._process_PUBLISH_error(message)
296
+ elsif message.request_type == WampClient::Message::Types::REGISTER
297
+ self._process_REGISTER_error(message)
298
+ elsif message.request_type == WampClient::Message::Types::UNREGISTER
299
+ self._process_UNREGISTER_error(message)
300
+ elsif message.request_type == WampClient::Message::Types::CALL
301
+ self._process_CALL_error(message)
302
+ else
303
+ # TODO: Some Error?? Not Implemented yet
304
+ end
305
+
306
+ # Process Messages
307
+ else
308
+ if message.is_a? WampClient::Message::Subscribed
309
+ self._process_SUBSCRIBED(message)
310
+ elsif message.is_a? WampClient::Message::Unsubscribed
311
+ self._process_UNSUBSCRIBED(message)
312
+ elsif message.is_a? WampClient::Message::Published
313
+ self._process_PUBLISHED(message)
314
+ elsif message.is_a? WampClient::Message::Event
315
+ self._process_EVENT(message)
316
+ elsif message.is_a? WampClient::Message::Registered
317
+ self._process_REGISTERED(message)
318
+ elsif message.is_a? WampClient::Message::Unregistered
319
+ self._process_UNREGISTERED(message)
320
+ elsif message.is_a? WampClient::Message::Invocation
321
+ self._process_INVOCATION(message)
322
+ elsif message.is_a? WampClient::Message::Result
323
+ self._process_RESULT(message)
324
+ else
325
+ # TODO: Some Error?? Not Implemented yet
326
+ end
327
+ end
328
+
329
+ end
330
+ end
331
+
332
+ end
333
+
334
+ #region Subscribe Logic
335
+
336
+ # Subscribes to a topic
337
+ # @param topic [String] The topic to subscribe to
338
+ # @param handler [lambda] The handler(args, kwargs, details) when an event is received
339
+ # @param options [Hash] The options for the subscription
340
+ # @param callback [block] The callback(subscription, error) called to signal if the subscription was a success or not
341
+ def subscribe(topic, handler, options={}, &callback)
342
+ unless is_open?
343
+ raise RuntimeError, "Session must be open to call 'subscribe'"
344
+ end
345
+
346
+ self.class.check_uri('topic', topic)
347
+ self.class.check_dict('options', options)
348
+ self.class.check_nil('handler', handler, false)
349
+
350
+ # Create a new subscribe request
351
+ request = self._generate_id
352
+ self._requests[:subscribe][request] = {t: topic, h: handler, o: options, c: callback}
353
+
354
+ # Send the message
355
+ subscribe = WampClient::Message::Subscribe.new(request, options, topic)
356
+ self._send_message(subscribe)
357
+ end
358
+
359
+ # Processes the response to a subscribe request
360
+ # @param msg [WampClient::Message::Subscribed] The response from the subscribe
361
+ def _process_SUBSCRIBED(msg)
362
+
363
+ # Remove the pending subscription, add it to the registered ones, and inform the caller
364
+ s = self._requests[:subscribe].delete(msg.subscribe_request)
365
+ if s
366
+
367
+ details = {}
368
+ details[:topic] = s[:t] unless details[:topic]
369
+ details[:type] = 'subscribe'
370
+
371
+ n_s = Subscription.new(s[:t], s[:h], s[:o], self, msg.subscription)
372
+ self._subscriptions[msg.subscription] = n_s
373
+ c = s[:c]
374
+ c.call(n_s, nil, details) if c
375
+ end
376
+
377
+ end
378
+
379
+ # Processes an error from a request
380
+ # @param msg [WampClient::Message::Error] The response from the subscribe
381
+ def _process_SUBSCRIBE_error(msg)
382
+
383
+ # Remove the pending subscription and inform the caller of the failure
384
+ s = self._requests[:subscribe].delete(msg.request_request)
385
+ if s
386
+
387
+ details = msg.details || {}
388
+ details[:topic] = s[:t] unless details[:topic]
389
+ details[:type] = 'subscribe'
390
+
391
+ c = s[:c]
392
+ c.call(nil, self._error_to_hash(msg), details) if c
393
+ end
394
+
395
+ end
396
+
397
+ # Processes and event from the broker
398
+ # @param msg [WampClient::Message::Event] An event that was published
399
+ def _process_EVENT(msg)
400
+
401
+ args = msg.publish_arguments || []
402
+ kwargs = msg.publish_argumentskw || {}
403
+
404
+ s = self._subscriptions[msg.subscribed_subscription]
405
+ if s
406
+ details = msg.details || {}
407
+ details[:publication] = msg.published_publication
408
+
409
+ h = s.handler
410
+ h.call(args, kwargs, details) if h
411
+ end
412
+
413
+ end
414
+
415
+ #endregion
416
+
417
+ #region Unsubscribe Logic
418
+
419
+ # Unsubscribes from a subscription
420
+ # @param subscription [Subscription] The subscription object from when the subscription was created
421
+ # @param callback [block] The callback(subscription, error, details) called to signal if the subscription was a success or not
422
+ def unsubscribe(subscription, &callback)
423
+ unless is_open?
424
+ raise RuntimeError, "Session must be open to call 'unsubscribe'"
425
+ end
426
+
427
+ self.class.check_nil('subscription', subscription, false)
428
+
429
+ # Create a new unsubscribe request
430
+ request = self._generate_id
431
+ self._requests[:unsubscribe][request] = { s: subscription, c: callback }
432
+
433
+ # Send the message
434
+ unsubscribe = WampClient::Message::Unsubscribe.new(request, subscription.id)
435
+ self._send_message(unsubscribe)
436
+ end
437
+
438
+ # Processes the response to a unsubscribe request
439
+ # @param msg [WampClient::Message::Unsubscribed] The response from the unsubscribe
440
+ def _process_UNSUBSCRIBED(msg)
441
+
442
+ # Remove the pending unsubscription, add it to the registered ones, and inform the caller
443
+ s = self._requests[:unsubscribe].delete(msg.unsubscribe_request)
444
+ if s
445
+ n_s = s[:s]
446
+ self._subscriptions.delete(n_s.id)
447
+
448
+ details = {}
449
+ details[:topic] = s[:s].topic
450
+ details[:type] = 'unsubscribe'
451
+
452
+ c = s[:c]
453
+ c.call(n_s, nil, details) if c
454
+ end
455
+
456
+ end
457
+
458
+
459
+ # Processes an error from a request
460
+ # @param msg [WampClient::Message::Error] The response from the subscribe
461
+ def _process_UNSUBSCRIBE_error(msg)
462
+
463
+ # Remove the pending subscription and inform the caller of the failure
464
+ s = self._requests[:unsubscribe].delete(msg.request_request)
465
+ if s
466
+
467
+ details = msg.details || {}
468
+ details[:topic] = s[:s].topic unless details[:topic]
469
+ details[:type] = 'unsubscribe'
470
+
471
+ c = s[:c]
472
+ c.call(nil, self._error_to_hash(msg), details) if c
473
+ end
474
+
475
+ end
476
+
477
+ #endregion
478
+
479
+ #region Publish Logic
480
+
481
+ # Publishes and event to a topic
482
+ # @param topic [String] The topic to publish the event to
483
+ # @param args [Array] The arguments
484
+ # @param kwargs [Hash] The keyword arguments
485
+ # @param options [Hash] The options for the publish
486
+ # @param callback [block] The callback(publish, error, details) called to signal if the publish was a success or not
487
+ def publish(topic, args=nil, kwargs=nil, options={}, &callback)
488
+ unless is_open?
489
+ raise RuntimeError, "Session must be open to call 'publish'"
490
+ end
491
+
492
+ self.class.check_uri('topic', topic)
493
+ self.class.check_dict('options', options)
494
+ self.class.check_list('args', args, true)
495
+ self.class.check_dict('kwargs', kwargs, true)
496
+
497
+ # Create a new publish request
498
+ request = self._generate_id
499
+ self._requests[:publish][request] = {t: topic, a: args, k: kwargs, o: options, c: callback} if options[:acknowledge]
500
+
501
+ # Send the message
502
+ publish = WampClient::Message::Publish.new(request, options, topic, args, kwargs)
503
+ self._send_message(publish)
504
+ end
505
+
506
+ # Processes the response to a publish request
507
+ # @param msg [WampClient::Message::Published] The response from the subscribe
508
+ def _process_PUBLISHED(msg)
509
+
510
+ # Remove the pending publish and alert the callback
511
+ p = self._requests[:publish].delete(msg.publish_request)
512
+ if p
513
+
514
+ details = {}
515
+ details[:topic] = p[:t]
516
+ details[:type] = 'publish'
517
+ details[:publication] = msg.publication
518
+
519
+ c = p[:c]
520
+ c.call(p, nil, details) if c
521
+ end
522
+
523
+ end
524
+
525
+ # Processes an error from a publish request
526
+ # @param msg [WampClient::Message::Error] The response from the subscribe
527
+ def _process_PUBLISH_error(msg)
528
+
529
+ # Remove the pending publish and inform the caller of the failure
530
+ s = self._requests[:publish].delete(msg.request_request)
531
+ if s
532
+
533
+ details = msg.details || {}
534
+ details[:topic] = s[:t] unless details[:topic]
535
+ details[:type] = 'publish'
536
+
537
+ c = s[:c]
538
+ c.call(nil, self._error_to_hash(msg), details) if c
539
+ end
540
+
541
+ end
542
+
543
+ #endregion
544
+
545
+ #region Register Logic
546
+
547
+ # Register to a procedure
548
+ # @param procedure [String] The procedure to register for
549
+ # @param handler [lambda] The handler(args, kwargs, details) when a invocation is received
550
+ # @param options [Hash] The options for the registration
551
+ # @param callback [block] The callback(registration, error, details) called to signal if the registration was a success or not
552
+ def register(procedure, handler, options={}, &callback)
553
+ unless is_open?
554
+ raise RuntimeError, "Session must be open to call 'register'"
555
+ end
556
+
557
+ self.class.check_uri('procedure', procedure)
558
+ self.class.check_dict('options', options)
559
+ self.class.check_nil('handler', handler, false)
560
+
561
+ # Create a new registration request
562
+ request = self._generate_id
563
+ self._requests[:register][request] = {p: procedure, h: handler, o: options, c: callback}
564
+
565
+ # Send the message
566
+ register = WampClient::Message::Register.new(request, options, procedure)
567
+ self._send_message(register)
568
+ end
569
+
570
+ # Processes the response to a register request
571
+ # @param msg [WampClient::Message::Registered] The response from the subscribe
572
+ def _process_REGISTERED(msg)
573
+
574
+ # Remove the pending subscription, add it to the registered ones, and inform the caller
575
+ r = self._requests[:register].delete(msg.register_request)
576
+ if r
577
+ n_r = Registration.new(r[:p], r[:h], r[:o], self, msg.registration)
578
+ self._registrations[msg.registration] = n_r
579
+
580
+ details = {}
581
+ details[:procedure] = r[:p]
582
+ details[:type] = 'register'
583
+
584
+ c = r[:c]
585
+ c.call(n_r, nil, details) if c
586
+ end
587
+
588
+ end
589
+
590
+ # Processes an error from a request
591
+ # @param msg [WampClient::Message::Error] The response from the register
592
+ def _process_REGISTER_error(msg)
593
+
594
+ # Remove the pending registration and inform the caller of the failure
595
+ r = self._requests[:register].delete(msg.request_request)
596
+ if r
597
+
598
+ details = msg.details || {}
599
+ details[:procedure] = r[:p] unless details[:procedure]
600
+ details[:type] = 'register'
601
+
602
+ c = r[:c]
603
+ c.call(nil, self._error_to_hash(msg), details) if c
604
+ end
605
+
606
+ end
607
+
608
+ # Processes and event from the broker
609
+ # @param msg [WampClient::Message::Invocation] An procedure that was called
610
+ def _process_INVOCATION(msg)
611
+
612
+ request = msg.request
613
+ args = msg.call_arguments || []
614
+ kwargs = msg.call_argumentskw || {}
615
+
616
+ details = msg.details || {}
617
+ details[:request] = request
618
+
619
+ r = self._registrations[msg.registered_registration]
620
+ if r
621
+ h = r.handler
622
+ if h
623
+ begin
624
+ value = h.call(args, kwargs, details)
625
+
626
+ if value.nil?
627
+ value = CallResult.new
628
+ elsif not value.is_a?(CallResult)
629
+ value = CallResult.new([value])
630
+ end
631
+
632
+ # TODO: Handle promise or future
633
+
634
+ yield_msg = WampClient::Message::Yield.new(request, {}, value.args, value.kwargs)
635
+ self._send_message(yield_msg)
636
+ rescue Exception => e
637
+ error = WampClient::Message::Error.new(WampClient::Message::Types::INVOCATION, request, {}, 'wamp.error.runtime', [e.to_s])
638
+ self._send_message(error)
639
+ end
640
+ end
641
+ end
642
+ end
643
+
644
+ #endregion
645
+
646
+ #region Unregister Logic
647
+
648
+ # Unregisters from a procedure
649
+ # @param registration [Registration] The registration object from when the registration was created
650
+ # @param callback [block] The callback(registration, error, details) called to signal if the unregistration was a success or not
651
+ def unregister(registration, &callback)
652
+ unless is_open?
653
+ raise RuntimeError, "Session must be open to call 'unregister'"
654
+ end
655
+
656
+ self.class.check_nil('registration', registration, false)
657
+
658
+ # Create a new unsubscribe request
659
+ request = self._generate_id
660
+ self._requests[:unregister][request] = { r: registration, c: callback }
661
+
662
+ # Send the message
663
+ unregister = WampClient::Message::Unregister.new(request, registration.id)
664
+ self._send_message(unregister)
665
+ end
666
+
667
+ # Processes the response to a unregister request
668
+ # @param msg [WampClient::Message::Unregistered] The response from the unsubscribe
669
+ def _process_UNREGISTERED(msg)
670
+
671
+ # Remove the pending unregistration, add it to the registered ones, and inform the caller
672
+ r = self._requests[:unregister].delete(msg.unregister_request)
673
+ if r
674
+ r_s = r[:r]
675
+ self._registrations.delete(r_s.id)
676
+
677
+ details = {}
678
+ details[:procedure] = r_s.procedure
679
+ details[:type] = 'unregister'
680
+
681
+ c = r[:c]
682
+ c.call(r_s, nil, details) if c
683
+ end
684
+
685
+ end
686
+
687
+ # Processes an error from a request
688
+ # @param msg [WampClient::Message::Error] The response from the subscribe
689
+ def _process_UNREGISTER_error(msg)
690
+
691
+ # Remove the pending subscription and inform the caller of the failure
692
+ r = self._requests[:unregister].delete(msg.request_request)
693
+ if r
694
+
695
+ details = msg.details || {}
696
+ details[:procedure] = r[:r].procedure unless details[:procedure]
697
+ details[:type] = 'unregister'
698
+
699
+ c = r[:c]
700
+ c.call(nil, self._error_to_hash(msg), details) if c
701
+ end
702
+
703
+ end
704
+
705
+ #endregion
706
+
707
+ #region Call Logic
708
+
709
+ # Publishes and event to a topic
710
+ # @param procedure [String] The procedure to invoke
711
+ # @param args [Array] The arguments
712
+ # @param kwargs [Hash] The keyword arguments
713
+ # @param options [Hash] The options for the call
714
+ # @param callback [block] The callback(result, error, details) called to signal if the call was a success or not
715
+ def call(procedure, args=nil, kwargs=nil, options={}, &callback)
716
+ unless is_open?
717
+ raise RuntimeError, "Session must be open to call 'call'"
718
+ end
719
+
720
+ self.class.check_uri('procedure', procedure)
721
+ self.class.check_dict('options', options)
722
+ self.class.check_list('args', args, true)
723
+ self.class.check_dict('kwargs', kwargs, true)
724
+
725
+ # Create a new call request
726
+ request = self._generate_id
727
+ self._requests[:call][request] = {p: procedure, a: args, k: kwargs, o: options, c: callback}
728
+
729
+ # Send the message
730
+ call = WampClient::Message::Call.new(request, options, procedure, args, kwargs)
731
+ self._send_message(call)
732
+ end
733
+
734
+ # Processes the response to a publish request
735
+ # @param msg [WampClient::Message::Result] The response from the call
736
+ def _process_RESULT(msg)
737
+
738
+ details = msg.details || {}
739
+
740
+ call = self._requests[:call][msg.call_request]
741
+
742
+ # Don't remove if progress is true and the options had receive_progress true
743
+ self._requests[:call].delete(msg.call_request) unless (details[:progress] and (call and call[:o][:receive_progress]))
744
+
745
+ if call
746
+ details[:procedure] = call[:p] unless details[:procedure]
747
+ details[:type] = 'call'
748
+
749
+ c = call[:c]
750
+ c.call(CallResult.new(msg.yield_arguments, msg.yield_argumentskw), nil, details) if c
751
+ end
752
+
753
+ end
754
+
755
+ # Processes an error from a call request
756
+ # @param msg [WampClient::Message::Error] The response from the call
757
+ def _process_CALL_error(msg)
758
+
759
+ # Remove the pending publish and inform the caller of the failure
760
+ call = self._requests[:call].delete(msg.request_request)
761
+ if call
762
+
763
+ details = msg.details || {}
764
+ details[:procedure] = call[:p] unless details[:procedure]
765
+ details[:type] = 'call'
766
+
767
+ c = call[:c]
768
+ c.call(nil, self._error_to_hash(msg), details) if c
769
+ end
770
+
771
+ end
772
+
773
+ #endregion
774
+
775
+ end
776
+ end