rabx-message 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/rabx-message.svg)](http://badge.fury.io/rb/rabx-message)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/jpmckinney/rabx-message.png)](http://travis-ci.org/jpmckinney/rabx-message)
|
5
|
+
[![Dependency Status](https://gemnasium.com/jpmckinney/rabx-message.png)](https://gemnasium.com/jpmckinney/rabx-message)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/jpmckinney/rabx-message/badge.png)](https://coveralls.io/r/jpmckinney/rabx-message)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/jpmckinney/rabx-message.png)](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
|