fd-bertrpc 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.md
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
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
@@ -0,0 +1,14 @@
1
+ module BERTRPC
2
+ class Mod
3
+ def initialize(svc, req, mod)
4
+ @svc = svc
5
+ @req = req
6
+ @mod = mod
7
+ end
8
+
9
+ def method_missing(cmd, *args)
10
+ args = [*args]
11
+ Action.new(@svc, @req, @mod, cmd, args).execute
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module BERTRPC
2
+ class Request
3
+ attr_accessor :kind, :options
4
+
5
+ def initialize(svc, kind, options)
6
+ @svc = svc
7
+ @kind = kind
8
+ @options = options
9
+ end
10
+
11
+ def method_missing(cmd, *args)
12
+ Mod.new(@svc, self, cmd)
13
+ end
14
+ end
15
+ end
@@ -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
@@ -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
@@ -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
@@ -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