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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +464 -0
- data/Rakefile +3 -0
- data/circle.yml +3 -0
- data/lib/wamp_client/auth.rb +18 -0
- data/lib/wamp_client/check.rb +57 -0
- data/lib/wamp_client/connection.rb +194 -0
- data/lib/wamp_client/message.rb +1283 -0
- data/lib/wamp_client/serializer.rb +40 -0
- data/lib/wamp_client/session.rb +776 -0
- data/lib/wamp_client/transport.rb +129 -0
- data/lib/wamp_client/version.rb +3 -0
- data/lib/wamp_client.rb +7 -0
- data/scripts/gen_message.rb +537 -0
- data/spec/auth_spec.rb +18 -0
- data/spec/check_spec.rb +197 -0
- data/spec/message_spec.rb +1478 -0
- data/spec/session_spec.rb +1004 -0
- data/spec/spec_helper.rb +43 -0
- data/tasks/rspec.rake +3 -0
- data/wamp_client.gemspec +29 -0
- metadata +170 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'wamp_client/serializer'
|
2
|
+
|
3
|
+
module WampClient
|
4
|
+
module Transport
|
5
|
+
class Base
|
6
|
+
|
7
|
+
# Callback when the socket is opened
|
8
|
+
@on_open
|
9
|
+
def on_open(&on_open)
|
10
|
+
@on_open = on_open
|
11
|
+
end
|
12
|
+
|
13
|
+
# Callback when the socket is closed. Parameters are
|
14
|
+
# @param reason [String] String telling the reason it was closed
|
15
|
+
@on_close
|
16
|
+
def on_close(&on_close)
|
17
|
+
@on_close = on_close
|
18
|
+
end
|
19
|
+
|
20
|
+
# Callback when a message is received. Parameters are
|
21
|
+
# @param msg [Array] The parsed message that was received
|
22
|
+
@on_message
|
23
|
+
def on_message(&on_message)
|
24
|
+
@on_message = on_message
|
25
|
+
end
|
26
|
+
|
27
|
+
# Callback when there is an error. Parameters are
|
28
|
+
@on_error
|
29
|
+
def on_error(&on_error)
|
30
|
+
@on_error = on_error
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_accessor :type, :uri, :headers, :protocol, :serializer, :connected
|
34
|
+
|
35
|
+
# Constructor for the transport
|
36
|
+
# @param options [Hash] The connection options. the different options are as follows
|
37
|
+
# @option options [String] :uri The url to connect to
|
38
|
+
# @option options [String] :protocol The protocol
|
39
|
+
# @option options [Hash] :headers Custom headers to include during the connection
|
40
|
+
# @option options [WampClient::Serializer::Base] :serializer The serializer to use
|
41
|
+
def initialize(options)
|
42
|
+
|
43
|
+
# Initialize the parameters
|
44
|
+
self.connected = false
|
45
|
+
self.uri = options[:uri]
|
46
|
+
self.headers = options[:headers] || {}
|
47
|
+
self.protocol = options[:protocol] || 'wamp.2.json'
|
48
|
+
self.serializer = options[:serializer] || WampClient::Serializer::JSONSerializer.new
|
49
|
+
|
50
|
+
# Add the wamp.2.json protocol header
|
51
|
+
self.headers['Sec-WebSocket-Protocol'] = self.protocol
|
52
|
+
|
53
|
+
# Initialize callbacks
|
54
|
+
@on_open = nil
|
55
|
+
@on_close = nil
|
56
|
+
@on_message = nil
|
57
|
+
@on_error = nil
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# Connects to the WAMP Server using the transport
|
62
|
+
def connect
|
63
|
+
# Implement in subclass
|
64
|
+
end
|
65
|
+
|
66
|
+
# Disconnects from the WAMP Server
|
67
|
+
def disconnect
|
68
|
+
# Implement in subclass
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if the transport it connected
|
72
|
+
def connected?
|
73
|
+
self.connected
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sends a Message
|
77
|
+
# @param [Array] msg - The message payload to send
|
78
|
+
def send_message(msg)
|
79
|
+
# Implement in subclass
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
# This implementation uses the 'websocket-eventmachine-client' Gem. This is the default if no transport is included
|
85
|
+
class WebSocketTransport < Base
|
86
|
+
attr_accessor :socket
|
87
|
+
|
88
|
+
def initialize(options)
|
89
|
+
super(options)
|
90
|
+
self.type = 'websocket'
|
91
|
+
self.socket = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def connect
|
95
|
+
self.socket = WebSocket::EventMachine::Client.connect(
|
96
|
+
:uri => self.uri,
|
97
|
+
:headers => self.headers
|
98
|
+
)
|
99
|
+
|
100
|
+
self.socket.onopen do
|
101
|
+
self.connected = true
|
102
|
+
@on_open.call unless @on_open.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
self.socket.onmessage do |msg, type|
|
106
|
+
@on_message.call(self.serializer.deserialize(msg)) unless @on_message.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
self.socket.onclose do |code, reason|
|
110
|
+
self.connected = false
|
111
|
+
@on_close.call(reason) unless @on_close.nil?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def disconnect
|
116
|
+
self.connected = !self.socket.close # close returns 'true' if the connection was closed immediately
|
117
|
+
end
|
118
|
+
|
119
|
+
def send_message(msg)
|
120
|
+
if self.connected
|
121
|
+
self.socket.send(self.serializer.serialize(msg), {type: 'text'})
|
122
|
+
else
|
123
|
+
raise RuntimeError, "Socket must be open to call 'send_message'"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/wamp_client.rb
ADDED
@@ -0,0 +1,537 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
def value_from_type(type)
|
4
|
+
|
5
|
+
value = nil
|
6
|
+
|
7
|
+
if type == 'int' or type == 'id'
|
8
|
+
value = '123'
|
9
|
+
elsif type == 'uri' or type == 'string'
|
10
|
+
value = "'string'"
|
11
|
+
elsif type == 'list'
|
12
|
+
value = "['test']"
|
13
|
+
elsif type == 'dict'
|
14
|
+
value = '{ test: 1 }'
|
15
|
+
end
|
16
|
+
|
17
|
+
value
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
message_type_lookup = {
|
22
|
+
'HELLO' => 1,
|
23
|
+
'WELCOME' => 2,
|
24
|
+
'ABORT' => 3,
|
25
|
+
'CHALLENGE' => 4,
|
26
|
+
'AUTHENTICATE' => 5,
|
27
|
+
'GOODBYE' => 6,
|
28
|
+
'ERROR' => 8,
|
29
|
+
'PUBLISH' => 16,
|
30
|
+
'PUBLISHED' => 17,
|
31
|
+
'SUBSCRIBE' => 32,
|
32
|
+
'SUBSCRIBED' => 33,
|
33
|
+
'UNSUBSCRIBE' => 34,
|
34
|
+
'UNSUBSCRIBED' => 35,
|
35
|
+
'EVENT' => 36,
|
36
|
+
'CALL' => 48,
|
37
|
+
'CANCEL' => 49,
|
38
|
+
'RESULT' => 50,
|
39
|
+
'REGISTER' => 64,
|
40
|
+
'REGISTERED' => 65,
|
41
|
+
'UNREGISTER' => 66,
|
42
|
+
'UNREGISTERED' => 67,
|
43
|
+
'INVOCATION' => 68,
|
44
|
+
'INTERRUPT' => 69,
|
45
|
+
'YIELD' => 70
|
46
|
+
}
|
47
|
+
|
48
|
+
message_type_define = ''
|
49
|
+
message_lookup_define = ''
|
50
|
+
count = 0
|
51
|
+
message_type_lookup.each do |name, value|
|
52
|
+
|
53
|
+
# Generate the defines
|
54
|
+
message_type_define += " #{name} = #{value}\n"
|
55
|
+
|
56
|
+
# Generate the lookup
|
57
|
+
if count == 0
|
58
|
+
message_lookup_define += ' if'
|
59
|
+
else
|
60
|
+
message_lookup_define += ' elsif'
|
61
|
+
end
|
62
|
+
message_lookup_define += " params[0] == Types::#{name}\n"
|
63
|
+
message_lookup_define += " object = WampClient::Message::#{name.downcase.capitalize}.parse(params)\n"
|
64
|
+
|
65
|
+
count += 1
|
66
|
+
end
|
67
|
+
|
68
|
+
source_file_header = "require 'wamp_client/check'
|
69
|
+
|
70
|
+
# !!!!THIS FILE IS AUTOGENERATED. DO NOT HAND EDIT!!!!
|
71
|
+
|
72
|
+
module WampClient
|
73
|
+
module Message
|
74
|
+
|
75
|
+
module Types
|
76
|
+
#{message_type_define} end
|
77
|
+
|
78
|
+
class Base
|
79
|
+
include WampClient::Check
|
80
|
+
|
81
|
+
def payload
|
82
|
+
[]
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param params [Array]
|
86
|
+
def self.parse(params)
|
87
|
+
object = nil
|
88
|
+
#{message_lookup_define} end
|
89
|
+
|
90
|
+
object
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
"
|
95
|
+
|
96
|
+
source_file_footer = "
|
97
|
+
end
|
98
|
+
end
|
99
|
+
"
|
100
|
+
|
101
|
+
test_file_header = "require 'spec_helper'
|
102
|
+
|
103
|
+
# !!!!THIS FILE IS AUTOGENERATED. DO NOT HAND EDIT!!!!
|
104
|
+
|
105
|
+
describe WampClient::Message do
|
106
|
+
"
|
107
|
+
|
108
|
+
test_file_footer = "
|
109
|
+
end
|
110
|
+
"
|
111
|
+
|
112
|
+
source_file = source_file_header
|
113
|
+
test_file = test_file_header
|
114
|
+
|
115
|
+
###############################################
|
116
|
+
# Iterate through message types
|
117
|
+
###############################################
|
118
|
+
|
119
|
+
messages = [
|
120
|
+
{
|
121
|
+
name: 'hello',
|
122
|
+
description: 'Sent by a Client to initiate opening of a WAMP session to a Router attaching to a Realm.',
|
123
|
+
formats: [
|
124
|
+
'[HELLO, Realm|uri, Details|dict]'
|
125
|
+
]
|
126
|
+
},
|
127
|
+
{
|
128
|
+
name: 'welcome',
|
129
|
+
description: 'Sent by a Router to accept a Client. The WAMP session is now open.',
|
130
|
+
formats: [
|
131
|
+
'[WELCOME, Session|id, Details|dict]'
|
132
|
+
]
|
133
|
+
},
|
134
|
+
{
|
135
|
+
name: 'abort',
|
136
|
+
description: 'Sent by a Peer*to abort the opening of a WAMP session. No response is expected.',
|
137
|
+
formats: [
|
138
|
+
'[ABORT, Details|dict, Reason|uri]'
|
139
|
+
]
|
140
|
+
},
|
141
|
+
{
|
142
|
+
name: 'goodbye',
|
143
|
+
description: "Sent by a Peer to close a previously opened WAMP session. Must be echo'ed by the receiving Peer.",
|
144
|
+
formats: [
|
145
|
+
'[GOODBYE, Details|dict, Reason|uri]'
|
146
|
+
]
|
147
|
+
},
|
148
|
+
{
|
149
|
+
name: 'error',
|
150
|
+
description: 'Error reply sent by a Peer as an error response to different kinds of requests.',
|
151
|
+
formats: [
|
152
|
+
'[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri]',
|
153
|
+
'[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list]',
|
154
|
+
'[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list, ArgumentsKw|dict]'
|
155
|
+
]
|
156
|
+
},
|
157
|
+
{
|
158
|
+
name: 'publish',
|
159
|
+
description: 'Sent by a Publisher to a Broker to publish an event.',
|
160
|
+
formats: [
|
161
|
+
'[PUBLISH, Request|id, Options|dict, Topic|uri]',
|
162
|
+
'[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list]',
|
163
|
+
'[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list, ArgumentsKw|dict]'
|
164
|
+
]
|
165
|
+
},
|
166
|
+
{
|
167
|
+
name: 'published',
|
168
|
+
description: 'Acknowledge sent by a Broker to a Publisher for acknowledged publications.',
|
169
|
+
formats: [
|
170
|
+
'[PUBLISHED, PUBLISH.Request|id, Publication|id]'
|
171
|
+
]
|
172
|
+
},
|
173
|
+
{
|
174
|
+
name: 'subscribe',
|
175
|
+
description: 'Subscribe request sent by a Subscriber to a Broker to subscribe to a topic.',
|
176
|
+
formats: [
|
177
|
+
'[SUBSCRIBE, Request|id, Options|dict, Topic|uri]'
|
178
|
+
]
|
179
|
+
},
|
180
|
+
{
|
181
|
+
name: 'subscribed',
|
182
|
+
description: 'Acknowledge sent by a Broker to a Subscriber to acknowledge a subscription.',
|
183
|
+
formats: [
|
184
|
+
'[SUBSCRIBED, SUBSCRIBE.Request|id, Subscription|id]'
|
185
|
+
]
|
186
|
+
},
|
187
|
+
{
|
188
|
+
name: 'unsubscribe',
|
189
|
+
description: 'Unsubscribe request sent by a Subscriber to a Broker to unsubscribe a subscription.',
|
190
|
+
formats: [
|
191
|
+
'[UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id]'
|
192
|
+
]
|
193
|
+
},
|
194
|
+
{
|
195
|
+
name: 'unsubscribed',
|
196
|
+
description: 'Acknowledge sent by a Broker to a Subscriber to acknowledge unsubscription.',
|
197
|
+
formats: [
|
198
|
+
'[UNSUBSCRIBED, UNSUBSCRIBE.Request|id]'
|
199
|
+
]
|
200
|
+
},
|
201
|
+
{
|
202
|
+
name: 'event',
|
203
|
+
description: 'Event dispatched by Broker to Subscribers for subscriptions the event was matching.',
|
204
|
+
formats: [
|
205
|
+
'[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict]',
|
206
|
+
'[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list]',
|
207
|
+
'[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list, PUBLISH.ArgumentsKw|dict]'
|
208
|
+
]
|
209
|
+
},
|
210
|
+
{
|
211
|
+
name: 'call',
|
212
|
+
description: 'Call as originally issued by the _Caller_ to the _Dealer_.',
|
213
|
+
formats: [
|
214
|
+
'[CALL, Request|id, Options|dict, Procedure|uri]',
|
215
|
+
'[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list]',
|
216
|
+
'[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list, ArgumentsKw|dict]'
|
217
|
+
]
|
218
|
+
},
|
219
|
+
{
|
220
|
+
name: 'result',
|
221
|
+
description: 'Result of a call as returned by _Dealer_ to _Caller_.',
|
222
|
+
formats: [
|
223
|
+
'[RESULT, CALL.Request|id, Details|dict]',
|
224
|
+
'[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list]',
|
225
|
+
'[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list, YIELD.ArgumentsKw|dict]'
|
226
|
+
]
|
227
|
+
},
|
228
|
+
{
|
229
|
+
name: 'register',
|
230
|
+
description: 'A _Callees_ request to register an endpoint at a _Dealer_.',
|
231
|
+
formats: [
|
232
|
+
'[REGISTER, Request|id, Options|dict, Procedure|uri]'
|
233
|
+
]
|
234
|
+
},
|
235
|
+
{
|
236
|
+
name: 'registered',
|
237
|
+
description: 'Acknowledge sent by a _Dealer_ to a _Callee_ for successful registration.',
|
238
|
+
formats: [
|
239
|
+
'[REGISTERED, REGISTER.Request|id, Registration|id]'
|
240
|
+
]
|
241
|
+
},
|
242
|
+
{
|
243
|
+
name: 'unregister',
|
244
|
+
description: 'A _Callees_ request to unregister a previously established registration.',
|
245
|
+
formats: [
|
246
|
+
'[UNREGISTER, Request|id, REGISTERED.Registration|id]'
|
247
|
+
]
|
248
|
+
},
|
249
|
+
{
|
250
|
+
name: 'unregistered',
|
251
|
+
description: 'Acknowledge sent by a _Dealer_ to a _Callee_ for successful unregistration.',
|
252
|
+
formats: [
|
253
|
+
'[UNREGISTERED, UNREGISTER.Request|id]'
|
254
|
+
]
|
255
|
+
},
|
256
|
+
{
|
257
|
+
name: 'invocation',
|
258
|
+
description: 'Actual invocation of an endpoint sent by _Dealer_ to a _Callee_.',
|
259
|
+
formats: [
|
260
|
+
'[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict]',
|
261
|
+
'[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list]',
|
262
|
+
'[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list, CALL.ArgumentsKw|dict]'
|
263
|
+
]
|
264
|
+
},
|
265
|
+
{
|
266
|
+
name: 'yield',
|
267
|
+
description: 'Actual yield from an endpoint sent by a _Callee_ to _Dealer_.',
|
268
|
+
formats: [
|
269
|
+
'[YIELD, INVOCATION.Request|id, Options|dict]',
|
270
|
+
'[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list]',
|
271
|
+
'[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list, ArgumentsKw|dict]'
|
272
|
+
]
|
273
|
+
},
|
274
|
+
{
|
275
|
+
name: 'challenge',
|
276
|
+
description: 'The "CHALLENGE" message is used with certain Authentication Methods. During authenticated session establishment, a *Router* sends a challenge message.',
|
277
|
+
formats: [
|
278
|
+
'[CHALLENGE, AuthMethod|string, Extra|dict]'
|
279
|
+
]
|
280
|
+
},
|
281
|
+
{
|
282
|
+
name: 'authenticate',
|
283
|
+
description: 'The "AUTHENTICATE" message is used with certain Authentication Methods. A *Client* having received a challenge is expected to respond by sending a signature or token.',
|
284
|
+
formats: [
|
285
|
+
'[AUTHENTICATE, Signature|string, Extra|dict]'
|
286
|
+
]
|
287
|
+
},
|
288
|
+
{
|
289
|
+
name: 'cancel',
|
290
|
+
description: 'The "CANCEL" message is used with the Call Canceling advanced feature. A _Caller_ can cancel and issued call actively by sending a cancel message to the _Dealer_.',
|
291
|
+
formats: [
|
292
|
+
'[CANCEL, CALL.Request|id, Options|dict]'
|
293
|
+
]
|
294
|
+
},
|
295
|
+
{
|
296
|
+
name: 'interrupt',
|
297
|
+
description: 'The "INTERRUPT" message is used with the Call Canceling advanced feature. Upon receiving a cancel for a pending call, a _Dealer_ will issue an interrupt to the _Callee_.',
|
298
|
+
formats: [
|
299
|
+
'[INTERRUPT, INVOCATION.Request|id, Options|dict]'
|
300
|
+
]
|
301
|
+
}
|
302
|
+
]
|
303
|
+
|
304
|
+
messages.each do |message|
|
305
|
+
|
306
|
+
###############################################
|
307
|
+
# Generate Lookups
|
308
|
+
###############################################
|
309
|
+
count = 0
|
310
|
+
params_lookup = {}
|
311
|
+
params = []
|
312
|
+
required_count = 0
|
313
|
+
param_formats = ''
|
314
|
+
message[:formats].each do |format|
|
315
|
+
param_formats += ' # ' + format + "\n"
|
316
|
+
|
317
|
+
# Generate the params
|
318
|
+
temp_format = format.delete(' ')
|
319
|
+
temp_format = temp_format.delete('[')
|
320
|
+
temp_format = temp_format.delete(']')
|
321
|
+
temp_format = temp_format.gsub('.', '_')
|
322
|
+
format_params = temp_format.split(',')
|
323
|
+
format_params.shift
|
324
|
+
|
325
|
+
format_params.each do |format_param|
|
326
|
+
parsed_param = format_param.split('|')
|
327
|
+
param_name = parsed_param[0].downcase
|
328
|
+
param_type = parsed_param[1]
|
329
|
+
|
330
|
+
if params_lookup[param_name].nil?
|
331
|
+
params.push(
|
332
|
+
{
|
333
|
+
name: param_name,
|
334
|
+
type: param_type,
|
335
|
+
required: count == 0
|
336
|
+
})
|
337
|
+
params_lookup[param_name] = true
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
if count == 0
|
342
|
+
required_count = params.count
|
343
|
+
end
|
344
|
+
|
345
|
+
count += 1
|
346
|
+
end
|
347
|
+
|
348
|
+
###############################################
|
349
|
+
# Source File
|
350
|
+
###############################################
|
351
|
+
source_file += "\n"
|
352
|
+
source_file += ' # ' + message[:name].capitalize + "\n"
|
353
|
+
source_file += ' # ' + message[:description] + "\n"
|
354
|
+
source_file += " # Formats:\n"
|
355
|
+
source_file += param_formats
|
356
|
+
source_file += ' class ' + message[:name].capitalize + " < Base\n"
|
357
|
+
|
358
|
+
# Generate the local variables
|
359
|
+
source_file += ' attr_accessor'
|
360
|
+
count = 0
|
361
|
+
params.each do |param|
|
362
|
+
source_file += ',' unless count == 0
|
363
|
+
source_file += " :#{param[:name]}"
|
364
|
+
count += 1
|
365
|
+
end
|
366
|
+
source_file += "\n"
|
367
|
+
|
368
|
+
# Generate the constructor
|
369
|
+
source_file += "\n def initialize("
|
370
|
+
count = 0
|
371
|
+
checks = ''
|
372
|
+
setters = ''
|
373
|
+
params.each do |param|
|
374
|
+
setters += " self.#{param[:name]} = #{param[:name]}\n"
|
375
|
+
|
376
|
+
source_file += ', ' if count > 0
|
377
|
+
if param[:required]
|
378
|
+
source_file += "#{param[:name]}"
|
379
|
+
checks += " self.class.check_#{param[:type]}('#{param[:name]}', #{param[:name]})\n"
|
380
|
+
else
|
381
|
+
source_file += "#{param[:name]}=nil"
|
382
|
+
checks += " self.class.check_#{param[:type]}('#{param[:name]}', #{param[:name]}, true)\n"
|
383
|
+
end
|
384
|
+
|
385
|
+
count += 1
|
386
|
+
end
|
387
|
+
source_file += ")\n\n"
|
388
|
+
source_file += checks + "\n"
|
389
|
+
source_file += setters + "\n"
|
390
|
+
source_file += " end\n"
|
391
|
+
|
392
|
+
# Generate the 'type' method
|
393
|
+
source_file += "\n def self.type\n Types::#{message[:name].upcase}\n end\n"
|
394
|
+
|
395
|
+
# Generate the parser
|
396
|
+
source_file += "\n def self.parse(params)\n"
|
397
|
+
source_file += "\n self.check_gte('params list', #{required_count+1}, params.count)\n"
|
398
|
+
source_file += " self.check_equal('message type', self.type, params[0])\n"
|
399
|
+
source_file += "\n params.shift\n self.new(*params)\n"
|
400
|
+
source_file += "\n end\n"
|
401
|
+
|
402
|
+
# Generate the payload
|
403
|
+
source_file += "\n def payload\n"
|
404
|
+
source_file += " payload = [self.class.type]\n"
|
405
|
+
params.each do |param|
|
406
|
+
if param[:required]
|
407
|
+
source_file += " payload.push(self.#{param[:name]})\n"
|
408
|
+
else
|
409
|
+
source_file += "\n return payload if (self.#{param[:name]}.nil? or self.#{param[:name]}.empty?)\n"
|
410
|
+
source_file += " payload.push(self.#{param[:name]})\n"
|
411
|
+
end
|
412
|
+
end
|
413
|
+
source_file += "\n payload\n"
|
414
|
+
source_file += " end\n"
|
415
|
+
|
416
|
+
# Generate the string
|
417
|
+
source_file += "\n def to_s\n"
|
418
|
+
source_file += " '#{message[:name].upcase} > ' + self.payload.to_s\n"
|
419
|
+
source_file += " end\n"
|
420
|
+
|
421
|
+
|
422
|
+
source_file += "\n end\n"
|
423
|
+
|
424
|
+
###############################################
|
425
|
+
# Test File
|
426
|
+
###############################################
|
427
|
+
|
428
|
+
value_array = []
|
429
|
+
params.each do |param|
|
430
|
+
if param[:required]
|
431
|
+
value_array.push(value_from_type(param[:type]))
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
class_name = "WampClient::Message::#{message[:name].capitalize}"
|
436
|
+
|
437
|
+
test_file += "\n describe #{class_name} do\n"
|
438
|
+
|
439
|
+
# Generate Constructor Test
|
440
|
+
test_file += "\n it 'creates the message object' do\n"
|
441
|
+
test_file += " params = [#{value_array.join(',')}]\n"
|
442
|
+
test_file += " object = #{class_name}.new(*params)\n\n"
|
443
|
+
params.each do |param|
|
444
|
+
if param[:required]
|
445
|
+
test_file += " expect(object.#{param[:name]}).to eq(#{value_from_type(param[:type])})\n"
|
446
|
+
end
|
447
|
+
end
|
448
|
+
test_file += " expect(object.is_a?(#{class_name})).to eq(true)\n"
|
449
|
+
test_file += " end\n"
|
450
|
+
|
451
|
+
# Generate Parser Test
|
452
|
+
test_file += "\n it 'parses the message and creates an object' do\n"
|
453
|
+
test_file += " params = [#{message_type_lookup[message[:name].upcase]},#{value_array.join(',')}]\n"
|
454
|
+
test_file += " object = #{class_name}.parse(params)\n\n"
|
455
|
+
params.each do |param|
|
456
|
+
if param[:required]
|
457
|
+
test_file += " expect(object.#{param[:name]}).to eq(#{value_from_type(param[:type])})\n"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
test_file += " expect(object.is_a?(#{class_name})).to eq(true)\n"
|
461
|
+
test_file += " end\n"
|
462
|
+
|
463
|
+
# Generate Global Parser Test
|
464
|
+
test_file += "\n it 'globally parses the message and creates an object' do\n"
|
465
|
+
test_file += " params = [#{message_type_lookup[message[:name].upcase]},#{value_array.join(',')}]\n"
|
466
|
+
test_file += " object = WampClient::Message::Base.parse(params)\n\n"
|
467
|
+
params.each do |param|
|
468
|
+
if param[:required]
|
469
|
+
test_file += " expect(object.#{param[:name]}).to eq(#{value_from_type(param[:type])})\n"
|
470
|
+
end
|
471
|
+
end
|
472
|
+
test_file += " expect(object.is_a?(#{class_name})).to eq(true)\n"
|
473
|
+
test_file += " end\n"
|
474
|
+
|
475
|
+
# Generate Payload Test
|
476
|
+
test_file += "\n it 'generates the payload' do\n"
|
477
|
+
test_file += " params = [#{value_array.join(',')}]\n"
|
478
|
+
test_file += " object = #{class_name}.new(*params)\n"
|
479
|
+
test_file += " payload = object.payload\n\n"
|
480
|
+
count = 0
|
481
|
+
test_file += " expect(payload.count).to eq(#{value_array.count+1})\n"
|
482
|
+
test_file += " expect(payload[0]).to eq(#{message_type_lookup[message[:name].upcase]})\n"
|
483
|
+
value_array.each do |value|
|
484
|
+
test_file += " expect(payload[#{count+1}]).to eq(#{value})\n"
|
485
|
+
count += 1
|
486
|
+
end
|
487
|
+
test_file += " end\n"
|
488
|
+
|
489
|
+
# Generate non-required parameter tests
|
490
|
+
params.each do |param|
|
491
|
+
unless param[:required]
|
492
|
+
value_array.push(value_from_type(param[:type]))
|
493
|
+
|
494
|
+
test_file += "\n describe 'checks optional parameter #{param[:name]}' do\n"
|
495
|
+
|
496
|
+
# Generate Constructor Test
|
497
|
+
test_file += "\n it 'creates the message object' do\n"
|
498
|
+
test_file += " params = [#{value_array.join(',')}]\n"
|
499
|
+
test_file += " object = #{class_name}.new(*params)\n\n"
|
500
|
+
test_file += " expect(object.is_a?(#{class_name})).to eq(true)\n"
|
501
|
+
test_file += " end\n"
|
502
|
+
|
503
|
+
# Generate Parser Test
|
504
|
+
test_file += "\n it 'parses the message and creates an object' do\n"
|
505
|
+
test_file += " params = [#{message_type_lookup[message[:name].upcase]},#{value_array.join(',')}]\n"
|
506
|
+
test_file += " object = #{class_name}.parse(params)\n\n"
|
507
|
+
test_file += " expect(object.is_a?(#{class_name})).to eq(true)\n"
|
508
|
+
test_file += " end\n"
|
509
|
+
|
510
|
+
# Generate Payload Test
|
511
|
+
test_file += "\n it 'generates the payload' do\n"
|
512
|
+
test_file += " params = [#{value_array.join(',')}]\n"
|
513
|
+
test_file += " object = #{class_name}.new(*params)\n"
|
514
|
+
test_file += " payload = object.payload\n\n"
|
515
|
+
count = 0
|
516
|
+
test_file += " expect(payload.count).to eq(#{value_array.count+1})\n"
|
517
|
+
test_file += " expect(payload[0]).to eq(#{message_type_lookup[message[:name].upcase]})\n"
|
518
|
+
value_array.each do |value|
|
519
|
+
test_file += " expect(payload[#{count+1}]).to eq(#{value})\n"
|
520
|
+
count += 1
|
521
|
+
end
|
522
|
+
test_file += " end\n"
|
523
|
+
|
524
|
+
test_file += "\n end\n"
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
test_file += "\n end\n"
|
529
|
+
|
530
|
+
end
|
531
|
+
|
532
|
+
source_file += source_file_footer
|
533
|
+
test_file += test_file_footer
|
534
|
+
|
535
|
+
File.open('message.rb.tmp', 'w') { |file| file.write(source_file) }
|
536
|
+
File.open('message_spec.rb.tmp', 'w') { |file| file.write(test_file) }
|
537
|
+
|
data/spec/auth_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WampClient::Auth do
|
4
|
+
|
5
|
+
describe 'cra' do
|
6
|
+
|
7
|
+
it 'generates the signature' do
|
8
|
+
|
9
|
+
challenge = "{ \"nonce\": \"LHRTC9zeOIrt_9U3\", \"authprovider\": \"userdb\", \"authid\": \"peter\", \"timestamp\": \"2014-06-22T16:36:25.448Z\", \"authrole\": \"user\", \"authmethod\": \"wampcra\", \"session\": 3251278072152162}"
|
10
|
+
secret = 'secret'
|
11
|
+
signature = WampClient::Auth::Cra.sign(secret, challenge)
|
12
|
+
expect(signature).to eq('Pji30JC9tb/T9tbEwxw5i0RyRa5UVBxuoIVTgT7hnkE=')
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|