jsonrpc-server 0.2.0 → 0.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/README CHANGED
@@ -1,14 +1,23 @@
1
- == About
1
+ About
2
+ =====
2
3
 
3
- This is an implementation of {JSON RPC 2.0}[link:http://groups.google.com/group/json-rpc/web/json-rpc-2-0?pli=1] server
4
+ This is an implementation of JSON RPC 2.0 server (http://groups.google.com/group/json-rpc/web/json-rpc-2-0?pli=1)
4
5
 
5
- == Using
6
+ Using
7
+ =====
6
8
 
7
- To make your rails controller JSON RPC 2.0 callable, make next include:
9
+ To make your rails controller JSON RPC 2.0 callable, do next
8
10
 
9
11
  class TestController < ActionController::Base
10
- include JSONServer
12
+ include JSONServer # adds json-rpc 2.0 functionality
11
13
 
14
+ def index
15
+ # next will redirect post contents to json-rpc dispatcher
16
+ # and output resut as string
17
+ render :text => dispatch_json_2(request.body.read)
18
+ end
19
+
20
+ # function which will become remotely callable
12
21
  def summ(left, right)
13
22
  return left+right
14
23
  end
@@ -19,17 +28,15 @@ In your config/routing.rb you will have to add
19
28
  post 'json' => 'test#index'
20
29
 
21
30
  JSONServer will automaticaly check if posted json is valid JSON-RPC 2.0 request
22
- and and fire called method. if method is not found - propper error will be sent.
31
+ and fire called method. if method is not found - propper error will be sent.
23
32
 
24
33
  Batch request are handled.
34
+ Notices are handled.
35
+ Test taken from examples on http://groups.google.com/group/json-rpc/web/json-rpc-2-0?pli=1
25
36
 
26
37
  == Errors
27
38
 
28
- You can raise standart errors, or JSONServer::Error with parameters :code,
39
+ You can raise standart errors, or JSONRPCError with parameters :code,
29
40
  :message, :data, which will be set for correspoding fields
30
41
  :code defaults to -30000 if JSONServer::Error and to -32000 otherwise
31
42
  :message defaults to "Runtime Error"
32
-
33
- == TODO list
34
-
35
- write tests
@@ -0,0 +1,36 @@
1
+ require 'json'
2
+ require 'jsonrpc-server/defs'
3
+ require 'jsonrpc-server/parser'
4
+
5
+ # JSONRPCServer - container, and router for json-rpc 2.0 requests
6
+ #
7
+ # Procedure handler classe, module or object is stored inside the class
8
+ #
9
+ # Handler can specify own procedure prefix to create namespace of allowed
10
+ # to call functions. say, one does not want someone to call to_s inside class
11
+ # which encrypts user data with secret key.
12
+
13
+ class JSONRPCServer
14
+
15
+ attr_accessor :handler, :prefix
16
+
17
+ def initialize in_handler, in_prefix = ""
18
+ self.handler = in_handler
19
+ self.prefix = in_prefix
20
+ end
21
+
22
+ def dispatch request
23
+ # here request specific errors are handled
24
+ begin
25
+ # no wraping, because single_request dispatcher does it
26
+ result = core_dispatch request
27
+ rescue JSON::ParserError
28
+ # called only on parsing error. all other errors
29
+ # have to be handled on per-request basis
30
+ result = wrap_error @@ParseError
31
+ end
32
+ return '' if result==nil || result.empty?
33
+ return result.to_json
34
+ end
35
+
36
+ end
@@ -0,0 +1,48 @@
1
+ # Different class difenitions
2
+ # Standart errors
3
+ # Constants
4
+
5
+ class JSONRPCServer
6
+
7
+ @@Version = "2.0".freeze
8
+
9
+ class Error < StandardError
10
+
11
+ attr_reader :data
12
+
13
+ def initialize *args
14
+ @data = {:code => -30000, :message => "Runtime Error"}
15
+
16
+ return unless args.length > 0
17
+
18
+ # if initialized by object
19
+ if (args.length == 1) and (args[0].is_a? Hash) then
20
+ @data.update(args[0])
21
+ return self
22
+ end
23
+
24
+ # per argument: code, message, data
25
+ @data[:code] = args[0] if args[0].is_a? Fixnum
26
+ @data[:message] = args[1] if args[1].is_a? String
27
+ @data[:data] = args[2] if args[2].is_a? Hash
28
+ end
29
+
30
+ def Error.to_e err
31
+ return err if err.is_a? Error
32
+ Error.new -32000, $!.message, $!.to_s
33
+ end
34
+
35
+ end
36
+
37
+ class Result
38
+ attr_reader :data
39
+ def initialize obj = nil
40
+ @data = obj
41
+ end
42
+ end
43
+
44
+ # Errors
45
+ @@MethodNotFound = Error.new(-32601, "Method not found.").freeze
46
+ @@ParseError = Error.new(-32700, "Parse error.").freeze
47
+ @@InvalidRequest = Error.new(-32600, "Invalid Request.").freeze
48
+ end
@@ -0,0 +1,69 @@
1
+ require 'jsonrpc-server/defs'
2
+
3
+ class JSONRPCServer
4
+
5
+ private
6
+
7
+ def core_dispatch request
8
+ data = JSON.parse request
9
+ return parse_batch data if data.kind_of? Array
10
+ return parse_single data
11
+ end
12
+
13
+ def wrap data, id = nil
14
+ data.update({ :jsonrpc => @@Version, :id => id })
15
+ end
16
+
17
+ def wrap_error error, id = nil
18
+ wrap({ :error => error.data }, id)
19
+ end
20
+
21
+ def wrap_result result, id = nil
22
+ wrap({ :result => result }, id)
23
+ end
24
+
25
+ def parse_batch data
26
+ # batch can not be empty
27
+ return wrap_error @@InvalidRequest if data.length == 0
28
+ res = data.collect{ |single|
29
+ parse_single single
30
+ }
31
+ res.delete(nil)
32
+ res
33
+ end
34
+
35
+ def parse_single data
36
+ current_id = nil
37
+ result = catch :result do
38
+ begin
39
+ raise @@InvalidRequest unless data.kind_of?(Hash)
40
+ current_id = data["id"] unless data["id"] == nil
41
+ check_request data
42
+ method_name = self.prefix + data["method"]
43
+ raise @@MethodNotFound unless handler.respond_to? method_name
44
+ if data["params"].kind_of? Array
45
+ arr = data["params"]
46
+ elsif
47
+ arr = handler.method(method_name).parameters.collect { |pd|
48
+ data["params"][pd[1].to_s]
49
+ }
50
+ end
51
+ result = handler.send(method_name, *arr)
52
+ throw(:result,
53
+ wrap_result(result, current_id)) unless current_id == nil
54
+ throw(:result, nil)
55
+ rescue
56
+ throw :result, wrap_error(Error.to_e($!), current_id)
57
+ end
58
+ end
59
+ result
60
+ end
61
+
62
+ def check_request data
63
+ return if data["jsonrpc"] == @@Version &&
64
+ data["method"].kind_of?(String) &&
65
+ data["method"] != ""
66
+ raise @@InvalidRequest
67
+ end
68
+
69
+ end
@@ -0,0 +1,73 @@
1
+ require 'test/unit'
2
+ require 'jsonrpc-server'
3
+
4
+ require './test/no_namespace'
5
+ require './test/rpc_class'
6
+
7
+ class All < Test::Unit::TestCase
8
+
9
+ @@server = JSONRPCServer.new NoNamespace.new
10
+ @@prefixed_server = JSONRPCServer.new RPCClass.new, "rpc_"
11
+
12
+ def test_from_file
13
+ file = YAML.load(File.open("test/cases.yml"))
14
+ file["tests"].each { |test|
15
+ result = @@server.dispatch test["request"]
16
+ expected_res = test["response"]
17
+ assert_equal(JSON.parse(expected_res),
18
+ JSON.parse(result), test["id"].to_s+" :: "+test["name"])
19
+ }
20
+ end
21
+
22
+ def test_notification
23
+ result = @@server.dispatch '{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}'
24
+ assert_equal('', result, "update_method")
25
+ assert_equal([1,2,3,4,5], @@server.handler.arr, "update_method")
26
+
27
+ @@server.handler.foobar_called = false
28
+ result = @@server.dispatch '{"jsonrpc": "2.0", "method": "foobar"}'
29
+ assert_equal('', result, "foobar_method")
30
+ assert_equal(true, @@server.handler.foobar_called, "foobar_method")
31
+ end
32
+
33
+ def test_call_batch
34
+ result = @@server.dispatch '[
35
+ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
36
+ {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
37
+ {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
38
+ {"foo": "boo"},
39
+ {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
40
+ {"jsonrpc": "2.0", "method": "get_data", "id": "9"}
41
+ ]'
42
+ expected_res = '[
43
+ {"jsonrpc": "2.0", "result": 7, "id": "1"},
44
+ {"jsonrpc": "2.0", "result": 19, "id": "2"},
45
+ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
46
+ {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, "id": "5"},
47
+ {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
48
+ ]'
49
+ assert_equal(JSON.parse(expected_res),
50
+ JSON.parse(result))
51
+ assert_equal(@@server.handler.notify_hello_data, 7)
52
+ end
53
+
54
+ def test_call_batch_all_notifications
55
+ @@server.handler.notify_sum_data = nil
56
+ @@server.handler.notify_hello_data = nil
57
+ result = @@server.dispatch '[
58
+ {"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]},
59
+ {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}
60
+ ]'
61
+ assert_equal("", result);
62
+ assert_equal([1,2,4], @@server.handler.notify_sum_data);
63
+ assert_equal(7, @@server.handler.notify_hello_data);
64
+ end
65
+
66
+ def test_simple_prefix_request
67
+ result = @@prefixed_server.dispatch '{"jsonrpc": "2.0", "method": "multiply", "params": [6, 8], "id": 1}'
68
+ expected_res = '{"jsonrpc": "2.0", "result": 48, "id": 1}'
69
+ assert_equal(JSON.parse(expected_res),
70
+ JSON.parse(result))
71
+ end
72
+
73
+ end
@@ -0,0 +1,64 @@
1
+ tests:
2
+ - id: 1
3
+ name: 'simple request'
4
+ request: '{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}'
5
+ response: '{"jsonrpc": "2.0", "result": 19, "id": 1}'
6
+
7
+ - id: 2
8
+ name: 'simple request'
9
+ request: '{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 1}'
10
+ response: '{"jsonrpc": "2.0", "result": -19, "id": 1}'
11
+
12
+ - id: 3
13
+ name: 'named request'
14
+ request: '{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 1}'
15
+ response: '{"jsonrpc": "2.0", "result": 19, "id": 1}'
16
+
17
+ - id: 4
18
+ name: 'named request'
19
+ request: '{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 23, "subtrahend": 42}, "id": 1}'
20
+ response: '{"jsonrpc": "2.0", "result": -19, "id": 1}'
21
+
22
+ - id: 5
23
+ name: 'non_existing'
24
+ request: '{"jsonrpc": "2.0", "method": "foobar1", "id": "1"}'
25
+ response: '{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, "id": "1"}'
26
+
27
+ - id: 6
28
+ name: 'parse_error'
29
+ request: '{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]'
30
+ response: '{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}'
31
+
32
+ - id: 7
33
+ name: 'invalid_request_object'
34
+ request: '{"jsonrpc": "2.0", "method": 1, "params": "bar"}'
35
+ response: '{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}'
36
+
37
+ - id: 8
38
+ name: 'call_batch_invalid_json'
39
+ request: '[ {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},{"jsonrpc": "2.0", "method" ]'
40
+ response: '{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error."}, "id": null}'
41
+
42
+ - id: 9
43
+ name: 'empty_batch'
44
+ request: '[]'
45
+ response: '{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}'
46
+
47
+ - id: 10
48
+ name: 'invalid_batch_not_empty'
49
+ request: '[1]'
50
+ response: '[
51
+ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
52
+ ]'
53
+
54
+ - id: 11
55
+ name: 'invalid_batch'
56
+ request: '[1,2,3]'
57
+ response: '[
58
+ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
59
+ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
60
+ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null}
61
+ ]'
62
+
63
+
64
+
@@ -0,0 +1,35 @@
1
+ # Example of RPC handler
2
+
3
+ class NoNamespace
4
+
5
+ attr_accessor :notify_sum_data, :arr, :notify_hello_data, :foobar_called
6
+
7
+ def sum left, middle, right
8
+ return left + middle + right
9
+ end
10
+
11
+ def get_data
12
+ return ["hello", 5]
13
+ end
14
+
15
+ def subtract minuend, subtrahend
16
+ return minuend - subtrahend
17
+ end
18
+
19
+ def notify_sum *arr
20
+ @notify_sum_data = arr
21
+ end
22
+
23
+ def update *arr
24
+ @arr = arr
25
+ end
26
+
27
+ def notify_hello hello
28
+ @notify_hello_data = hello
29
+ end
30
+
31
+ def foobar
32
+ @foobar_called = true
33
+ end
34
+ end
35
+
@@ -0,0 +1,5 @@
1
+ class RPCClass
2
+ def rpc_multiply left, right
3
+ return left * right
4
+ end
5
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
7
+ - 3
8
8
  - 0
9
- version: 0.2.0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Dima Levchenko
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-05-31 00:00:00 +03:00
17
+ date: 2011-06-07 00:00:00 +03:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -27,10 +27,16 @@ extensions: []
27
27
  extra_rdoc_files: []
28
28
 
29
29
  files:
30
- - lib/json_server.rb
30
+ - lib/jsonrpc-server.rb
31
+ - lib/jsonrpc-server/parser.rb
32
+ - lib/jsonrpc-server/defs.rb
33
+ - test/cases.yml
34
+ - test/rpc_class.rb
35
+ - test/no_namespace.rb
36
+ - test/all.rb
31
37
  - README
32
38
  has_rdoc: true
33
- homepage: http://rubygems.org/gems/jsonrpc-server
39
+ homepage: https://github.com/dimalev/jsonrpc-server
34
40
  licenses: []
35
41
 
36
42
  post_install_message:
@@ -1,135 +0,0 @@
1
- require 'json'
2
-
3
- module JSONServer
4
-
5
- @@JSONRPCVersion = "2.0".freeze
6
-
7
- class JSONRPCError < RuntimeError
8
- attr_reader :code, :message, :data
9
- def initialize obj = {}
10
- @code = obj[:code] || -30000
11
- @message = obj[:message] || "Runtime Error"
12
- @data = obj[:data]
13
- end
14
- end
15
-
16
- def dispatch_json_2 request
17
- # here request specific errors are handled
18
- begin
19
- # no wraping, because single_request dispatcher does it
20
- result = core_dispatch request
21
- rescue JSONRPCError
22
- result = wrap_error({ :code => $!.code,
23
- :message => $!.message,
24
- :data => $!.data
25
- })
26
- rescue RuntimeError
27
- result = wrap_error({ :code => -32000,
28
- :message => $!.message,
29
- :data => $!.to_s
30
- })
31
- end
32
- if result != nil
33
- return result.to_json
34
- end
35
- return ''
36
- end
37
-
38
- private
39
-
40
- def core_dispatch request
41
- begin
42
- data = JSON.parse request
43
- rescue JSON::ParserError
44
- parse_error # converting error into json-rpc 2.0
45
- end
46
- if data.kind_of? Array
47
- invalid_request if data.length == 0 # batch can not be empty
48
- res = data.collect{ |single|
49
- parse_single single
50
- }
51
- res.delete(nil)
52
- return res unless res.length == 0
53
- return nil
54
- end
55
- parse_single data
56
- end
57
-
58
- def wrap data, id = nil
59
- data.update({ :jsonrpc => @@JSONRPCVersion, :id => id })
60
- end
61
-
62
- def wrap_error error, id = nil
63
- wrap({ :error => error }, id)
64
- end
65
-
66
- def wrap_result result, id = nil
67
- wrap({:result => result}, id)
68
- end
69
-
70
- def parse_single(data)
71
- current_id = nil
72
- begin
73
- invalid_request unless data.kind_of?(Hash)
74
- current_id = data["id"] unless data["id"] == nil
75
- if data["jsonrpc"] != @@JSONRPCVersion ||
76
- (not data["method"].kind_of?(String)) ||
77
- data["method"] == ""
78
- invalid_request
79
- end
80
- self.class.public_instance_methods.each { |method|
81
- if method.to_s == data["method"]
82
- if data["params"].kind_of? Array
83
- arr = data["params"]
84
- elsif
85
- arr = self.method(data["method"]).parameters.collect { |pd|
86
- data["params"][pd[1].to_s]
87
- }
88
- end
89
- result = self.send(data["method"], *arr)
90
- if current_id != nil
91
- return wrap_result(result, current_id)
92
- else
93
- return
94
- end
95
- end
96
- }
97
- method_not_found
98
- rescue JSONRPCError
99
- result = wrap_error({ :code => $!.code,
100
- :message => $!.message,
101
- :data => $!.data
102
- }, current_id)
103
- rescue RuntimeError
104
- result = wrap_error({ :code => -32000,
105
- :message => $!.message,
106
- :data => $!.to_s
107
- }, current_id)
108
- end
109
- end
110
-
111
- ##
112
- # Error handling section
113
- ##
114
-
115
- def method_not_found
116
- raise(JSONRPCError, {
117
- :code => -32601,
118
- :message => "Method not found."
119
- }, caller)
120
- end
121
-
122
- def parse_error
123
- raise(JSONRPCError, {
124
- :code => -32700,
125
- :message => "Parse error."
126
- }, caller)
127
- end
128
-
129
- def invalid_request
130
- raise(JSONRPCError, {
131
- :code => -32600,
132
- :message => "Invalid Request."
133
- }, caller)
134
- end
135
- end