json-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.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ Json Rpc
2
+ ========
3
+
4
+ Implementation of [JSON RPC 2.0](http://groups.google.com/group/json-rpc/web/json-rpc-2-0) protocol.
5
+ It allows you to create easily json rpc server.
6
+
7
+ Usage
8
+ -----
9
+
10
+ Simple Rack example:
11
+
12
+ ~~~~~~ {ruby}
13
+ require 'json-rpc'
14
+
15
+ class SyncApp
16
+ include JsonRpc
17
+ def call env
18
+ result = dispatch(env) { |e|
19
+ logger.info "#{e}"
20
+ }
21
+ result
22
+ end
23
+
24
+ def rpc_sum a, b
25
+ a + b
26
+ end
27
+ end
28
+ run SyncApp.new
29
+ ~~~~~~
30
+
31
+ Asynchronous Event Machine example:
32
+
33
+ ~~~~~~ {ruby}
34
+ require 'json-rpc'
35
+
36
+ class AsyncApp
37
+ include JsonRpc
38
+ AsyncResponse = [-1, {}, []].freeze
39
+ def call env
40
+ result = dispatch(env)
41
+ env['async.callback'].call result
42
+ AsyncResponse
43
+ end
44
+
45
+ def rpc_sum a, b
46
+ result = Rpc::AsyncResult.new
47
+ EventMachine::next_tick do
48
+ result.reply a + b
49
+ result.succeed
50
+ end
51
+ result
52
+ end
53
+ end
54
+ ~~~~~~
55
+
56
+ ### License
57
+ Copyright 2011 [Helios Technologies Ltd.](http://www.heliostech.hk)
58
+
59
+ Licensed under the Apache License, Version 2.0 (the "License");
60
+ you may not use this file except in compliance with the License.
61
+ You may obtain a copy of the License at
62
+
63
+ http://www.apache.org/licenses/LICENSE-2.0
64
+
65
+ Unless required by applicable law or agreed to in writing, software
66
+ distributed under the License is distributed on an "AS IS" BASIS,
67
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
68
+ See the License for the specific language governing permissions and
69
+ limitations under the License.
@@ -0,0 +1,28 @@
1
+ # -*- RUBY -*-
2
+ $: << File::join(File::dirname(__FILE__), "..", "lib")
3
+ require 'json-rpc'
4
+
5
+ class AsyncApp
6
+ include JsonRpc
7
+
8
+ AsyncResponse = [-1, {}, []].freeze
9
+
10
+ def call env
11
+ result = dispatch(env) { |e|
12
+ puts "#{e} backtrace: #{e.backtrace.join "\n"}"
13
+ }
14
+ env['async.callback'].call result
15
+ AsyncResponse
16
+ end
17
+
18
+ def rpc_sum a, b
19
+ result = Rpc::AsyncResult.new
20
+ EventMachine::next_tick do
21
+ result.reply a + b
22
+ result.succeed
23
+ end
24
+ result
25
+ end
26
+ end
27
+
28
+ run AsyncApp.new
@@ -0,0 +1,20 @@
1
+ # -*- RUBY -*-
2
+ $: << File::join(File::dirname(__FILE__), "..", "lib")
3
+ require 'json-rpc'
4
+
5
+ class SyncApp
6
+ include JsonRpc
7
+
8
+ def call env
9
+ result = dispatch(env) { |e|
10
+ logger.info "#{e} backtrace: #{e.backtrace.join "\n"}"
11
+ }
12
+ result
13
+ end
14
+
15
+ def rpc_sum a, b
16
+ a + b
17
+ end
18
+ end
19
+
20
+ run SyncApp.new
data/lib/json-rpc.rb ADDED
@@ -0,0 +1,127 @@
1
+ require 'json'
2
+ require 'eventmachine'
3
+ require 'uri'
4
+
5
+ module JsonRpc
6
+
7
+ # Call the correct method for each query
8
+ # The method should be prefixed by rpc_
9
+ # If the method doesn't exists, an error will be return in JSON
10
+ # More details in http://groups.google.com/group/json-rpc/web/json-rpc-2-0
11
+ def rpc_call env, &ecb
12
+ begin
13
+ request = Rpc::parse env
14
+ status = Rpc::validate request
15
+ result = Rpc::route request, self
16
+
17
+ rescue Rpc::Error => e
18
+ status = e.status
19
+ result = e.result
20
+ ecb.call(e) if ecb
21
+ end
22
+
23
+ [status, {'Content-Type' => Rpc::ContentType}, result]
24
+ end
25
+
26
+ module Rpc
27
+ Version = "2.0".freeze
28
+ Prefix = "rpc_".freeze
29
+ ContentType = "application/json".freeze
30
+
31
+ ErrorProtocol = {
32
+ :parse_error => [500, -32700, "Parse error"],
33
+ :invalid_request => [400, -32600, "Invalid Request"],
34
+ :method_not_found => [404, -32601, "Method not found"],
35
+ :invalid_params => [500, -32602, "Invalid params"],
36
+ :internal_error => [500, -32603, "Internal error"],
37
+ }
38
+
39
+ class Error < RuntimeError
40
+ attr_reader :status, :code, :msg
41
+ attr_accessor :id
42
+ def initialize status, code, msg
43
+ @status, @code, @msg = status, code, msg
44
+ end
45
+ def result
46
+ res = {"jsonrpc" => "2.0", "id" => id,
47
+ "error" => {"code" => code, "message" => msg}
48
+ }
49
+ res.delete_if { |k, v| v == nil}
50
+ res.to_json
51
+ end
52
+ end
53
+
54
+ def self.error index, id = nil
55
+ id = nil unless id.is_a? Fixnum
56
+ ex = Rpc::Error.new *ErrorProtocol[index]
57
+ ex.id = id
58
+ ex
59
+ end
60
+
61
+ def self.validate request
62
+ return 200 if request["jsonrpc"] == Version and
63
+ request["method"].kind_of?(String) and
64
+ request["method"] != ""
65
+ raise error :invalid_request, request["id"]
66
+ end
67
+
68
+ def self.parse env
69
+ begin
70
+ case env["REQUEST_METHOD"]
71
+ when "POST"
72
+ JSON.parse(env["rack.input"].read)
73
+ when "GET"
74
+ req = Rack::Request.new(env)
75
+ obj = req.params
76
+ obj["id"] = obj["id"] ? obj["id"].to_i : nil
77
+ obj["params"] = JSON::parse(obj["params"])
78
+ obj
79
+ else
80
+ raise error :invalid_request
81
+ end
82
+
83
+ rescue JSON::ParserError, Exception
84
+ raise error :parse_error
85
+ end
86
+ end
87
+
88
+ def self.route request, ctrl
89
+ method, params = Prefix + request["method"], request["params"]
90
+
91
+ unless ctrl.respond_to? method
92
+ raise error :method_not_found, request["id"]
93
+ end
94
+
95
+ result = ctrl.send(method, *params)
96
+ if result.is_a? AsyncResult
97
+ result.id = request["id"]
98
+ return result
99
+ end
100
+ forge_response result, request["id"]
101
+ end
102
+
103
+ def self.forge_response result, id = nil
104
+ return nil if id == nil
105
+ {"jsonrpc" => "2.0", "id" => id, "result" => result}.to_json
106
+ end
107
+
108
+ # The class RpcDeferrable is useful helps you to build a Json Rpc server
109
+ # into an asynchronous way
110
+ class AsyncResult
111
+ include EventMachine::Deferrable
112
+
113
+ attr_reader :response
114
+ attr_accessor :id
115
+
116
+ def reply obj
117
+ @callback.call(Rpc::forge_response(obj, @id))
118
+ end
119
+
120
+ #FIXME thin specific
121
+ def each &blk
122
+ @callback = blk
123
+ end
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,59 @@
1
+ require 'test/unit'
2
+ require 'rack/mock'
3
+ require 'json-rpc'
4
+ require 'uri'
5
+
6
+ class All < Test::Unit::TestCase
7
+ include JsonRpc
8
+
9
+ ReqSubtract = {"jsonrpc"=>"2.0", "method"=>"subtract", "params"=>[42, 23], "id"=>1}
10
+ ReqSubtractString = JSON::dump(ReqSubtract)
11
+
12
+ def test_post_parse
13
+ opts = {
14
+ :input => ReqSubtractString,
15
+ :method => 'POST'
16
+ }
17
+ rs = Rpc::parse Rack::MockRequest.env_for("", opts)
18
+ assert_equal ReqSubtract, rs
19
+ end
20
+
21
+ def test_get_parse
22
+ opts = {:method => 'GET'}
23
+ rs = Rpc::parse Rack::MockRequest.env_for("/?method=subtract&params=[42,23]&id=1&jsonrpc=2.0")
24
+ assert_equal ReqSubtract, rs
25
+ end
26
+
27
+ def test_invalid_json_post_parse
28
+ req_string = '{"jsonrpc": "2.0", "method": "subtract", "params": [42, 2'
29
+
30
+ # POST REQUEST
31
+ opts = {:input => req_string,
32
+ :method => 'POST'
33
+ }
34
+ assert_raise(Rpc::Error) {
35
+ rs = Rpc::parse Rack::MockRequest.env_for("", opts)
36
+ }
37
+ end
38
+
39
+ def test_validate
40
+ req_hash = { "jsonrpc" => "2.0", "method" => "s", "params" => 1, "id" => 22 }
41
+ assert_nothing_raised {
42
+ Rpc::validate req_hash
43
+ }
44
+ req_hash = { "method" => "s", "params" => 1, "id" => 22 }
45
+ assert_raise(Rpc::Error) {
46
+ Rpc::validate req_hash
47
+ }
48
+ req_hash = { "jsonrpc" => "2.0", "method" => "s", "params" => 1, "id" => "22" }
49
+ assert_raise(Rpc::Error) {
50
+ Rpc::validate req_hash
51
+ }
52
+ end
53
+
54
+ def test_response_forging
55
+ assert_equal({"jsonrpc" => "2.0", "result" => 12, "id" => 7},
56
+ JSON.parse(Rpc::forge_response(12, 7)))
57
+ assert_equal(nil, Rpc::forge_response(12))
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-rpc
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Helios Technologies Ltd.
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-06-15 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Implementation of JSON RPC 2.0 protocol. It allows you to create easily a json rpc server in pure Rack, in Rails, or asynchronous using Thin and EventMachine.
22
+ email: contact@heliostech.hk
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/json-rpc.rb
31
+ - test/test_rpc_module.rb
32
+ - example/async_app.ru
33
+ - example/rack_app.ru
34
+ - README.md
35
+ has_rdoc: true
36
+ homepage: https://github.com/helios-technologies/json-rpc
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project: "[none]"
63
+ rubygems_version: 1.3.7
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: JSON RPC 2.0 library for rack applications
67
+ test_files: []
68
+