sinatra-rpc 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +113 -0
- data/Rakefile +6 -0
- data/lib/sinatra/rpc.rb +99 -0
- data/lib/sinatra/rpc/fault.rb +39 -0
- data/lib/sinatra/rpc/handler/echo.rb +17 -0
- data/lib/sinatra/rpc/handler/introspection.rb +44 -0
- data/lib/sinatra/rpc/helpers.rb +70 -0
- data/lib/sinatra/rpc/serializer.rb +31 -0
- data/lib/sinatra/rpc/serializer/base.rb +52 -0
- data/lib/sinatra/rpc/serializer/xmlrpc.rb +37 -0
- data/lib/sinatra/rpc/utils.rb +107 -0
- data/lib/sinatra/rpc/version.rb +5 -0
- data/sinatra-rpc.gemspec +30 -0
- data/spec/sinatra/rpc/fault_spec.rb +10 -0
- data/spec/sinatra/rpc/handler/echo_spec.rb +12 -0
- data/spec/sinatra/rpc/handler/introspection_spec.rb +74 -0
- data/spec/sinatra/rpc/helpers_spec.rb +148 -0
- data/spec/sinatra/rpc/serializer/base_spec.rb +50 -0
- data/spec/sinatra/rpc/serializer/xmlrpc_spec.rb +20 -0
- data/spec/sinatra/rpc/serializer_spec.rb +47 -0
- data/spec/sinatra/rpc/utils_spec.rb +166 -0
- data/spec/sinatra/rpc_spec.rb +115 -0
- data/spec/spec_helper.rb +12 -0
- metadata +198 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Sinatra
|
|
2
|
+
module RPC
|
|
3
|
+
# All the classes defined in this module represent serialization
|
|
4
|
+
# mechanisms for RPC requests/responses.
|
|
5
|
+
module Serializer
|
|
6
|
+
|
|
7
|
+
# Find the right Serializer::Base subclass for the given
|
|
8
|
+
# Content-Type HTTP request header.
|
|
9
|
+
#
|
|
10
|
+
# @param content_type [String] the value of the Content-Type header
|
|
11
|
+
# @return [Class] a Serializer class that can be used to
|
|
12
|
+
# satisfy the request
|
|
13
|
+
def find(content_type)
|
|
14
|
+
@registry[content_type] or @registry[nil]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Add a serializer for a list of content types to the
|
|
18
|
+
# internal registry of Serializer classes.
|
|
19
|
+
def register(serializer_class, content_types)
|
|
20
|
+
@registry ||= {}
|
|
21
|
+
content_types.each do |c|
|
|
22
|
+
@registry[c] = serializer_class
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
extend self
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
require "sinatra/rpc/serializer/xmlrpc"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Sinatra
|
|
2
|
+
module RPC
|
|
3
|
+
module Serializer
|
|
4
|
+
# The base class for all Serializer instances.
|
|
5
|
+
class Base
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
attr_reader :response_content_type
|
|
9
|
+
|
|
10
|
+
# Set the list of content types supported by this serializer.
|
|
11
|
+
# @param content_types [*String] the list of supported content types;
|
|
12
|
+
# if set to `nil`, this serializer is used as a default in case the
|
|
13
|
+
# content type is not specified in the request.
|
|
14
|
+
def content_types(*content_types)
|
|
15
|
+
Sinatra::RPC::Serializer.register self, content_types
|
|
16
|
+
@response_content_type = content_types.compact.first
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# The content type that should be set in responses. By default
|
|
21
|
+
# it is the first from the list of content types defined by the class.
|
|
22
|
+
# @return [String] the content type to set in the response header.
|
|
23
|
+
def content_type
|
|
24
|
+
self.class.response_content_type
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# An hash of options to set with the response content type.
|
|
28
|
+
# For example, {charset: 'utf-8'} is used in XML-RPC.
|
|
29
|
+
# The default implementation returns an empty hash.
|
|
30
|
+
def content_type_options
|
|
31
|
+
{}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Parse an incoming RPC request. This method must be implemented by
|
|
35
|
+
# subclasses.
|
|
36
|
+
# @param request [String] the body of the HTTP POST request.
|
|
37
|
+
# @return [Array] an array of the form ['handler.rpcMethod', [arg1, arg2, ...]]
|
|
38
|
+
def parse(request)
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Convert the response object to a string to be used in the body of
|
|
43
|
+
# the HTTP response. Must be implemented by subclasses.
|
|
44
|
+
# @param response [Object] any response object
|
|
45
|
+
# @return [String] a string representation of the response
|
|
46
|
+
def dump(response)
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'sinatra/rpc/serializer/base'
|
|
2
|
+
require 'xmlrpc/marshal'
|
|
3
|
+
|
|
4
|
+
module Sinatra
|
|
5
|
+
module RPC
|
|
6
|
+
module Serializer
|
|
7
|
+
# This class handles XML-RPC calls.
|
|
8
|
+
class XMLRPC < Base
|
|
9
|
+
content_types nil, 'text/xml'
|
|
10
|
+
|
|
11
|
+
# This initializer creates an internal XMLRPC::Marshal instance.
|
|
12
|
+
def initialize
|
|
13
|
+
@xmlrpc = ::XMLRPC::Marshal.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The charset is set to UTF-8.
|
|
17
|
+
# (see Base#content_type_options)
|
|
18
|
+
def content_type_options
|
|
19
|
+
{charset: 'utf-8'}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# (see Base#parse)
|
|
23
|
+
def parse(request)
|
|
24
|
+
@xmlrpc.load_call(request)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# (see Base#dump)
|
|
28
|
+
def dump(response)
|
|
29
|
+
if Sinatra::RPC::Fault === response
|
|
30
|
+
response = ::XMLRPC::FaultException.new(response.code, response.message)
|
|
31
|
+
end
|
|
32
|
+
@xmlrpc.dump_response(response)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require 'method_source'
|
|
2
|
+
module Sinatra
|
|
3
|
+
module RPC
|
|
4
|
+
module Utils
|
|
5
|
+
# Returns the camelcase version of the given string.
|
|
6
|
+
#
|
|
7
|
+
# @param string [String] the string to convert
|
|
8
|
+
# @param uppercase_first_letter [Boolean] set to true if the first letter of the
|
|
9
|
+
# result needs to be uppercase
|
|
10
|
+
# @return [String] the converted string
|
|
11
|
+
# @example
|
|
12
|
+
#
|
|
13
|
+
# Sinatra::RPC::Utils.camelize 'my_test_method', false # => 'myTestMethod'
|
|
14
|
+
# Sinatra::RPC::Utils.camelize 'test_class' # => 'TestClass'
|
|
15
|
+
#
|
|
16
|
+
def camelize(string, uppercase_first_letter = true)
|
|
17
|
+
tokens = string.to_s.split /_+/
|
|
18
|
+
first_token = (uppercase_first_letter ? tokens.first.capitalize : tokens.first.downcase)
|
|
19
|
+
([first_token] + tokens[1..-1].map(&:capitalize)).join
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Converts a camelcase string to its underscore version.
|
|
23
|
+
#
|
|
24
|
+
# @param string [String] the string to convert
|
|
25
|
+
# @return [String] the converted string
|
|
26
|
+
def underscore(string)
|
|
27
|
+
word = string.to_s.dup
|
|
28
|
+
word.gsub!(/::/, '/')
|
|
29
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
|
30
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
|
31
|
+
word.tr!("-", "_")
|
|
32
|
+
word.downcase!
|
|
33
|
+
word
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Extract the documentation for a given object method.
|
|
37
|
+
#
|
|
38
|
+
# @param method [Method] a method object
|
|
39
|
+
# @return [String] the method help, without initial comment characters (#)
|
|
40
|
+
def method_help(method)
|
|
41
|
+
method.comment.gsub(/^#\s/m, '').strip
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Extract the signature for a given object method, as a list of string starting
|
|
45
|
+
# with the return type. The information is retrieved only by parsing the documentation,
|
|
46
|
+
# and the value [['nil']] is returned if no documentation is available.
|
|
47
|
+
#
|
|
48
|
+
# @param method [Method] a method object
|
|
49
|
+
# @return [String] the method signature as a list of strings
|
|
50
|
+
def method_signature(method)
|
|
51
|
+
help = method_help(method)
|
|
52
|
+
params = []
|
|
53
|
+
ret = nil
|
|
54
|
+
help.each_line do |l|
|
|
55
|
+
case l
|
|
56
|
+
when /@param[\s\w]+\[(\w+).*\]/
|
|
57
|
+
params << $1.downcase
|
|
58
|
+
when /@return[\s\w]+\[(\w+).*\]/
|
|
59
|
+
ret = $1.downcase
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
ret ||= 'nil'
|
|
63
|
+
[[ret] + params]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Return a hash with all the methods in an object that can be exposed as
|
|
67
|
+
# RPC methods as keys. The values are themselves hashes containing the
|
|
68
|
+
# object, the name of the Ruby method, a method description and its signature.
|
|
69
|
+
#
|
|
70
|
+
# @example
|
|
71
|
+
#
|
|
72
|
+
# # A simple class.
|
|
73
|
+
# class MyClass
|
|
74
|
+
# # A simple method.
|
|
75
|
+
# # @param folks [String] people to greet
|
|
76
|
+
# # @return [String] the greeting
|
|
77
|
+
# def greet(folks); "hi, #{folks}!"; end
|
|
78
|
+
# end
|
|
79
|
+
#
|
|
80
|
+
# Sinatra::RPC::Utils.rpc_methods 'myclass', MyClass.new
|
|
81
|
+
# # => {'myclass.myMethod' => {
|
|
82
|
+
# # handler: <MyClass instance>,
|
|
83
|
+
# # method: :my_method,
|
|
84
|
+
# # help: "A simple method.\n@param folks [String] ...",
|
|
85
|
+
# # signature: [['string', 'string']]
|
|
86
|
+
# # }
|
|
87
|
+
def rpc_methods(namespace = nil, object)
|
|
88
|
+
public_methods = object.class.instance_methods - Object.instance_methods
|
|
89
|
+
method_index = {}
|
|
90
|
+
public_methods.each do |method_name|
|
|
91
|
+
method = object.class.instance_method(method_name)
|
|
92
|
+
rpc_name = camelize method_name, false
|
|
93
|
+
key = [namespace, rpc_name].compact.join '.'
|
|
94
|
+
method_index[key] = {
|
|
95
|
+
handler: object,
|
|
96
|
+
method: method_name,
|
|
97
|
+
help: method_help(method),
|
|
98
|
+
signature: method_signature(method)
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
method_index
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
extend self
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
data/sinatra-rpc.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'sinatra/rpc/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "sinatra-rpc"
|
|
8
|
+
spec.version = Sinatra::RPC::VERSION
|
|
9
|
+
spec.authors = ["Andrea Bernardo Ciddio"]
|
|
10
|
+
spec.email = ["bcandrea@gmail.com"]
|
|
11
|
+
spec.description = %q{A Sinatra extension module providing RPC server functionality}
|
|
12
|
+
spec.summary = %q{The Sinatra::RPC module provides an extension that can be used to build RPC endpoints.}
|
|
13
|
+
spec.homepage = "https://github.com/bcandrea/sinatra-rpc"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_runtime_dependency "method_source", "~> 0.8.0"
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
24
|
+
spec.add_development_dependency "rake"
|
|
25
|
+
spec.add_development_dependency "rspec"
|
|
26
|
+
spec.add_development_dependency "yard"
|
|
27
|
+
spec.add_development_dependency "coveralls"
|
|
28
|
+
spec.add_development_dependency "rack-test"
|
|
29
|
+
spec.add_development_dependency "sinatra"
|
|
30
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Sinatra::RPC::Fault do
|
|
4
|
+
it 'should generate exception classes' do
|
|
5
|
+
Sinatra::RPC::Fault.register :very_bad_request, 400
|
|
6
|
+
Sinatra::RPC::VeryBadRequestFault::CODE.should == 400
|
|
7
|
+
RuntimeError.should === Sinatra::RPC::VeryBadRequestFault.new
|
|
8
|
+
Sinatra::RPC::Fault.should === Sinatra::RPC::VeryBadRequestFault.new
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Sinatra::RPC::Handler::Introspection do
|
|
4
|
+
|
|
5
|
+
before(:each) do
|
|
6
|
+
index = {
|
|
7
|
+
'ns.myMethod' => {
|
|
8
|
+
handler: nil,
|
|
9
|
+
method: :my_method,
|
|
10
|
+
help: "This is a test method.",
|
|
11
|
+
signature: [['nil']]
|
|
12
|
+
},
|
|
13
|
+
'ns.greet' => {
|
|
14
|
+
handler: nil,
|
|
15
|
+
method: :greet,
|
|
16
|
+
help: %q{
|
|
17
|
+
Full method doc for greet.
|
|
18
|
+
@param folks [String] people to greet
|
|
19
|
+
@return [String] the greeting
|
|
20
|
+
}.strip,
|
|
21
|
+
signature: [['string', 'string']]
|
|
22
|
+
},
|
|
23
|
+
'ns.bye' => {
|
|
24
|
+
handler: nil,
|
|
25
|
+
method: :bye,
|
|
26
|
+
help: %q{
|
|
27
|
+
Partially documented.
|
|
28
|
+
@param people [String] the people
|
|
29
|
+
}.strip,
|
|
30
|
+
signature: [['nil', 'string']]
|
|
31
|
+
},
|
|
32
|
+
'ns.soLong' => {
|
|
33
|
+
handler: nil,
|
|
34
|
+
method: :so_long,
|
|
35
|
+
help: %q{
|
|
36
|
+
Partially documented.
|
|
37
|
+
@return [String] the result
|
|
38
|
+
}.strip,
|
|
39
|
+
signature: [['string']]
|
|
40
|
+
},
|
|
41
|
+
'ns.multi' => {
|
|
42
|
+
handler: nil,
|
|
43
|
+
method: :multi,
|
|
44
|
+
help: %q{
|
|
45
|
+
Multiple types.
|
|
46
|
+
@param arg [String, Class] the arg
|
|
47
|
+
@return [Array] the result
|
|
48
|
+
}.strip,
|
|
49
|
+
signature: [['array', 'string']]
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
@settings = double('settings', rpc_method_index: index)
|
|
53
|
+
@app = double('app', settings: @settings)
|
|
54
|
+
@intro = Sinatra::RPC::Handler::Introspection.new @app
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should list all the methods in the correct order' do
|
|
58
|
+
@intro.list_methods.should == %w{ ns.bye ns.greet ns.multi ns.myMethod ns.soLong }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should get the method signatures' do
|
|
62
|
+
@intro.method_signature('ns.multi').should == [['array', 'string']]
|
|
63
|
+
@intro.method_signature('ns.greet').should == [['string', 'string']]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'should get the method help' do
|
|
67
|
+
@intro.method_help('ns.myMethod').should == "This is a test method."
|
|
68
|
+
@intro.method_help('ns.soLong').should == %q{
|
|
69
|
+
Partially documented.
|
|
70
|
+
@return [String] the result
|
|
71
|
+
}.strip
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Sinatra::RPC::Helpers do
|
|
4
|
+
|
|
5
|
+
before(:all) do
|
|
6
|
+
@registry = Sinatra::RPC::Serializer.instance_variable_get('@registry').dup
|
|
7
|
+
module HelpersTest
|
|
8
|
+
class Serializer1 < Sinatra::RPC::Serializer::Base
|
|
9
|
+
content_types 'application/x-app1', 'application/x-app2'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Serializer2 < Sinatra::RPC::Serializer::Base
|
|
13
|
+
content_types nil, 'application/x-app3'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class MyClass
|
|
17
|
+
def my_method(text)
|
|
18
|
+
"this is #{text}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class MyApp
|
|
23
|
+
include Sinatra::RPC::Helpers
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
after(:all) do
|
|
29
|
+
Sinatra::RPC::Serializer.instance_variable_set('@registry', @registry)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
before(:each) do
|
|
33
|
+
@app = HelpersTest::MyApp.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context '#select_serializer' do
|
|
37
|
+
it 'should generate the correct serializer instance' do
|
|
38
|
+
@app.select_serializer(nil).class.should == HelpersTest::Serializer2
|
|
39
|
+
@app.select_serializer('application/x-app2').class.should == HelpersTest::Serializer1
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context '#call_rpc_method' do
|
|
44
|
+
|
|
45
|
+
before(:each) do
|
|
46
|
+
@app = HelpersTest::MyApp.new
|
|
47
|
+
@handler = HelpersTest::MyClass.new
|
|
48
|
+
@settings = double(:settings,
|
|
49
|
+
rpc_method_index: {
|
|
50
|
+
'myClass.myMethod' => {
|
|
51
|
+
method: :my_method,
|
|
52
|
+
handler: @handler,
|
|
53
|
+
help: '',
|
|
54
|
+
signature: [['string', 'string']]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
@app.stub(:settings) {@settings}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should call the correct method' do
|
|
62
|
+
@app.call_rpc_method('myClass.myMethod', 'some text').should == 'this is some text'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'should raise a NotFound exception if the method does not exist' do
|
|
66
|
+
expect {
|
|
67
|
+
@app.call_rpc_method 'myClass.someMethod', [42]
|
|
68
|
+
}.to raise_error(Sinatra::RPC::NotFound)
|
|
69
|
+
|
|
70
|
+
expect {
|
|
71
|
+
@app.call_rpc_method 'anotherMethod', ['arg1', 'arg2']
|
|
72
|
+
}.to raise_error(Sinatra::RPC::NotFound)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context '#handle_rpc' do
|
|
77
|
+
before(:each) do
|
|
78
|
+
@app = HelpersTest::MyApp.new
|
|
79
|
+
@serializer = double('serializer',
|
|
80
|
+
parse: ['myClass.myMethod', ['some text']],
|
|
81
|
+
dump: '<the serialized object>',
|
|
82
|
+
content_type: 'test/content-type',
|
|
83
|
+
content_type_options: {})
|
|
84
|
+
@request_body = double('body', read: '<serialized request>')
|
|
85
|
+
@request = double('request', body: @request_body, env: {})
|
|
86
|
+
@app.stub(:content_type)
|
|
87
|
+
@app.stub(:select_serializer) {@serializer}
|
|
88
|
+
@app.stub(:call_rpc_method) { 'this is some text' }
|
|
89
|
+
@app.stub(:halt)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'should handle a successful RPC call' do
|
|
93
|
+
@app.handle_rpc(@request).should == '<the serialized object>'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should reject empty requests with a 400 error' do
|
|
97
|
+
@request_body.stub(:read) {''}
|
|
98
|
+
@app.should_receive(:halt).with(400).once
|
|
99
|
+
@app.handle_rpc(@request)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'should reply with a 404 when the method does not exist' do
|
|
103
|
+
@app.should_receive(:call_rpc_method).
|
|
104
|
+
with('myClass.myMethod', ['some text']).ordered.once.
|
|
105
|
+
and_raise(Sinatra::RPC::NotFound)
|
|
106
|
+
@app.should_receive(:halt).with(404).ordered.once
|
|
107
|
+
@app.handle_rpc(@request)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context 'with errors' do
|
|
111
|
+
|
|
112
|
+
before(:each) do
|
|
113
|
+
@serializer.stub(:dump) do |ex|
|
|
114
|
+
ex.message
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'should transmit back an RPC fault' do
|
|
119
|
+
Sinatra::RPC::Fault.register(:this_is_a_test, 1234)
|
|
120
|
+
ex = Sinatra::RPC::ThisIsATestFault.new 'Problems!'
|
|
121
|
+
@app.should_receive(:call_rpc_method).
|
|
122
|
+
with('myClass.myMethod', ['some text']).ordered.once.and_raise(ex)
|
|
123
|
+
@serializer.should_receive(:dump).with(ex).ordered.once
|
|
124
|
+
@app.handle_rpc(@request).should == 'Problems!'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'should wrap argument errors' do
|
|
128
|
+
|
|
129
|
+
@app.should_receive(:call_rpc_method).
|
|
130
|
+
with('myClass.myMethod', ['some text']).ordered.once.
|
|
131
|
+
and_raise(ArgumentError.new('bad argument'))
|
|
132
|
+
@serializer.should_receive(:dump).with(
|
|
133
|
+
kind_of(Sinatra::RPC::BadRequestFault)).ordered.once
|
|
134
|
+
@app.handle_rpc(@request).should == 'bad argument'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'should wrap generic errors' do
|
|
138
|
+
@app.should_receive(:call_rpc_method).
|
|
139
|
+
with('myClass.myMethod', ['some text']).ordered.once.
|
|
140
|
+
and_raise(RuntimeError.new('a runtime error'))
|
|
141
|
+
@serializer.should_receive(:dump).with(
|
|
142
|
+
kind_of(Sinatra::RPC::GenericFault)).ordered.once
|
|
143
|
+
@app.handle_rpc(@request).should == "RuntimeError: a runtime error"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|