wamp_client 0.0.1

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