fd-bertrpc 1.3.0
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.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/History.txt +33 -0
- data/LICENSE +20 -0
- data/README.md +71 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/bertrpc.gemspec +70 -0
- data/lib/bertrpc.rb +20 -0
- data/lib/bertrpc/action.rb +113 -0
- data/lib/bertrpc/encodes.rb +38 -0
- data/lib/bertrpc/errors.rb +76 -0
- data/lib/bertrpc/mod.rb +14 -0
- data/lib/bertrpc/request.rb +15 -0
- data/lib/bertrpc/service.rb +40 -0
- data/test/action_test.rb +126 -0
- data/test/encodes_test.rb +58 -0
- data/test/error_test.rb +33 -0
- data/test/mod_test.rb +32 -0
- data/test/request_test.rb +24 -0
- data/test/service_test.rb +47 -0
- data/test/test_helper.rb +12 -0
- metadata +110 -0
data/.document
ADDED
data/History.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= 1.3.0 / 2010-02-24
|
2
|
+
* Enhancements
|
3
|
+
* Raise BERTRPC::ReadTimeout if remote connection is closed
|
4
|
+
|
5
|
+
= 1.2.1 / 2010-02-10
|
6
|
+
* Bug fixes
|
7
|
+
* Restrict to IPv4
|
8
|
+
|
9
|
+
= 1.2.0 / 2010-02-09
|
10
|
+
* Enhancements
|
11
|
+
* Better timeouts courtesy of select(2)
|
12
|
+
* BERTRPCError gives self for original_exception if nil
|
13
|
+
|
14
|
+
= 1.1.2 / 2009-11-27
|
15
|
+
* Minor Changes
|
16
|
+
* Add useful debugging information to ReadTimeoutError
|
17
|
+
* Switch to using raw socket timeouts over buffered io (Linux only)
|
18
|
+
* Add BERTRPC::VERSION and BERTRPC.version
|
19
|
+
|
20
|
+
= 1.1.1 / 2009-10-28
|
21
|
+
Major Changes
|
22
|
+
* Require bert-1.1.0 or greater
|
23
|
+
* Remove dependency on Erlectricity
|
24
|
+
|
25
|
+
= 1.1.0 / 2009-10-27
|
26
|
+
* Add read socket timeout
|
27
|
+
|
28
|
+
= 1.0.0 / 2009-10-19
|
29
|
+
* No changes. Production ready!
|
30
|
+
|
31
|
+
= 0.4.0 / 2009-10-08
|
32
|
+
* Major changes
|
33
|
+
* Convert to use BERT gem.
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Tom Preston-Werner
|
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,71 @@
|
|
1
|
+
BERTRPC
|
2
|
+
=======
|
3
|
+
|
4
|
+
By Tom Preston-Werner (tom@mojombo.com)
|
5
|
+
|
6
|
+
BERT-RPC client library for Ruby. Makes it ridiculously simple to interface
|
7
|
+
with BERT-RPC servers.
|
8
|
+
|
9
|
+
See the full BERT-RPC specification at [bert-rpc.org](http://bert-rpc.org).
|
10
|
+
|
11
|
+
This library currently only supports the following BERT-RPC features:
|
12
|
+
|
13
|
+
* `call` requests
|
14
|
+
* `cast` requests
|
15
|
+
|
16
|
+
BERTRPC was developed for GitHub and is currently in production use performing
|
17
|
+
millions of RPC requests every day. The stability and performance have been
|
18
|
+
exemplary.
|
19
|
+
|
20
|
+
|
21
|
+
Installation
|
22
|
+
------------
|
23
|
+
|
24
|
+
$ gem install bertrpc -s http://gemcutter.org
|
25
|
+
|
26
|
+
|
27
|
+
Examples
|
28
|
+
--------
|
29
|
+
|
30
|
+
Require the library and create a service:
|
31
|
+
|
32
|
+
require 'bertrpc'
|
33
|
+
svc = BERTRPC::Service.new('localhost', 9999)
|
34
|
+
|
35
|
+
Make a call:
|
36
|
+
|
37
|
+
svc.call.calc.add(1, 2)
|
38
|
+
# => 3
|
39
|
+
|
40
|
+
The underlying BERT-RPC transaction of the above call is:
|
41
|
+
|
42
|
+
-> {call, calc, add, [1, 2]}
|
43
|
+
<- {reply, 3}
|
44
|
+
|
45
|
+
Make a cast:
|
46
|
+
|
47
|
+
svc.cast.stats.incr
|
48
|
+
# => nil
|
49
|
+
|
50
|
+
The underlying BERT-RPC transaction of the above cast is:
|
51
|
+
|
52
|
+
-> {cast, stats, incr, []}
|
53
|
+
<- {noreply}
|
54
|
+
|
55
|
+
|
56
|
+
Documentation
|
57
|
+
-------------
|
58
|
+
|
59
|
+
Creating a service:
|
60
|
+
|
61
|
+
# No timeout
|
62
|
+
svc = BERTRPC::Service.new('localhost', 9999)
|
63
|
+
|
64
|
+
# 10s socket read timeout, raises BERTRPC::ReadTimeoutError
|
65
|
+
svc = BERTRPC::Service.new('localhost', 9999, 10)
|
66
|
+
|
67
|
+
|
68
|
+
Copyright
|
69
|
+
---------
|
70
|
+
|
71
|
+
Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "fd-bertrpc"
|
8
|
+
gem.summary = %Q{BERTRPC is a Ruby BERT-RPC client library.}
|
9
|
+
gem.email = "tom@mojombo.com"
|
10
|
+
gem.homepage = "http://github.com/mojombo/bertrpc"
|
11
|
+
gem.authors = ["Tom Preston-Werner"]
|
12
|
+
gem.add_dependency('bert', '>= 1.1.0', '< 2.0.0')
|
13
|
+
# gem is a Gem::Specification...
|
14
|
+
# see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/*_test.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/*_test.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION.yml')
|
46
|
+
config = YAML.load(File.read('VERSION.yml'))
|
47
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
48
|
+
else
|
49
|
+
version = ""
|
50
|
+
end
|
51
|
+
|
52
|
+
rdoc.rdoc_dir = 'rdoc'
|
53
|
+
rdoc.title = "bertrpc #{version}"
|
54
|
+
rdoc.rdoc_files.include('README*')
|
55
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
|
+
end
|
57
|
+
|
58
|
+
task :console do
|
59
|
+
exec('irb -Ilib -rbertrpc')
|
60
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.3.0
|
data/bertrpc.gemspec
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{fd-bertrpc}
|
8
|
+
s.version = "1.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Tom Preston-Werner"]
|
12
|
+
s.date = %q{2010-02-24}
|
13
|
+
s.email = %q{tom@mojombo.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"History.txt",
|
22
|
+
"LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"bertrpc.gemspec",
|
27
|
+
"lib/bertrpc.rb",
|
28
|
+
"lib/bertrpc/action.rb",
|
29
|
+
"lib/bertrpc/encodes.rb",
|
30
|
+
"lib/bertrpc/errors.rb",
|
31
|
+
"lib/bertrpc/mod.rb",
|
32
|
+
"lib/bertrpc/request.rb",
|
33
|
+
"lib/bertrpc/service.rb",
|
34
|
+
"test/action_test.rb",
|
35
|
+
"test/encodes_test.rb",
|
36
|
+
"test/error_test.rb",
|
37
|
+
"test/mod_test.rb",
|
38
|
+
"test/request_test.rb",
|
39
|
+
"test/service_test.rb",
|
40
|
+
"test/test_helper.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/mojombo/bertrpc}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{BERTRPC is a Ruby BERT-RPC client library.}
|
47
|
+
s.test_files = [
|
48
|
+
"test/action_test.rb",
|
49
|
+
"test/encodes_test.rb",
|
50
|
+
"test/error_test.rb",
|
51
|
+
"test/mod_test.rb",
|
52
|
+
"test/request_test.rb",
|
53
|
+
"test/service_test.rb",
|
54
|
+
"test/test_helper.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
62
|
+
s.add_runtime_dependency(%q<bert>, [">= 1.1.0", "< 2.0.0"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<bert>, [">= 1.1.0", "< 2.0.0"])
|
65
|
+
end
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<bert>, [">= 1.1.0", "< 2.0.0"])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
data/lib/bertrpc.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bert'
|
2
|
+
require 'socket'
|
3
|
+
require 'net/protocol'
|
4
|
+
|
5
|
+
require 'bertrpc/service'
|
6
|
+
require 'bertrpc/request'
|
7
|
+
require 'bertrpc/mod'
|
8
|
+
require 'bertrpc/encodes'
|
9
|
+
require 'bertrpc/action'
|
10
|
+
require 'bertrpc/errors'
|
11
|
+
|
12
|
+
module BERTRPC
|
13
|
+
def self.version
|
14
|
+
File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION])).chomp
|
15
|
+
rescue
|
16
|
+
'unknown'
|
17
|
+
end
|
18
|
+
|
19
|
+
VERSION = self.version
|
20
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module BERTRPC
|
2
|
+
class Action
|
3
|
+
include Encodes
|
4
|
+
|
5
|
+
def initialize(svc, req, mod, fun, args)
|
6
|
+
@svc = svc
|
7
|
+
@req = req
|
8
|
+
@mod = mod
|
9
|
+
@fun = fun
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
bert_request = encode_ruby_request(t[@req.kind, @mod, @fun, @args])
|
15
|
+
bert_response = transaction(bert_request)
|
16
|
+
decode_bert_response(bert_response)
|
17
|
+
end
|
18
|
+
|
19
|
+
#private
|
20
|
+
|
21
|
+
def write(sock, bert)
|
22
|
+
sock.write([bert.length].pack("N"))
|
23
|
+
sock.write(bert)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read(sock, len, timeout)
|
27
|
+
data, size = [], 0
|
28
|
+
while size < len
|
29
|
+
r, w, e = IO.select([sock], [], [], timeout)
|
30
|
+
raise Errno::EAGAIN if r.nil?
|
31
|
+
msg, sender = sock.recvfrom(len - size)
|
32
|
+
raise Errno::ECONNRESET if msg.size == 0
|
33
|
+
size += msg.size
|
34
|
+
data << msg
|
35
|
+
end
|
36
|
+
data.join ''
|
37
|
+
end
|
38
|
+
|
39
|
+
def transaction(bert_request)
|
40
|
+
timeout = @svc.timeout && Float(@svc.timeout)
|
41
|
+
sock = connect_to(@svc.host, @svc.port, timeout)
|
42
|
+
|
43
|
+
if @req.options
|
44
|
+
if @req.options[:cache] && @req.options[:cache][0] == :validation
|
45
|
+
token = @req.options[:cache][1]
|
46
|
+
info_bert = encode_ruby_request(t[:info, :cache, [:validation, token]])
|
47
|
+
write(sock, info_bert)
|
48
|
+
end
|
49
|
+
|
50
|
+
if @req.options[:stream]
|
51
|
+
info_bert = encode_ruby_request(t[:info, :stream, []])
|
52
|
+
write(sock, info_bert)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
write(sock, bert_request)
|
57
|
+
|
58
|
+
case (@req.options||{})[:stream]
|
59
|
+
when IO then
|
60
|
+
while blob = stream.read(1024)
|
61
|
+
write(sock, blob)
|
62
|
+
end
|
63
|
+
write(sock, "")
|
64
|
+
when String
|
65
|
+
File.open(stream) do |f|
|
66
|
+
while blob = f.read(1024)
|
67
|
+
write(sock, blob)
|
68
|
+
end
|
69
|
+
write(sock, "")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
lenheader = read(sock, 4, timeout)
|
74
|
+
raise ProtocolError.new(ProtocolError::NO_HEADER) unless lenheader
|
75
|
+
len = lenheader.unpack('N').first
|
76
|
+
bert_response = read(sock, len, timeout)
|
77
|
+
raise ProtocolError.new(ProtocolError::NO_DATA) unless bert_response
|
78
|
+
sock.close
|
79
|
+
bert_response
|
80
|
+
rescue Errno::ECONNREFUSED
|
81
|
+
raise ConnectionError.new(@svc.host, @svc.port)
|
82
|
+
rescue Errno::EAGAIN
|
83
|
+
raise ReadTimeoutError.new(@svc.host, @svc.port, @svc.timeout)
|
84
|
+
rescue Errno::ECONNRESET
|
85
|
+
raise ReadError.new(@svc.host, @svc.port)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Creates a socket object which does speedy, non-blocking reads
|
89
|
+
# and can perform reliable read timeouts.
|
90
|
+
#
|
91
|
+
# Raises Timeout::Error on timeout.
|
92
|
+
#
|
93
|
+
# +host+ String address of the target TCP server
|
94
|
+
# +port+ Integer port of the target TCP server
|
95
|
+
# +timeout+ Optional Integer (in seconds) of the read timeout
|
96
|
+
def connect_to(host, port, timeout = nil)
|
97
|
+
addr = Socket.getaddrinfo(host, nil, Socket::AF_INET)
|
98
|
+
sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
99
|
+
sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
100
|
+
|
101
|
+
if timeout
|
102
|
+
secs = Integer(timeout)
|
103
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
104
|
+
optval = [secs, usecs].pack("l_2")
|
105
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
106
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
107
|
+
end
|
108
|
+
|
109
|
+
sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
|
110
|
+
sock
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module BERTRPC
|
2
|
+
module Encodes
|
3
|
+
def encode_ruby_request(ruby_request)
|
4
|
+
BERT.encode(ruby_request)
|
5
|
+
end
|
6
|
+
|
7
|
+
def decode_bert_response(bert_response)
|
8
|
+
ruby_response = BERT.decode(bert_response)
|
9
|
+
case ruby_response[0]
|
10
|
+
when :reply
|
11
|
+
ruby_response[1]
|
12
|
+
when :noreply
|
13
|
+
nil
|
14
|
+
when :error
|
15
|
+
error(ruby_response[1])
|
16
|
+
else
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(err)
|
22
|
+
level, code, klass, message, backtrace = err
|
23
|
+
|
24
|
+
case level
|
25
|
+
when :protocol
|
26
|
+
raise ProtocolError.new([code, message], klass, backtrace)
|
27
|
+
when :server
|
28
|
+
raise ServerError.new([code, message], klass, backtrace)
|
29
|
+
when :user
|
30
|
+
raise UserError.new([code, message], klass, backtrace)
|
31
|
+
when :proxy
|
32
|
+
raise ProxyError.new([code, message], klass, backtrace)
|
33
|
+
else
|
34
|
+
raise
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module BERTRPC
|
2
|
+
class BERTRPCError < StandardError
|
3
|
+
attr_accessor :code, :original_exception
|
4
|
+
|
5
|
+
def initialize(msg = nil, klass = nil, bt = [])
|
6
|
+
case msg
|
7
|
+
when Array
|
8
|
+
code, message = msg
|
9
|
+
else
|
10
|
+
code, message = [0, msg]
|
11
|
+
end
|
12
|
+
|
13
|
+
if klass
|
14
|
+
self.original_exception = RemoteError.new("#{klass}: #{message}")
|
15
|
+
self.original_exception.set_backtrace(bt)
|
16
|
+
else
|
17
|
+
self.original_exception = self
|
18
|
+
end
|
19
|
+
|
20
|
+
self.code = code
|
21
|
+
super(message)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class RemoteError < StandardError
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class ConnectionError < BERTRPCError
|
30
|
+
attr_reader :host, :port
|
31
|
+
def initialize(host, port)
|
32
|
+
@host, @port = host, port
|
33
|
+
super("Unable to connect to #{host}:#{port}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Raised when we don't get a response from a server in a timely
|
38
|
+
# manner. This typically occurs in spite of a successful connection.
|
39
|
+
class ReadTimeoutError < BERTRPCError
|
40
|
+
attr_reader :host, :port, :timeout
|
41
|
+
def initialize(host, port, timeout)
|
42
|
+
@host, @port, @timeout = host, port, timeout
|
43
|
+
super("No response from #{host}:#{port} in #{timeout}s")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Raised when unexpected EOF is reached on the socket.
|
48
|
+
class ReadError < BERTRPCError
|
49
|
+
attr_reader :host, :port
|
50
|
+
def initialize(host, port)
|
51
|
+
@host, @port = host, port
|
52
|
+
super("Unable to read from #{host}:#{port}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ProtocolError < BERTRPCError
|
57
|
+
NO_HEADER = [0, "Unable to read length header from server."]
|
58
|
+
NO_DATA = [1, "Unable to read data from server."]
|
59
|
+
end
|
60
|
+
|
61
|
+
class ServerError < BERTRPCError
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class UserError < BERTRPCError
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
class ProxyError < BERTRPCError
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class InvalidOption < BERTRPCError
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/bertrpc/mod.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module BERTRPC
|
2
|
+
class Service
|
3
|
+
attr_accessor :host, :port, :timeout
|
4
|
+
|
5
|
+
def initialize(host, port, timeout = nil)
|
6
|
+
@host = host
|
7
|
+
@port = port
|
8
|
+
@timeout = timeout
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(options = nil)
|
12
|
+
verify_options(options)
|
13
|
+
Request.new(self, :call, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cast(options = nil)
|
17
|
+
verify_options(options)
|
18
|
+
Request.new(self, :cast, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# private
|
22
|
+
|
23
|
+
def verify_options(options)
|
24
|
+
(options||{}).each do |key, value|
|
25
|
+
case key
|
26
|
+
when :stream
|
27
|
+
unless (String === value and File.file?(value))
|
28
|
+
raise InvalidOption.new("Valid :stream args are IO or path")
|
29
|
+
end
|
30
|
+
when :case
|
31
|
+
unless value[0] == :validation && value[1].is_a?(String)
|
32
|
+
raise InvalidOption.new("Valid :cache args are [:validation, String]")
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise InvalidOption.new("Valid options are :cache and :stream")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/test/action_test.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ActionTest < Test::Unit::TestCase
|
4
|
+
context "An Action" do
|
5
|
+
setup do
|
6
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
7
|
+
@req = @svc.call
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be created with a Service, module name, fun name, and args" do
|
11
|
+
assert BERTRPC::Action.new(@svc, @req, :mymod, :myfun, [1, 2]).is_a?(BERTRPC::Action)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "An Action instance" do
|
16
|
+
setup do
|
17
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
18
|
+
@req = @svc.call
|
19
|
+
@enc = Enc.new
|
20
|
+
end
|
21
|
+
|
22
|
+
should "call with single-arity" do
|
23
|
+
req = @enc.encode_ruby_request(t[:call, :mymod, :myfun, [1]])
|
24
|
+
res = @enc.encode_ruby_request(t[:reply, 2])
|
25
|
+
call = BERTRPC::Action.new(@svc, @req, :mymod, :myfun, [1])
|
26
|
+
call.expects(:transaction).with(req).returns(res)
|
27
|
+
assert_equal 2, call.execute
|
28
|
+
end
|
29
|
+
|
30
|
+
should "call with single-arity array" do
|
31
|
+
req = @enc.encode_ruby_request(t[:call, :mymod, :myfun, [[1, 2, 3]]])
|
32
|
+
res = @enc.encode_ruby_request(t[:reply, [4, 5, 6]])
|
33
|
+
call = BERTRPC::Action.new(@svc, @req, :mymod, :myfun, [[1, 2, 3]])
|
34
|
+
call.expects(:transaction).with(req).returns(res)
|
35
|
+
assert_equal [4, 5, 6], call.execute
|
36
|
+
end
|
37
|
+
|
38
|
+
should "call with multi-arity" do
|
39
|
+
req = @enc.encode_ruby_request(t[:call, :mymod, :myfun, [1, 2, 3]])
|
40
|
+
res = @enc.encode_ruby_request(t[:reply, [4, 5, 6]])
|
41
|
+
call = BERTRPC::Action.new(@svc, @req, :mymod, :myfun, [1, 2, 3])
|
42
|
+
call.expects(:transaction).with(req).returns(res)
|
43
|
+
assert_equal [4, 5, 6], call.execute
|
44
|
+
end
|
45
|
+
|
46
|
+
context "sync_request" do
|
47
|
+
setup do
|
48
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
49
|
+
@req = @svc.call
|
50
|
+
@call = BERTRPC::Action.new(@svc, @req, :mymod, :myfun, [])
|
51
|
+
end
|
52
|
+
|
53
|
+
should "read and write BERT-Ps from the socket" do
|
54
|
+
io = stub()
|
55
|
+
io.expects(:write).with("\000\000\000\003")
|
56
|
+
io.expects(:write).with("foo")
|
57
|
+
@call.expects(:read).with(io, 4, nil).returns("\000\000\000\003")
|
58
|
+
@call.expects(:read).with(io, 3, nil).returns("bar")
|
59
|
+
io.expects(:close)
|
60
|
+
@call.expects(:connect_to).returns(io)
|
61
|
+
assert_equal "bar", @call.transaction("foo")
|
62
|
+
end
|
63
|
+
|
64
|
+
should "raise a ProtocolError when the length is invalid" do
|
65
|
+
io = stub()
|
66
|
+
io.expects(:write).with("\000\000\000\003")
|
67
|
+
io.expects(:write).with("foo")
|
68
|
+
@call.expects(:read).with(io, 4, nil).returns(nil)
|
69
|
+
@call.expects(:connect_to).returns(io)
|
70
|
+
begin
|
71
|
+
@call.transaction("foo")
|
72
|
+
fail "Should have thrown an error"
|
73
|
+
rescue BERTRPC::ProtocolError => e
|
74
|
+
assert_equal 0, e.code
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
should "raise a ProtocolError when the data is invalid" do
|
79
|
+
io = stub()
|
80
|
+
io.expects(:write).with("\000\000\000\003")
|
81
|
+
io.expects(:write).with("foo")
|
82
|
+
@call.expects(:read).with(io, 4, nil).returns("\000\000\000\003")
|
83
|
+
@call.expects(:read).with(io, 3, nil).returns(nil)
|
84
|
+
@call.expects(:connect_to).returns(io)
|
85
|
+
begin
|
86
|
+
@call.transaction("foo")
|
87
|
+
fail "Should have thrown an error"
|
88
|
+
rescue BERTRPC::ProtocolError => e
|
89
|
+
assert_equal 1, e.code
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
should "raise a ReadTimeoutError when the connection times out" do
|
94
|
+
io = stub()
|
95
|
+
io.expects(:write).with("\000\000\000\003")
|
96
|
+
io.expects(:write).with("foo")
|
97
|
+
@call.expects(:read).with(io, 4, nil).raises(Errno::EAGAIN)
|
98
|
+
@call.expects(:connect_to).returns(io)
|
99
|
+
begin
|
100
|
+
@call.transaction("foo")
|
101
|
+
fail "Should have thrown an error"
|
102
|
+
rescue BERTRPC::ReadTimeoutError => e
|
103
|
+
assert_equal 0, e.code
|
104
|
+
assert_equal 'localhost', e.host
|
105
|
+
assert_equal 9941, e.port
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
should "raise a ReadError when the socket becomes unreadable" do
|
110
|
+
io = stub()
|
111
|
+
io.expects(:write).with("\000\000\000\003")
|
112
|
+
io.expects(:write).with("foo")
|
113
|
+
@call.expects(:read).with(io, 4, nil).raises(Errno::ECONNRESET)
|
114
|
+
@call.expects(:connect_to).returns(io)
|
115
|
+
begin
|
116
|
+
@call.transaction("foo")
|
117
|
+
fail "Should have thrown an error"
|
118
|
+
rescue BERTRPC::ReadError => e
|
119
|
+
assert_equal 0, e.code
|
120
|
+
assert_equal 'localhost', e.host
|
121
|
+
assert_equal 9941, e.port
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EncodesTest < Test::Unit::TestCase
|
4
|
+
context "An Encodes includer" do
|
5
|
+
setup do
|
6
|
+
@enc = Enc.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context "ruby request encoder" do
|
10
|
+
should "return BERT-RPC encoded request" do
|
11
|
+
bert = "\203h\004d\000\004calld\000\005mymodd\000\005myfunl\000\000\000\003a\001a\002a\003j"
|
12
|
+
assert_equal bert, @enc.encode_ruby_request(t[:call, :mymod, :myfun, [1, 2, 3]])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "bert response decoder" do
|
17
|
+
should "return response when reply" do
|
18
|
+
req = @enc.encode_ruby_request(t[:reply, [1, 2, 3]])
|
19
|
+
res = @enc.decode_bert_response(req)
|
20
|
+
assert_equal [1, 2, 3], res
|
21
|
+
end
|
22
|
+
|
23
|
+
should "return nil when noreply" do
|
24
|
+
req = @enc.encode_ruby_request(t[:noreply])
|
25
|
+
res = @enc.decode_bert_response(req)
|
26
|
+
assert_equal nil, res
|
27
|
+
end
|
28
|
+
|
29
|
+
should "raise a ProtocolError error when protocol level error is returned" do
|
30
|
+
req = @enc.encode_ruby_request(t[:error, [:protocol, 1, "class", "invalid", []]])
|
31
|
+
assert_raises(BERTRPC::ProtocolError) do
|
32
|
+
@enc.decode_bert_response(req)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
should "raise a ServerError error when server level error is returned" do
|
37
|
+
req = @enc.encode_ruby_request(t[:error, [:server, 1, "class", "invalid", []]])
|
38
|
+
assert_raises(BERTRPC::ServerError) do
|
39
|
+
@enc.decode_bert_response(req)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
should "raise a UserError error when user level error is returned" do
|
44
|
+
req = @enc.encode_ruby_request([:error, [:user, 1, "class", "invalid", []]])
|
45
|
+
assert_raises(BERTRPC::UserError) do
|
46
|
+
@enc.decode_bert_response(req)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
should "raise a ProxyError error when proxy level error is returned" do
|
51
|
+
req = @enc.encode_ruby_request([:error, [:proxy, 1, "class", "invalid", []]])
|
52
|
+
assert_raises(BERTRPC::ProxyError) do
|
53
|
+
@enc.decode_bert_response(req)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/test/error_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ErrorTest < Test::Unit::TestCase
|
4
|
+
context "Errors in general" do
|
5
|
+
should "be creatable with just a message string" do
|
6
|
+
begin
|
7
|
+
raise BERTRPC::BERTRPCError.new('msg')
|
8
|
+
rescue Object => e
|
9
|
+
assert_equal "msg", e.message
|
10
|
+
assert_equal 0, e.code
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
should "be creatable with a [code, message] array" do
|
15
|
+
begin
|
16
|
+
raise BERTRPC::BERTRPCError.new([7, 'msg'])
|
17
|
+
rescue Object => e
|
18
|
+
assert_equal "msg", e.message
|
19
|
+
assert_equal 7, e.code
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should "record the original exception" do
|
24
|
+
begin
|
25
|
+
raise BERTRPC::BERTRPCError.new('msg', 'Error', ['foo', 'bar'])
|
26
|
+
rescue Object => e
|
27
|
+
assert_equal "msg", e.message
|
28
|
+
assert_equal "Error: msg", e.original_exception.message
|
29
|
+
assert_equal ['foo', 'bar'], e.original_exception.backtrace
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/test/mod_test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ModTest < Test::Unit::TestCase
|
4
|
+
context "A Mod" do
|
5
|
+
setup do
|
6
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
7
|
+
@req = @svc.call
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be created with a Service, request and module name" do
|
11
|
+
assert BERTRPC::Mod.new(@svc, @req, :mymod).is_a?(BERTRPC::Mod)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "A Mod instance" do
|
16
|
+
setup do
|
17
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
18
|
+
@req = @svc.call
|
19
|
+
@mod = BERTRPC::Mod.new(@svc, @req, :mymod)
|
20
|
+
end
|
21
|
+
|
22
|
+
should "call execute on the type" do
|
23
|
+
m = mock(:execute => nil)
|
24
|
+
BERTRPC::Action.expects(:new).with(@svc, @req, :mymod, :myfun, [1, 2, 3]).returns(m)
|
25
|
+
@mod.myfun(1, 2, 3)
|
26
|
+
|
27
|
+
m = mock(:execute => nil)
|
28
|
+
BERTRPC::Action.expects(:new).with(@svc, @req, :mymod, :myfun, [1]).returns(m)
|
29
|
+
@mod.myfun(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
4
|
+
context "A Request" do
|
5
|
+
setup do
|
6
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
7
|
+
end
|
8
|
+
|
9
|
+
should "be created with a Service and type" do
|
10
|
+
assert BERTRPC::Request.new(@svc, :call, nil).is_a?(BERTRPC::Request)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "A Request instance" do
|
15
|
+
setup do
|
16
|
+
svc = BERTRPC::Service.new('localhost', 9941)
|
17
|
+
@req = BERTRPC::Request.new(@svc, :call, nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
should "return a Mod instance" do
|
21
|
+
assert @req.myfun.is_a?(BERTRPC::Mod)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ServiceTest < Test::Unit::TestCase
|
4
|
+
context "A Service" do
|
5
|
+
should "be creatable with host and port" do
|
6
|
+
svc = BERTRPC::Service.new('localhost', 9941)
|
7
|
+
assert svc.is_a?(BERTRPC::Service)
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be creatable with host, port, and timeout" do
|
11
|
+
svc = BERTRPC::Service.new('localhost', 9941, 5)
|
12
|
+
assert svc.is_a?(BERTRPC::Service)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "A Service Instance's" do
|
17
|
+
setup do
|
18
|
+
@svc = BERTRPC::Service.new('localhost', 9941)
|
19
|
+
end
|
20
|
+
|
21
|
+
context "accessors" do
|
22
|
+
should "return it's host" do
|
23
|
+
assert_equal 'localhost', @svc.host
|
24
|
+
end
|
25
|
+
|
26
|
+
should "return it's port" do
|
27
|
+
assert_equal 9941, @svc.port
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "call method" do
|
32
|
+
should "return a call type Request" do
|
33
|
+
req = @svc.call
|
34
|
+
assert req.is_a?(BERTRPC::Request)
|
35
|
+
assert_equal :call, req.kind
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "cast method" do
|
40
|
+
should "return a cast type Request" do
|
41
|
+
req = @svc.cast
|
42
|
+
assert req.is_a?(BERTRPC::Request)
|
43
|
+
assert_equal :cast, req.kind
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'bertrpc'
|
9
|
+
|
10
|
+
class Enc
|
11
|
+
include BERTRPC::Encodes
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fd-bertrpc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 1.3.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Tom Preston-Werner
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-01 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: bert
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 1
|
30
|
+
- 0
|
31
|
+
version: 1.1.0
|
32
|
+
- - <
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
segments:
|
35
|
+
- 2
|
36
|
+
- 0
|
37
|
+
- 0
|
38
|
+
version: 2.0.0
|
39
|
+
type: :runtime
|
40
|
+
version_requirements: *id001
|
41
|
+
description:
|
42
|
+
email: tom@mojombo.com
|
43
|
+
executables: []
|
44
|
+
|
45
|
+
extensions: []
|
46
|
+
|
47
|
+
extra_rdoc_files:
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
files:
|
51
|
+
- .document
|
52
|
+
- .gitignore
|
53
|
+
- History.txt
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- VERSION
|
58
|
+
- bertrpc.gemspec
|
59
|
+
- lib/bertrpc.rb
|
60
|
+
- lib/bertrpc/action.rb
|
61
|
+
- lib/bertrpc/encodes.rb
|
62
|
+
- lib/bertrpc/errors.rb
|
63
|
+
- lib/bertrpc/mod.rb
|
64
|
+
- lib/bertrpc/request.rb
|
65
|
+
- lib/bertrpc/service.rb
|
66
|
+
- test/action_test.rb
|
67
|
+
- test/encodes_test.rb
|
68
|
+
- test/error_test.rb
|
69
|
+
- test/mod_test.rb
|
70
|
+
- test/request_test.rb
|
71
|
+
- test/service_test.rb
|
72
|
+
- test/test_helper.rb
|
73
|
+
has_rdoc: true
|
74
|
+
homepage: http://github.com/mojombo/bertrpc
|
75
|
+
licenses: []
|
76
|
+
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --charset=UTF-8
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.3.6
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: BERTRPC is a Ruby BERT-RPC client library.
|
103
|
+
test_files:
|
104
|
+
- test/action_test.rb
|
105
|
+
- test/encodes_test.rb
|
106
|
+
- test/error_test.rb
|
107
|
+
- test/mod_test.rb
|
108
|
+
- test/request_test.rb
|
109
|
+
- test/service_test.rb
|
110
|
+
- test/test_helper.rb
|