rpc 0.1 → 0.2
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/.gitignore +2 -0
- data/CHANGELOG +7 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +28 -0
- data/LICENSE +20 -0
- data/README.textile +7 -0
- data/examples/em-http-request-json/client.rb +28 -0
- data/examples/net-http-json/client.rb +33 -0
- data/examples/net-http-json/console.rb +13 -0
- data/examples/server.ru +54 -0
- data/lib/rpc.rb +143 -0
- data/lib/rpc/clients/amqp/coolio.rb +0 -0
- data/lib/rpc/clients/amqp/eventmachine.rb +0 -0
- data/lib/rpc/clients/amqp/socket.rb +0 -0
- data/lib/rpc/clients/em-http-request.rb +55 -0
- data/lib/rpc/clients/net-http.rb +50 -0
- data/lib/rpc/clients/redis.rb +0 -0
- data/lib/rpc/encoders/json.rb +138 -0
- data/lib/rpc/encoders/xml.rb +0 -0
- data/rpc.gemspec +34 -0
- metadata +24 -6
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
source "http://gemcutter.org"
|
4
|
+
|
5
|
+
group(:examples) do
|
6
|
+
gem "rack"
|
7
|
+
end
|
8
|
+
|
9
|
+
group(:em) do
|
10
|
+
gem "em-http-request"
|
11
|
+
end
|
12
|
+
|
13
|
+
# group(:amqp) do
|
14
|
+
# gem "amq-client"
|
15
|
+
# end
|
16
|
+
|
17
|
+
group(:test) do
|
18
|
+
gem "rspec", ">=2.0.0"
|
19
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://gemcutter.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.5)
|
5
|
+
diff-lcs (1.1.2)
|
6
|
+
em-http-request (0.3.0)
|
7
|
+
addressable (>= 2.0.0)
|
8
|
+
escape_utils
|
9
|
+
eventmachine (>= 0.12.9)
|
10
|
+
escape_utils (0.2.3)
|
11
|
+
eventmachine (0.12.10)
|
12
|
+
rack (1.2.2)
|
13
|
+
rspec (2.5.0)
|
14
|
+
rspec-core (~> 2.5.0)
|
15
|
+
rspec-expectations (~> 2.5.0)
|
16
|
+
rspec-mocks (~> 2.5.0)
|
17
|
+
rspec-core (2.5.1)
|
18
|
+
rspec-expectations (2.5.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.5.0)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
em-http-request
|
27
|
+
rack
|
28
|
+
rspec (>= 2.0.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jakub Stastny aka botanicus
|
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.textile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
5
|
+
|
6
|
+
require "rpc"
|
7
|
+
|
8
|
+
RPC.logging = true
|
9
|
+
|
10
|
+
client = RPC::Clients::EmHttpRequest.new("http://127.0.0.1:8081")
|
11
|
+
|
12
|
+
RPC::Client.new(client) do |client|
|
13
|
+
# Get result of an existing method.
|
14
|
+
client.server_timestamp do |result, error|
|
15
|
+
puts "Server timestamp is #{result}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get result of a non-existing method via method_missing.
|
19
|
+
client.send(:+, 1) do |result, error|
|
20
|
+
puts "Method missing works: #{result}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Synchronous error handling.
|
24
|
+
client.buggy_method do |result, error|
|
25
|
+
STDERR.puts "EXCEPTION CAUGHT:"
|
26
|
+
STDERR.puts "#{error.class} #{error.message}"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
5
|
+
|
6
|
+
require "rpc"
|
7
|
+
|
8
|
+
RPC.logging = true
|
9
|
+
|
10
|
+
client = RPC::Client.setup("http://127.0.0.1:8081")
|
11
|
+
|
12
|
+
# Get result of an existing method.
|
13
|
+
puts "Server timestamp is #{client.server_timestamp}"
|
14
|
+
|
15
|
+
# Get result of a non-existing method via method_missing.
|
16
|
+
puts "Method missing works: #{client + 1}"
|
17
|
+
|
18
|
+
# Synchronous error handling.
|
19
|
+
begin
|
20
|
+
client.buggy_method
|
21
|
+
rescue Exception => exception
|
22
|
+
STDERR.puts "EXCEPTION CAUGHT: #{exception.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Notification isn't supported, because HTTP works in
|
26
|
+
# request/response mode, so it does behave in the same
|
27
|
+
# manner as RPC via method_missing.
|
28
|
+
puts "Sending a notification ..."
|
29
|
+
client.notification(:log, "Some shit.")
|
30
|
+
|
31
|
+
# Batch.
|
32
|
+
result = client.batch([[:log, ["Message"], nil], [:a_method, []]])
|
33
|
+
puts "Batch result: #{result}"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__)
|
5
|
+
|
6
|
+
require "rpc"
|
7
|
+
require "irb"
|
8
|
+
|
9
|
+
@client = RPC::Client.setup("http://127.0.0.1:8081")
|
10
|
+
|
11
|
+
puts "~ RPC Client initialised, use @client to access it."
|
12
|
+
|
13
|
+
IRB.start(__FILE__)
|
data/examples/server.ru
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env rackup --port 8081
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
7
|
+
|
8
|
+
require "rpc"
|
9
|
+
require "rack/request"
|
10
|
+
|
11
|
+
RPC.logging = true
|
12
|
+
# RPC.development = true
|
13
|
+
|
14
|
+
class RpcRunner
|
15
|
+
def server
|
16
|
+
@server ||= RPC::Server.new(RemoteObject.new)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
request = Rack::Request.new(env)
|
21
|
+
command = request.body.read
|
22
|
+
binary = self.server.execute(command)
|
23
|
+
if binary.match(/NoMethodError/)
|
24
|
+
response(404, binary)
|
25
|
+
else
|
26
|
+
response(200, binary)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def response(status, body)
|
31
|
+
headers = {
|
32
|
+
"Content-Type" => "application/json-rpc",
|
33
|
+
"Content-Length" => body.bytesize.to_s}
|
34
|
+
[status, headers, [body]]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class RemoteObject
|
39
|
+
def server_timestamp
|
40
|
+
Time.now.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
def buggy_method
|
44
|
+
raise "It doesn't work!"
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(name, *args)
|
48
|
+
"[SERVER] received method #{name} with #{args.inspect}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
map("/") do
|
53
|
+
run RpcRunner.new
|
54
|
+
end
|
data/lib/rpc.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RPC
|
4
|
+
module Clients
|
5
|
+
autoload :NetHttp, "rpc/clients/net-http"
|
6
|
+
autoload :EmHttpRequest, "rpc/clients/em-http-request"
|
7
|
+
end
|
8
|
+
|
9
|
+
module Encoders
|
10
|
+
autoload :Json, "rpc/encoders/json"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.logging
|
14
|
+
@logging ||= $DEBUG
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.logging=(boolean)
|
18
|
+
@logging = boolean
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.log(message)
|
22
|
+
STDERR.puts(message) if self.logging
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.development=(boolean)
|
26
|
+
@development = boolean
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.development?
|
30
|
+
!! @development
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.full_const_get(const_name)
|
34
|
+
parts = const_name.sub(/^::/, "").split("::")
|
35
|
+
parts.reduce(Object) do |constant, part|
|
36
|
+
constant.const_get(part)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Server
|
41
|
+
def initialize(subject, encoder = RPC::Encoders::Json::Server.new)
|
42
|
+
@subject, @encoder = subject, encoder
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute(encoded_command)
|
46
|
+
@encoder.execute(encoded_command, @subject)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module ExceptionsMixin
|
51
|
+
attr_accessor :server_backtrace
|
52
|
+
|
53
|
+
# NOTE: We can't use super to get the client backtrace,
|
54
|
+
# because backtrace is generated only if there is none
|
55
|
+
# yet and because we are redefining the backtrace method,
|
56
|
+
# there always will be some backtrace.
|
57
|
+
def backtrace
|
58
|
+
@backtrace ||= begin
|
59
|
+
caller(3) + ["... server ..."] + self.server_backtrace
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Client < BasicObject
|
65
|
+
def self.setup(uri, client_class = Clients::NetHttp, encoder = Encoders::Json::Client.new)
|
66
|
+
client = client_class.new(uri)
|
67
|
+
self.new(client, encoder)
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(client, encoder = Encoders::Json::Client.new, &block)
|
71
|
+
@client, @encoder = client, encoder
|
72
|
+
|
73
|
+
if block
|
74
|
+
@client.run do
|
75
|
+
block.call(self)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
@client.connect
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def notification(*args)
|
83
|
+
data = @encoder.notification(*args)
|
84
|
+
@client.send(data)
|
85
|
+
end
|
86
|
+
|
87
|
+
def batch(*args)
|
88
|
+
data = @encoder.batch(*args)
|
89
|
+
@client.send(data)
|
90
|
+
end
|
91
|
+
|
92
|
+
# 1) Sync: it'll return the value.
|
93
|
+
# 2) Async: you have to add #subscribe
|
94
|
+
def method_missing(method, *args, &callback)
|
95
|
+
binary = @encoder.encode(method, *args)
|
96
|
+
|
97
|
+
if @client.async?
|
98
|
+
@client.send(binary) do |encoded_result|
|
99
|
+
result = @encoder.decode(encoded_result)
|
100
|
+
callback.call(result["result"], get_exception(result["error"]))
|
101
|
+
end
|
102
|
+
else
|
103
|
+
::Kernel.raise("You can't specify callback for a synchronous client.") if callback
|
104
|
+
|
105
|
+
encoded_result = @client.send(binary)
|
106
|
+
result = @encoder.decode(encoded_result)
|
107
|
+
|
108
|
+
if result.respond_to?(:merge) # Hash, only one result.
|
109
|
+
result_or_raise(result)
|
110
|
+
else # Array, multiple results.
|
111
|
+
result.map do |result|
|
112
|
+
result_or_raise(result)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def result_or_raise(result)
|
119
|
+
if error = result["error"]
|
120
|
+
exception = self.get_exception(error)
|
121
|
+
::Kernel.raise(exception)
|
122
|
+
else
|
123
|
+
result["result"]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_exception(error)
|
128
|
+
return unless error
|
129
|
+
exception = error["error"]
|
130
|
+
resolved_class = ::RPC.full_const_get(exception["class"])
|
131
|
+
klass = resolved_class || ::RuntimeError
|
132
|
+
message = resolved_class ? exception["message"] : error["message"]
|
133
|
+
instance = klass.new(message)
|
134
|
+
instance.extend(::RPC::ExceptionsMixin)
|
135
|
+
instance.server_backtrace = exception["backtrace"]
|
136
|
+
instance
|
137
|
+
end
|
138
|
+
|
139
|
+
def close_connection
|
140
|
+
@client.disconnect
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# https://github.com/eventmachine/em-http-request
|
4
|
+
|
5
|
+
require "eventmachine"
|
6
|
+
require "em-http-request"
|
7
|
+
|
8
|
+
# Note that we support only HTTP POST. JSON-RPC can be done
|
9
|
+
# via HTTP GET as well, but since HTTP POST is the preferred
|
10
|
+
# method, I decided to implement only it. More info can is here:
|
11
|
+
# http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
12
|
+
|
13
|
+
module RPC
|
14
|
+
module Clients
|
15
|
+
class EmHttpRequest
|
16
|
+
HEADERS ||= {"Accept" => "application/json-rpc"}
|
17
|
+
|
18
|
+
def initialize(uri)
|
19
|
+
@client = EventMachine::HttpRequest.new(uri)
|
20
|
+
@in_progress = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def connect
|
24
|
+
end
|
25
|
+
|
26
|
+
def disconnect
|
27
|
+
end
|
28
|
+
|
29
|
+
def run(&block)
|
30
|
+
EM.run do
|
31
|
+
block.call
|
32
|
+
|
33
|
+
# Note: There's no way how to stop the
|
34
|
+
# reactor when there are no remaining events.
|
35
|
+
EM.add_periodic_timer(0.1) do
|
36
|
+
EM.stop if @in_progress == 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def send(data, &callback)
|
42
|
+
request = @client.post(head: HEADERS, body: data)
|
43
|
+
@in_progress += 1
|
44
|
+
request.callback do |response|
|
45
|
+
callback.call(response.response)
|
46
|
+
@in_progress -= 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def async?
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
# Note that we support only HTTP POST. JSON-RPC can be done
|
6
|
+
# via HTTP GET as well, but since HTTP POST is the preferred
|
7
|
+
# method, I decided to implement only it. More info can is here:
|
8
|
+
# http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
9
|
+
|
10
|
+
module Net
|
11
|
+
autoload :HTTP, "net/http"
|
12
|
+
autoload :HTTPS, "net/https"
|
13
|
+
end
|
14
|
+
|
15
|
+
module RPC
|
16
|
+
module Clients
|
17
|
+
class NetHttp
|
18
|
+
HEADERS ||= {"Accept" => "application/json-rpc"}
|
19
|
+
|
20
|
+
def initialize(uri)
|
21
|
+
@uri = URI.parse(uri)
|
22
|
+
klass = Net.const_get(@uri.scheme.upcase)
|
23
|
+
@client = klass.new(@uri.host, @uri.port)
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect
|
27
|
+
@client.start
|
28
|
+
end
|
29
|
+
|
30
|
+
def disconnect
|
31
|
+
@client.finish
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(&block)
|
35
|
+
self.connect
|
36
|
+
block.call
|
37
|
+
self.disconnect
|
38
|
+
end
|
39
|
+
|
40
|
+
def send(data)
|
41
|
+
path = @uri.path.empty? ? "/" : @uri.path
|
42
|
+
@client.post(path, data, HEADERS).body
|
43
|
+
end
|
44
|
+
|
45
|
+
def async?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
File without changes
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# http://en.wikipedia.org/wiki/JSON-RPC
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "yajl/json_gem"
|
7
|
+
rescue LoadError
|
8
|
+
require "json"
|
9
|
+
end
|
10
|
+
|
11
|
+
module RPC
|
12
|
+
module Encoders
|
13
|
+
module Json
|
14
|
+
# This library works with JSON-RPC 2.0
|
15
|
+
# http://groups.google.com/group/json-rpc/web/json-rpc-2-0
|
16
|
+
JSON_RPC_VERSION ||= "2.0"
|
17
|
+
|
18
|
+
# http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html#ErrorObject
|
19
|
+
module Errors
|
20
|
+
# @note The exceptions are "eaten", because no client should be able to shut the server down.
|
21
|
+
def exception(exception, code = 000, message = "#{exception.class}: #{exception.message}")
|
22
|
+
unless RPC.development?
|
23
|
+
object = {class: exception.class.to_s, message: exception.message, backtrace: exception.backtrace}
|
24
|
+
self.error(message, code, object)
|
25
|
+
else
|
26
|
+
raise exception
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def error(message, code, object)
|
31
|
+
error = {name: "JSONRPCError", code: code, message: message, error: object}
|
32
|
+
RPC.log "ERROR #{message} (#{code}) #{error[:error].inspect}"
|
33
|
+
error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Request
|
38
|
+
attr_reader :data
|
39
|
+
def initialize(method, params, id = self.generate_id)
|
40
|
+
@data = {jsonrpc: JSON_RPC_VERSION, method: method, params: params}
|
41
|
+
@data.merge!(id: id) unless id.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def generate_id
|
45
|
+
rand(999_999_999_999)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Client
|
50
|
+
include Errors
|
51
|
+
|
52
|
+
def encode(method, *args)
|
53
|
+
data = Request.new(method, args).data
|
54
|
+
RPC.log "CLIENT ENCODE #{data.inspect}"
|
55
|
+
JSON.generate(data)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Notifications are calls which don't require response.
|
59
|
+
# They look just the same, but they don't have any id.
|
60
|
+
def notification(method, *args)
|
61
|
+
data = Request.new(method, args, nil).data
|
62
|
+
RPC.log "CLIENT ENCODE NOTIFICATION #{data.inspect}"
|
63
|
+
JSON.generate(data)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Provide list of requests and notifications to run on the server.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# ["list", ["/"], ["clear", "logs", nil]]
|
70
|
+
def batch(requests)
|
71
|
+
data = requests.map { |request| Request.new(*request).data }
|
72
|
+
RPC.log "CLIENT ENCODE BATCH #{data.inspect}"
|
73
|
+
JSON.generate(data)
|
74
|
+
end
|
75
|
+
|
76
|
+
# TODO: support batch
|
77
|
+
def decode(binary)
|
78
|
+
object = JSON.parse(binary)
|
79
|
+
RPC.log "CLIENT DECODE #{object.inspect}"
|
80
|
+
object
|
81
|
+
rescue JSON::ParserError => error
|
82
|
+
self.exception(error, -32600, "Invalid Request.")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Server
|
87
|
+
include Errors
|
88
|
+
|
89
|
+
def decode(binary)
|
90
|
+
object = JSON.parse(binary)
|
91
|
+
RPC.log "SERVER DECODE #{object.inspect}"
|
92
|
+
object
|
93
|
+
rescue JSON::ParserError => error
|
94
|
+
# This is supposed to result in HTTP 500.
|
95
|
+
raise self.exception(error, -32700, "Parse error.")
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute(encoded_result, subject)
|
99
|
+
result = self.decode(encoded_result)
|
100
|
+
|
101
|
+
if result.respond_to?(:merge) # Hash, only one result.
|
102
|
+
self.encode(result_or_error(subject, result))
|
103
|
+
else # Array, multiple results.
|
104
|
+
self.encode(
|
105
|
+
result.map do |result|
|
106
|
+
result_or_error(subject, result)
|
107
|
+
end
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def result_or_error(subject, command)
|
113
|
+
method, args = command["method"], command["params"]
|
114
|
+
result = subject.send(method, *args)
|
115
|
+
self.response(result, nil, command["id"])
|
116
|
+
rescue NoMethodError => error
|
117
|
+
error = self.exception(error, -32601, "Method not found.")
|
118
|
+
self.response(nil, error, command["id"])
|
119
|
+
rescue ArgumentError => error
|
120
|
+
error = self.exception(error, -32602, "Invalid params.")
|
121
|
+
self.response(nil, error, command["id"])
|
122
|
+
rescue Exception => exception
|
123
|
+
error = self.exception(exception)
|
124
|
+
self.response(nil, error, command["id"])
|
125
|
+
end
|
126
|
+
|
127
|
+
def response(result, error, id)
|
128
|
+
{jsonrpc: JSON_RPC_VERSION, result: result, error: error, id: id}
|
129
|
+
end
|
130
|
+
|
131
|
+
def encode(response)
|
132
|
+
RPC.log "SERVER ENCODE: #{response.inspect}"
|
133
|
+
JSON.generate(response)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
File without changes
|
data/rpc.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "rpc"
|
8
|
+
s.version = "0.2"
|
9
|
+
s.authors = ["Jakub Stastny aka botanicus"]
|
10
|
+
s.homepage = "http://github.com/ruby-amqp/rpc"
|
11
|
+
s.summary = "Generic RPC library for Ruby."
|
12
|
+
s.description = "#{s.summary} Currently it supports JSON-RPC over HTTP, support for AMQP and Redis will follow soon."
|
13
|
+
s.cert_chain = nil
|
14
|
+
s.email = Base64.decode64("c3Rhc3RueUAxMDFpZGVhcy5jeg==\n")
|
15
|
+
s.has_rdoc = true
|
16
|
+
|
17
|
+
# files
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# Ruby version
|
22
|
+
s.required_ruby_version = ::Gem::Requirement.new("~> 1.9")
|
23
|
+
|
24
|
+
begin
|
25
|
+
require "changelog"
|
26
|
+
rescue LoadError
|
27
|
+
warn "You have to have changelog gem installed for post install message"
|
28
|
+
else
|
29
|
+
s.post_install_message = CHANGELOG.new.version_changes
|
30
|
+
end
|
31
|
+
|
32
|
+
# RubyForge
|
33
|
+
s.rubyforge_project = "rpc"
|
34
|
+
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: rpc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: "0.
|
5
|
+
version: "0.2"
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jakub Stastny aka botanicus
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain:
|
12
|
-
date: 2011-04
|
12
|
+
date: 2011-05-04 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -21,14 +21,32 @@ extensions: []
|
|
21
21
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
|
-
files:
|
25
|
-
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- CHANGELOG
|
27
|
+
- Gemfile
|
28
|
+
- Gemfile.lock
|
29
|
+
- LICENSE
|
30
|
+
- README.textile
|
31
|
+
- examples/em-http-request-json/client.rb
|
32
|
+
- examples/net-http-json/client.rb
|
33
|
+
- examples/net-http-json/console.rb
|
34
|
+
- examples/server.ru
|
35
|
+
- lib/rpc.rb
|
36
|
+
- lib/rpc/clients/amqp/coolio.rb
|
37
|
+
- lib/rpc/clients/amqp/eventmachine.rb
|
38
|
+
- lib/rpc/clients/amqp/socket.rb
|
39
|
+
- lib/rpc/clients/em-http-request.rb
|
40
|
+
- lib/rpc/clients/net-http.rb
|
41
|
+
- lib/rpc/clients/redis.rb
|
42
|
+
- lib/rpc/encoders/json.rb
|
43
|
+
- lib/rpc/encoders/xml.rb
|
44
|
+
- rpc.gemspec
|
26
45
|
has_rdoc: true
|
27
46
|
homepage: http://github.com/ruby-amqp/rpc
|
28
47
|
licenses: []
|
29
48
|
|
30
|
-
post_install_message: "[\e[32mVersion 0.
|
31
|
-
[\e[32mVersion 0.1\e[0m] [FEATURE] JSON-RPC encoder.\n"
|
49
|
+
post_install_message: "[\e[32mVersion 0.2\e[0m] [FEATURE] Full JSON-RPC 2.0 support (added batch, errors and notifications).\n"
|
32
50
|
rdoc_options: []
|
33
51
|
|
34
52
|
require_paths:
|