rabx-message 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 +6 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +66 -0
- data/Rakefile +16 -0
- data/USAGE +1 -0
- data/lib/50418 +1 -0
- data/lib/rabx/message.rb +225 -0
- data/lib/rabx/message/version.rb +5 -0
- data/rabx-message.gemspec +24 -0
- data/spec/message_spec.rb +266 -0
- data/spec/spec_helper.rb +11 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 07ba374abe002ab50743fb9c8a81b96855e27015
|
4
|
+
data.tar.gz: 363d8672a81d05b0ab1621b74da7084009b980dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1e2d11ab1bb67e1033212640602abd2c82eb345bc99805d7862639a9261e49a8327d56b5faf98a5763936ef555df7db72b513fd897e829d63e50e6a6f3a0e298
|
7
|
+
data.tar.gz: 064e5b31853c42a501181b83a1aff1a8e6dbe3711c7c3ab496275e167fa63e115a6304e8e2bf24ae6351e5f5244ce137f27e24a283b48db342000369a76f8163
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 James McKinney
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# RPC using Anything But XML (RABX) message parser and emitter
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/rabx-message)
|
4
|
+
[](http://travis-ci.org/jpmckinney/rabx-message)
|
5
|
+
[](https://gemnasium.com/jpmckinney/rabx-message)
|
6
|
+
[](https://coveralls.io/r/jpmckinney/rabx-message)
|
7
|
+
[](https://codeclimate.com/github/jpmckinney/rabx-message)
|
8
|
+
|
9
|
+
The RABX specification is a [Perl module](https://github.com/mysociety/commonlib/blob/master/perllib/RABX.pm) by [mySociety](https://www.mysociety.org/).
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
require 'rabx/message'
|
15
|
+
```
|
16
|
+
|
17
|
+
Dump a RABX message:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
RABX::Message.dump('R', 'RPC.method', ['argument', 3])
|
21
|
+
# => "R1:0,10:RPC.method,L1:2,T8:argument,I1:3,"
|
22
|
+
RABX::Message.dump('S', {name: 'foo', email: 'foo@example.com'})
|
23
|
+
# => "S1:0,A1:2,T4:name,T3:foo,T5:email,T15:foo@example.com,"
|
24
|
+
RABX::Message.dump('E', 404, 'Not Found')
|
25
|
+
# => "E1:0,3:404,9:Not Found,N"
|
26
|
+
```
|
27
|
+
|
28
|
+
Load a RABX message:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
message = RABX::Message.load("R1:0,10:RPC.method,L1:2,T8:argument,I1:3,")
|
32
|
+
message.type # "R"
|
33
|
+
message.method # "RPC.method"
|
34
|
+
message.argumetns # ["argument", 3]
|
35
|
+
|
36
|
+
message = RABX::Message.load("S1:0,A1:2,T4:name,T3:foo,T5:email,T15:foo@example.com,")
|
37
|
+
message.type # "S"
|
38
|
+
message.value # {"name"=>"foo", "email"=>"foo@example.com"}
|
39
|
+
|
40
|
+
message = RABX::Message.load("E1:0,3:404,9:Not Found,N")
|
41
|
+
message.type # "E"
|
42
|
+
message.code # "404"
|
43
|
+
message.text # "Not Found"
|
44
|
+
message.extra # nil
|
45
|
+
```
|
46
|
+
|
47
|
+
See the [documentation](http://www.rubydoc.info/gems/rabx-message) to see how to work with `RABX::Message` instances.
|
48
|
+
|
49
|
+
## Notes
|
50
|
+
|
51
|
+
Generic RABX clients include:
|
52
|
+
|
53
|
+
* [Perl](https://github.com/mysociety/commonlib/blob/master/perllib/RABX.pm)
|
54
|
+
* [PHP](https://github.com/mysociety/commonlib/blob/master/phplib/rabx.php)
|
55
|
+
* [Python](https://github.com/mysociety/commonlib/blob/master/pylib/mysociety/rabx.py) (REST interface)
|
56
|
+
* [Ruby](https://github.com/mysociety/commonlib/blob/master/rblib/rabx.rb) (REST interface)
|
57
|
+
* [Command-line](https://github.com/mysociety/misc-scripts/blob/master/bin/rabx)
|
58
|
+
|
59
|
+
mySociety has scripts to convert a Perl RABX server to a server-specific client in:
|
60
|
+
|
61
|
+
* [Perl](https://github.com/mysociety/misc-scripts/blob/master/bin/rabxtopl.pl)
|
62
|
+
* [PHP](https://github.com/mysociety/misc-scripts/blob/master/bin/rabxtophp.pl)
|
63
|
+
* [Python](https://github.com/mysociety/misc-scripts/blob/master/bin/rabxresttopy.pl)
|
64
|
+
* [Ruby](https://github.com/mysociety/misc-scripts/blob/master/bin/rabxresttorb.pl)
|
65
|
+
|
66
|
+
Copyright (c) 2014 James McKinney, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'yard'
|
11
|
+
YARD::Rake::YardocTask.new
|
12
|
+
rescue LoadError
|
13
|
+
task :yard do
|
14
|
+
abort 'YARD is not available. In order to run yard, you must: gem install yard'
|
15
|
+
end
|
16
|
+
end
|
data/USAGE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
See README.md for full usage details.
|
data/lib/50418
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
E1:0,3:515,48:first byte of call string should be "R", not "D",N
|
data/lib/rabx/message.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'netstring'
|
2
|
+
|
3
|
+
module RABX
|
4
|
+
class Message
|
5
|
+
class Error < StandardError; end
|
6
|
+
class ProtocolError < Error; end
|
7
|
+
class InterfaceError < Error; end
|
8
|
+
|
9
|
+
# Dumps a RABX message.
|
10
|
+
#
|
11
|
+
# @param [String] type a message type
|
12
|
+
# @param [Array] args message contents
|
13
|
+
# @return [String] the REBX message
|
14
|
+
def self.dump(type, *args)
|
15
|
+
string = type + Netstring.dump('0')
|
16
|
+
if type == 'R'
|
17
|
+
string + Netstring.dump(args[0]) + _dump(args[1])
|
18
|
+
elsif type == 'S'
|
19
|
+
if args.size.zero?
|
20
|
+
string
|
21
|
+
else
|
22
|
+
string + _dump(args[0])
|
23
|
+
end
|
24
|
+
elsif type == 'E'
|
25
|
+
string + Netstring.dump(args[0].to_s) + Netstring.dump(args[1]) + _dump(args[2])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Loads a RABX message.
|
30
|
+
#
|
31
|
+
# @param [String] s a RABX message
|
32
|
+
# @return [Message] a parsed message
|
33
|
+
def self.load(s)
|
34
|
+
case s[0]
|
35
|
+
when 'R'
|
36
|
+
RequestMessage.new(s)
|
37
|
+
when 'S'
|
38
|
+
SuccessMessage.new(s)
|
39
|
+
when 'E'
|
40
|
+
ErrorMessage.new(s)
|
41
|
+
else
|
42
|
+
raise ProtocolError, %(unknown RABX message type "#{s[0]}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :version
|
47
|
+
|
48
|
+
# @param [String] s a RABX message
|
49
|
+
def initialize(s)
|
50
|
+
if s.empty?
|
51
|
+
raise ProtocolError, 'string is too short'
|
52
|
+
end
|
53
|
+
@s = s.dup # string
|
54
|
+
@p = 1 # pointer
|
55
|
+
@size = @s.size
|
56
|
+
@version = getnetstring
|
57
|
+
parse
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the RABX message.
|
61
|
+
#
|
62
|
+
# @return [String] the RABX message
|
63
|
+
def to_s
|
64
|
+
@s
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the message type.
|
68
|
+
#
|
69
|
+
# @return [String] the message type
|
70
|
+
def type
|
71
|
+
@s[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns whether the reader has reached the end-of-file.
|
75
|
+
#
|
76
|
+
# @return [Boolean] whether the reader has reached the end-of-file
|
77
|
+
def eof?
|
78
|
+
@p == @size
|
79
|
+
end
|
80
|
+
|
81
|
+
# Loads a netstring from the current position in the message.
|
82
|
+
#
|
83
|
+
# @return [Netstring,nil] a string, or nil if at end-of-file
|
84
|
+
def getnetstring
|
85
|
+
if eof?
|
86
|
+
nil
|
87
|
+
else
|
88
|
+
string = Netstring.load(@s[@p..-1])
|
89
|
+
@p += string.netstring.size
|
90
|
+
string
|
91
|
+
end
|
92
|
+
rescue Netstring::Error => e
|
93
|
+
raise ProtocolError, "#{e.message} in netstring starting at #{@p}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Loads a typed netstring from the current position in the message.
|
97
|
+
#
|
98
|
+
# @return the contents of the netstring, or nil if at end-of-file
|
99
|
+
def gets
|
100
|
+
if eof?
|
101
|
+
nil
|
102
|
+
else
|
103
|
+
type = @s[@p]
|
104
|
+
@p += 1
|
105
|
+
|
106
|
+
if type == 'N' # null
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
case type
|
111
|
+
when 'I' # integer
|
112
|
+
value = getnetstring.to_s
|
113
|
+
begin
|
114
|
+
Integer(value)
|
115
|
+
rescue ArgumentError
|
116
|
+
raise ProtocolError, %(expected integer, got #{value.inspect} at position #{@p - value.size - 1})
|
117
|
+
end
|
118
|
+
when 'R' # real
|
119
|
+
value = getnetstring.to_s
|
120
|
+
begin
|
121
|
+
Float(value)
|
122
|
+
rescue ArgumentError
|
123
|
+
raise ProtocolError, %(expected float, got #{value.inspect} at position #{@p - value.size - 1})
|
124
|
+
end
|
125
|
+
when 'T', 'B' # text, binary
|
126
|
+
getnetstring
|
127
|
+
when 'L' # list
|
128
|
+
value = getnetstring.to_s
|
129
|
+
begin
|
130
|
+
size = Integer(value)
|
131
|
+
rescue ArgumentError
|
132
|
+
raise ProtocolError, %(expected integer, got #{value.inspect} at position #{@p - value.size - 1})
|
133
|
+
end
|
134
|
+
array = []
|
135
|
+
size.times do |n|
|
136
|
+
if eof?
|
137
|
+
raise ProtocolError, %(expected #{size} items, got #{n} items at position #{@p})
|
138
|
+
end
|
139
|
+
array << gets
|
140
|
+
end
|
141
|
+
array
|
142
|
+
when 'A' # associative array
|
143
|
+
value = getnetstring.to_s
|
144
|
+
begin
|
145
|
+
size = Integer(value)
|
146
|
+
rescue ArgumentError
|
147
|
+
raise ProtocolError, %(expected integer, got #{value.inspect} at position #{@p - value.size - 1})
|
148
|
+
end
|
149
|
+
hash = {}
|
150
|
+
size.times do |n|
|
151
|
+
if eof?
|
152
|
+
raise ProtocolError, %(expected #{size} items, got #{n} items at position #{@p})
|
153
|
+
end
|
154
|
+
key = gets # test for repeated keys?
|
155
|
+
if eof?
|
156
|
+
raise ProtocolError, %(expected value, got end-of-file at position #{@p})
|
157
|
+
end
|
158
|
+
hash[key] = gets
|
159
|
+
end
|
160
|
+
hash
|
161
|
+
else
|
162
|
+
raise ProtocolError, %(bad type character "#{type}" at position #{@p})
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def parse
|
170
|
+
# If not implemented, user can read message using `getnetstring` and `gets`.
|
171
|
+
end
|
172
|
+
|
173
|
+
def self._dump(args)
|
174
|
+
case args
|
175
|
+
when nil
|
176
|
+
'N'
|
177
|
+
when true
|
178
|
+
'I' + Netstring.dump('1')
|
179
|
+
when false
|
180
|
+
'I' + Netstring.dump('0')
|
181
|
+
when Integer
|
182
|
+
'I' + Netstring.dump(args.to_s)
|
183
|
+
when Float
|
184
|
+
'R' + Netstring.dump(args.to_s)
|
185
|
+
when String
|
186
|
+
'T' + Netstring.dump(args)
|
187
|
+
when Symbol
|
188
|
+
'T' + Netstring.dump(args.to_s)
|
189
|
+
when Array
|
190
|
+
'L' + Netstring.dump(args.size.to_s) + args.map{|arg| _dump(arg)}.join
|
191
|
+
when Hash
|
192
|
+
'A' + Netstring.dump(args.size.to_s) + args.map{|k,v| _dump(k) + _dump(v)}.join
|
193
|
+
else
|
194
|
+
raise InterfaceError, "can't pass #{args.class} over RABX"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class RequestMessage < Message
|
200
|
+
attr_reader :method, :arguments
|
201
|
+
|
202
|
+
def parse
|
203
|
+
@method = getnetstring
|
204
|
+
@arguments = gets
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
class SuccessMessage < Message
|
209
|
+
attr_reader :value
|
210
|
+
|
211
|
+
def parse
|
212
|
+
@value = gets
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
class ErrorMessage < Message
|
217
|
+
attr_reader :code, :text, :extra
|
218
|
+
|
219
|
+
def parse
|
220
|
+
@code = getnetstring
|
221
|
+
@text = getnetstring
|
222
|
+
@extra = gets unless eof?
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/rabx/message/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "rabx-message"
|
6
|
+
s.version = RABX::Message::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["James McKinney"]
|
9
|
+
s.homepage = "http://github.com/jpmckinney/rabx-message"
|
10
|
+
s.summary = %q{A RPC using Anything But XML (RABX) message parser and emitter}
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_runtime_dependency('netstring', '~> 0.0')
|
19
|
+
|
20
|
+
s.add_development_dependency('coveralls')
|
21
|
+
s.add_development_dependency('json', '~> 1.8') # to silence coveralls warning
|
22
|
+
s.add_development_dependency('rake')
|
23
|
+
s.add_development_dependency('rspec', '~> 3.1')
|
24
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RABX::Message do
|
4
|
+
let :invalid_netstring do
|
5
|
+
RABX::Message.new('R1:0,1:x')
|
6
|
+
end
|
7
|
+
|
8
|
+
let :invalid_rabx do
|
9
|
+
RABX::Message.new('S1:0,I1:x')
|
10
|
+
end
|
11
|
+
|
12
|
+
let :n do
|
13
|
+
RABX::Message.new('S1:0,N')
|
14
|
+
end
|
15
|
+
|
16
|
+
let :i do
|
17
|
+
RABX::Message.new('S1:0,I2:34,')
|
18
|
+
end
|
19
|
+
let :invalid_i do
|
20
|
+
RABX::Message.new('S1:0,I2:xy,')
|
21
|
+
end
|
22
|
+
|
23
|
+
let :r do
|
24
|
+
RABX::Message.new('S1:0,R4:1.23,')
|
25
|
+
end
|
26
|
+
let :invalid_r do
|
27
|
+
RABX::Message.new('S1:0,R4:abcd,')
|
28
|
+
end
|
29
|
+
|
30
|
+
let :t do
|
31
|
+
RABX::Message.new('S1:0,T2:xy,')
|
32
|
+
end
|
33
|
+
|
34
|
+
let :b do
|
35
|
+
RABX::Message.new('S1:0,B2:ab,')
|
36
|
+
end
|
37
|
+
|
38
|
+
let :l do
|
39
|
+
RABX::Message.new('S1:0,L1:2,I2:34,T2:xy,')
|
40
|
+
end
|
41
|
+
let :invalid_l_size do
|
42
|
+
RABX::Message.new('S1:0,L1:x,I2:34,T2:xy,')
|
43
|
+
end
|
44
|
+
let :missing_l_item do
|
45
|
+
RABX::Message.new('S1:0,L1:2,I2:34,')
|
46
|
+
end
|
47
|
+
|
48
|
+
let :a do
|
49
|
+
RABX::Message.new('S1:0,A1:2,I2:34,T2:xy,R4:1.23,B2:ab,')
|
50
|
+
end
|
51
|
+
let :invalid_a_size do
|
52
|
+
RABX::Message.new('S1:0,A1:x,I2:34,T2:xy,R4:1.23,B2:ab,')
|
53
|
+
end
|
54
|
+
let :missing_a_item do
|
55
|
+
RABX::Message.new('S1:0,A1:2,I2:34,T2:xy,')
|
56
|
+
end
|
57
|
+
let :missing_a_value do
|
58
|
+
RABX::Message.new('S1:0,A1:2,I2:34,T2:xy,R4:1.23,')
|
59
|
+
end
|
60
|
+
|
61
|
+
let :invalid_type do
|
62
|
+
RABX::Message.new('S1:0,U1:x,')
|
63
|
+
end
|
64
|
+
|
65
|
+
let :empty_message do
|
66
|
+
'S1:0,'
|
67
|
+
end
|
68
|
+
let :empty do
|
69
|
+
RABX::Message.new(empty_message)
|
70
|
+
end
|
71
|
+
|
72
|
+
let :request_message do
|
73
|
+
'R1:0,6:method,L1:2,I2:34,T2:xy,'
|
74
|
+
end
|
75
|
+
let :request do
|
76
|
+
RABX::Message.new(request_message)
|
77
|
+
end
|
78
|
+
|
79
|
+
let :success_message do
|
80
|
+
'S1:0,A1:2,I2:34,T2:xy,R4:1.23,T2:ab,'
|
81
|
+
end
|
82
|
+
let :success do
|
83
|
+
RABX::Message.new(success_message)
|
84
|
+
end
|
85
|
+
|
86
|
+
let :error_message do
|
87
|
+
"E1:0,3:404,9:Not Found,N"
|
88
|
+
end
|
89
|
+
let :error do
|
90
|
+
RABX::Message.new(error_message)
|
91
|
+
end
|
92
|
+
|
93
|
+
let :unknown_message do
|
94
|
+
'U1:0,'
|
95
|
+
end
|
96
|
+
let :unknown do
|
97
|
+
RABX::Message.new(unknown_message)
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '.dump' do
|
101
|
+
it 'should dump a RABX message' do
|
102
|
+
{
|
103
|
+
['S'] => empty_message,
|
104
|
+
['R', 'method', [34, 'xy']] => request_message,
|
105
|
+
['S', {34 => 'xy', 1.23 => 'ab'}] => success_message,
|
106
|
+
['E', 404, 'Not Found'] => error_message,
|
107
|
+
['S', nil] => 'S1:0,N',
|
108
|
+
['S', true] => 'S1:0,I1:1,',
|
109
|
+
['S', false] => 'S1:0,I1:0,',
|
110
|
+
['S', 1] => 'S1:0,I1:1,',
|
111
|
+
['S', 1.1] => 'S1:0,R3:1.1,',
|
112
|
+
['S', 'x'] => 'S1:0,T1:x,',
|
113
|
+
['S', :x] => 'S1:0,T1:x,',
|
114
|
+
['S', []] => 'S1:0,L1:0,',
|
115
|
+
['S', {}] => 'S1:0,A1:0,',
|
116
|
+
}.each do |args,string|
|
117
|
+
expect(RABX::Message.dump(*args)).to eq(string)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '.load' do
|
123
|
+
it 'should load a RABX request message' do
|
124
|
+
message = RABX::Message.load(request_message)
|
125
|
+
expect(message.method).to eq('method')
|
126
|
+
expect(message.arguments).to eq([34, 'xy'])
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should load a RABX success message' do
|
130
|
+
message = RABX::Message.load(success_message)
|
131
|
+
expect(message.value).to eq({34 => 'xy', 1.23 => 'ab'})
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should load a RABX error message' do
|
135
|
+
message = RABX::Message.load(error_message)
|
136
|
+
expect(message.code).to eq('404')
|
137
|
+
expect(message.text).to eq('Not Found')
|
138
|
+
expect(message.extra).to eq(nil)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should raise an error if type is unknown' do
|
142
|
+
expect{RABX::Message.load(unknown_message)}.to raise_error(RABX::Message::ProtocolError, 'unknown RABX message type "U"')
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#initialize' do
|
147
|
+
it 'should initialize a RABX message' do
|
148
|
+
expect{request}.to_not raise_error
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should make a copy of the string' do
|
152
|
+
expect(request.instance_variable_get(:@s)).to eq('R1:0,6:method,L1:2,I2:34,T2:xy,')
|
153
|
+
request_message.reverse!
|
154
|
+
expect(request.instance_variable_get(:@s)).to eq('R1:0,6:method,L1:2,I2:34,T2:xy,')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should raise an error if the string is empty' do
|
158
|
+
expect{RABX::Message.new('')}.to raise_error(RABX::Message::ProtocolError, 'string is too short')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#to_s' do
|
163
|
+
it 'should return the message' do
|
164
|
+
expect(request.to_s).to eq(request_message)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#type' do
|
169
|
+
it 'should return the message type' do
|
170
|
+
expect(request.type).to eq('R')
|
171
|
+
expect(success.type).to eq('S')
|
172
|
+
expect(error.type).to eq('E')
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe '#eof?' do
|
177
|
+
it 'should return true only if the message has been read' do
|
178
|
+
message = RABX::Message.new('S1:0,N')
|
179
|
+
expect(message.eof?).to eq(false)
|
180
|
+
expect(message.gets).to eq(nil)
|
181
|
+
expect(message.eof?).to eq(true)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '#getnetstring' do
|
186
|
+
it 'should read a netstring' do
|
187
|
+
expect(error.getnetstring).to eq('404')
|
188
|
+
expect(error.getnetstring).to eq('Not Found')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'should return nil if at end of message' do
|
192
|
+
expect(empty.getnetstring).to eq(nil)
|
193
|
+
expect(empty.getnetstring).to eq(nil)
|
194
|
+
expect(empty.getnetstring).to eq(nil)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should raise an error if the netstring is invalid' do
|
198
|
+
expect{invalid_netstring.getnetstring}.to raise_error(RABX::Message::ProtocolError)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#gets' do
|
203
|
+
it 'should read a N message' do
|
204
|
+
expect(n.gets).to eq(nil)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should read a I message' do
|
208
|
+
expect(i.gets).to eq(34)
|
209
|
+
end
|
210
|
+
it 'should raise an error if an integer is invalid' do
|
211
|
+
expect{invalid_i.gets}.to raise_error(RABX::Message::ProtocolError, 'expected integer, got "xy" at position 8')
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should read a R message' do
|
215
|
+
expect(r.gets).to eq(1.23)
|
216
|
+
end
|
217
|
+
it 'should raise an error if a float is invalid' do
|
218
|
+
expect{invalid_r.gets}.to raise_error(RABX::Message::ProtocolError, 'expected float, got "abcd" at position 8')
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should read a T message' do
|
222
|
+
expect(t.gets).to eq('xy')
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should read a B message' do
|
226
|
+
expect(b.gets).to eq('ab')
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'should read a L message' do
|
230
|
+
expect(l.gets).to eq([34, 'xy'])
|
231
|
+
end
|
232
|
+
it 'should raise an error if a list size is invalid' do
|
233
|
+
expect{invalid_l_size.gets}.to raise_error(RABX::Message::ProtocolError, 'expected integer, got "x" at position 8')
|
234
|
+
end
|
235
|
+
it 'should raise an error if a list item is missing' do
|
236
|
+
expect{missing_l_item.gets}.to raise_error(RABX::Message::ProtocolError, 'expected 2 items, got 1 items at position 16')
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should read a A message' do
|
240
|
+
expect(a.gets).to eq({34 => 'xy', 1.23 => 'ab'})
|
241
|
+
end
|
242
|
+
it 'should raise an error if a hash size is invalid' do
|
243
|
+
expect{invalid_a_size.gets}.to raise_error(RABX::Message::ProtocolError, 'expected integer, got "x" at position 8')
|
244
|
+
end
|
245
|
+
it 'should raise an error if a hash item is missing' do
|
246
|
+
expect{missing_a_item.gets}.to raise_error(RABX::Message::ProtocolError, 'expected 2 items, got 1 items at position 22')
|
247
|
+
end
|
248
|
+
it 'should raise an error if a hash value is missing' do
|
249
|
+
expect{missing_a_value.gets}.to raise_error(RABX::Message::ProtocolError, 'expected value, got end-of-file at position 30')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should raise an error if the type is invalid' do
|
253
|
+
expect{invalid_type.gets}.to raise_error(RABX::Message::ProtocolError, 'bad type character "U" at position 6')
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should return nil if at end of message' do
|
257
|
+
expect(empty.gets).to eq(nil)
|
258
|
+
expect(empty.gets).to eq(nil)
|
259
|
+
expect(empty.gets).to eq(nil)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should raise an error if the netstring is invalid' do
|
263
|
+
expect{invalid_rabx.gets}.to raise_error(RABX::Message::ProtocolError)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rabx-message
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James McKinney
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: netstring
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coveralls
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.1'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- ".gitignore"
|
90
|
+
- ".rspec"
|
91
|
+
- ".travis.yml"
|
92
|
+
- ".yardopts"
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- USAGE
|
98
|
+
- lib/50418
|
99
|
+
- lib/rabx/message.rb
|
100
|
+
- lib/rabx/message/version.rb
|
101
|
+
- rabx-message.gemspec
|
102
|
+
- spec/message_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
homepage: http://github.com/jpmckinney/rabx-message
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.2.2
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: A RPC using Anything But XML (RABX) message parser and emitter
|
128
|
+
test_files:
|
129
|
+
- spec/message_spec.rb
|
130
|
+
- spec/spec_helper.rb
|