jimson 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Jimson
2
+ ### JSON-RPC 2.0 Client and Server for Ruby
3
+
4
+ ## Client: Quick Start
5
+ require 'jimson'
6
+ client = Jimson::Client.new("http://www.example.com:8999") # the URL for the JSON-RPC 2.0 server to connect to
7
+ result = client.sum(1,2) # call the 'sum' method on the RPC server and save the result '3'
8
+
9
+ ## Server: Quick Start
10
+ require 'jimson'
11
+
12
+ class MyHandler
13
+ extend Jimson::Handler
14
+
15
+ def sum(a,b)
16
+ a + b
17
+ end
18
+ end
19
+
20
+ server = Jimson::Server.new(MyHandler.new)
21
+ server.start # serve with webrick on http://0.0.0.0:8999/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.4.0
data/lib/jimson.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'jimson/handler'
2
3
  require 'jimson/server'
3
4
  require 'jimson/client'
4
5
 
data/lib/jimson/client.rb CHANGED
@@ -1,6 +1,11 @@
1
+ require 'blankslate'
2
+ module Jimson
3
+ class Client < BlankSlate
4
+ end
5
+ end
6
+
1
7
  require 'rest-client'
2
- require 'jimson/server_error'
3
- require 'jimson/client_error'
8
+ require 'jimson/client/error'
4
9
  require 'jimson/request'
5
10
  require 'jimson/response'
6
11
 
@@ -24,10 +29,14 @@ module Jimson
24
29
  begin
25
30
  data = JSON.parse(resp)
26
31
  rescue
27
- raise Jimson::ClientError::InvalidJSON.new(resp)
32
+ raise Client::Error::InvalidJSON.new(resp)
28
33
  end
29
34
 
30
35
  return process_single_response(data)
36
+
37
+ rescue Exception, StandardError => e
38
+ e.extend(Client::Error) unless e.is_a?(Client::Error)
39
+ raise e
31
40
  end
32
41
 
33
42
  def send_single_request(method, args)
@@ -39,20 +48,17 @@ module Jimson
39
48
  }.to_json
40
49
  resp = RestClient.post(@url, post_data, :content_type => 'application/json')
41
50
  if resp.nil? || resp.body.nil? || resp.body.empty?
42
- raise Jimson::ClientError::InvalidResponse.new
51
+ raise Client::Error::InvalidResponse.new
43
52
  end
44
53
 
45
54
  return resp.body
46
-
47
- rescue Exception, StandardError
48
- raise Jimson::ClientError::InternalError.new($!)
49
55
  end
50
56
 
51
57
  def send_batch_request(batch)
52
58
  post_data = batch.to_json
53
59
  resp = RestClient.post(@url, post_data, :content_type => 'application/json')
54
60
  if resp.nil? || resp.body.nil? || resp.body.empty?
55
- raise Jimson::ClientError::InvalidResponse.new
61
+ raise Client::Error::InvalidResponse.new
56
62
  end
57
63
 
58
64
  return resp.body
@@ -61,29 +67,21 @@ module Jimson
61
67
  def process_batch_response(responses)
62
68
  responses.each do |resp|
63
69
  saved_response = @batch.map { |r| r[1] }.select { |r| r.id == resp['id'] }.first
64
- raise Jimson::ClientError::InvalidResponse.new unless !!saved_response
70
+ raise Client::Error::InvalidResponse.new if saved_response.nil?
65
71
  saved_response.populate!(resp)
66
72
  end
67
73
  end
68
74
 
69
75
  def process_single_response(data)
70
- raise Jimson::ClientError::InvalidResponse.new if !valid_response?(data)
76
+ raise Client::Error::InvalidResponse.new if !valid_response?(data)
71
77
 
72
78
  if !!data['error']
73
79
  code = data['error']['code']
74
- if Jimson::ServerError::CODES.keys.include?(code)
75
- raise Jimson::ServerError::CODES[code].new
76
- else
77
- raise Jimson::ClientError::UnknownServerError.new(code, data['error']['message'])
78
- end
80
+ msg = data['error']['message']
81
+ raise Client::Error::ServerError.new(code, msg)
79
82
  end
80
83
 
81
84
  return data['result']
82
-
83
- rescue Jimson::ClientError::Generic, Jimson::ServerError::Generic => e
84
- raise e
85
- rescue Exception, StandardError => e
86
- raise Jimson::ClientError::InternalError.new(e)
87
85
  end
88
86
 
89
87
  def valid_response?(data)
@@ -113,7 +111,7 @@ module Jimson
113
111
 
114
112
  def push_batch_request(request)
115
113
  request.id = self.class.make_id
116
- response = Jimson::Response.new(request.id)
114
+ response = Response.new(request.id)
117
115
  @batch << [request, response]
118
116
  return response
119
117
  end
@@ -125,7 +123,7 @@ module Jimson
125
123
  begin
126
124
  responses = JSON.parse(response)
127
125
  rescue
128
- raise Jimson::ClientError::InvalidJSON.new(json)
126
+ raise Client::Error::InvalidJSON.new(json)
129
127
  end
130
128
 
131
129
  process_batch_response(responses)
@@ -134,7 +132,7 @@ module Jimson
134
132
 
135
133
  end
136
134
 
137
- class BatchClient
135
+ class BatchClient < BlankSlate
138
136
 
139
137
  def initialize(helper)
140
138
  @helper = helper
@@ -147,7 +145,10 @@ module Jimson
147
145
 
148
146
  end
149
147
 
150
- class Client
148
+ class Client < BlankSlate
149
+ reveal :instance_variable_get
150
+ reveal :inspect
151
+ reveal :to_s
151
152
 
152
153
  def self.batch(client)
153
154
  helper = client.instance_variable_get(:@helper)
@@ -161,7 +162,11 @@ module Jimson
161
162
  end
162
163
 
163
164
  def method_missing(sym, *args, &block)
164
- @helper.process_call(sym, args)
165
+ self[sym, args]
166
+ end
167
+
168
+ def [](method, *args)
169
+ @helper.process_call(method, args.flatten)
165
170
  end
166
171
 
167
172
  end
@@ -0,0 +1,23 @@
1
+ module Jimson
2
+ class Client
3
+ module Error
4
+ class InvalidResponse < StandardError
5
+ def initialize()
6
+ super('Invalid or empty response from server.')
7
+ end
8
+ end
9
+
10
+ class InvalidJSON < StandardError
11
+ def initialize(json)
12
+ super("Couldn't parse JSON string received from server:\n#{json}")
13
+ end
14
+ end
15
+
16
+ class ServerError < StandardError
17
+ def initialize(code, message)
18
+ super("Server error #{code}: #{message}")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Jimson
2
+ module Handler
3
+
4
+ def jimson_default_methods
5
+ self.instance_methods.map(&:to_s) - Object.methods.map(&:to_s)
6
+ end
7
+
8
+ def jimson_expose(*methods)
9
+ @jimson_exposed_methods ||= []
10
+ @jimson_exposed_methods += methods.map(&:to_s)
11
+ end
12
+
13
+ def jimson_exclude(*methods)
14
+ @jimson_excluded_methods ||= []
15
+ @jimson_excluded_methods += methods.map(&:to_s)
16
+ end
17
+
18
+ def jimson_exposed_methods
19
+ @jimson_exposed_methods ||= []
20
+ @jimson_excluded_methods ||= []
21
+ (jimson_default_methods - @jimson_excluded_methods + @jimson_exposed_methods).sort.map(&:to_sym)
22
+ end
23
+
24
+ end
25
+ end
data/lib/jimson/server.rb CHANGED
@@ -2,10 +2,28 @@ require 'rack'
2
2
  require 'rack/request'
3
3
  require 'rack/response'
4
4
  require 'json'
5
+ require 'jimson/handler'
6
+ require 'jimson/server/error'
5
7
 
6
8
  module Jimson
7
9
  class Server
8
10
 
11
+ class System
12
+ extend Handler
13
+
14
+ def initialize(handler)
15
+ @handler = handler
16
+ end
17
+
18
+ def listMethods
19
+ @handler.class.jimson_exposed_methods
20
+ end
21
+
22
+ def isAlive
23
+ true
24
+ end
25
+ end
26
+
9
27
  JSON_RPC_VERSION = '2.0'
10
28
 
11
29
  attr_accessor :handler, :host, :port
@@ -52,17 +70,17 @@ module Jimson
52
70
  begin
53
71
  request = parse_request(content)
54
72
  if request.is_a?(Array)
55
- raise Jimson::ServerError::InvalidRequest.new if request.empty?
73
+ raise Server::Error::InvalidRequest.new if request.empty?
56
74
  response = request.map { |req| handle_request(req) }
57
75
  else
58
76
  response = handle_request(request)
59
77
  end
60
- rescue Jimson::ServerError::ParseError, Jimson::ServerError::InvalidRequest => e
78
+ rescue Server::Error::ParseError, Server::Error::InvalidRequest => e
61
79
  response = error_response(e)
62
- rescue Jimson::ServerError::Generic => e
80
+ rescue Server::Error => e
63
81
  response = error_response(e, request)
64
- rescue StandardError, Exception
65
- response = error_response(Jimson::ServerError::InternalError.new)
82
+ rescue StandardError, Exception => e
83
+ response = error_response(Server::Error::InternalError.new(e))
66
84
  end
67
85
 
68
86
  response.compact! if response.is_a?(Array)
@@ -76,11 +94,11 @@ module Jimson
76
94
  response = nil
77
95
  begin
78
96
  if !validate_request(request)
79
- response = error_response(Jimson::ServerError::InvalidRequest.new)
97
+ response = error_response(Server::Error::InvalidRequest.new)
80
98
  else
81
99
  response = create_response(request)
82
100
  end
83
- rescue Jimson::ServerError::Generic => e
101
+ rescue Server::Error => e
84
102
  response = error_response(e, request)
85
103
  end
86
104
 
@@ -112,20 +130,9 @@ module Jimson
112
130
  end
113
131
 
114
132
  def create_response(request)
133
+ method = request['method']
115
134
  params = request['params']
116
- begin
117
- if params.is_a?(Hash)
118
- result = @handler.send(request['method'], params)
119
- else
120
- result = @handler.send(request['method'], *params)
121
- end
122
- rescue NoMethodError
123
- raise Jimson::ServerError::MethodNotFound.new
124
- rescue ArgumentError
125
- raise Jimson::ServerError::InvalidParams.new
126
- rescue
127
- raise Jimson::ServerError::ApplicationError.new($!)
128
- end
135
+ result = dispatch_request(method, params)
129
136
 
130
137
  response = success_response(request, result)
131
138
 
@@ -134,7 +141,42 @@ module Jimson
134
141
  # that are within a batch request.
135
142
  response = nil if !request.has_key?('id')
136
143
 
137
- response
144
+ return response
145
+
146
+ rescue Server::Error => e
147
+ raise e
148
+ rescue ArgumentError
149
+ raise Server::Error::InvalidParams.new
150
+ rescue Exception, StandardError => e
151
+ raise Server::Error::ApplicationError.new(e)
152
+ end
153
+
154
+ def dispatch_request(method, params)
155
+ # normally route requests to the user-suplied handler
156
+ handler = @handler
157
+
158
+ # switch to the System handler if a system method was called
159
+ sys_regex = /^system\./
160
+ if method =~ sys_regex
161
+ handler = System.new(@handler)
162
+ # remove the 'system.' prefix before from the method name
163
+ method.gsub!(sys_regex, '')
164
+ end
165
+
166
+ method = method.to_sym
167
+
168
+ if !handler.class.jimson_exposed_methods.include?(method) \
169
+ || !handler.respond_to?(method)
170
+ raise Server::Error::MethodNotFound.new(method)
171
+ end
172
+
173
+ if params.nil?
174
+ return handler.send(method)
175
+ elsif params.is_a?(Hash)
176
+ return handler.send(method, params)
177
+ else
178
+ return handler.send(method, *params)
179
+ end
138
180
  end
139
181
 
140
182
  def error_response(error, request = nil)
@@ -162,7 +204,7 @@ module Jimson
162
204
  def parse_request(post)
163
205
  data = JSON.parse(post)
164
206
  rescue
165
- raise Jimson::ServerError::ParseError.new
207
+ raise Server::Error::ParseError.new
166
208
  end
167
209
 
168
210
  end
@@ -0,0 +1,64 @@
1
+ module Jimson
2
+ class Server
3
+ class Error < StandardError
4
+ attr_accessor :code, :message
5
+
6
+ def initialize(code, message)
7
+ @code = code
8
+ @message = message
9
+ super(message)
10
+ end
11
+
12
+ def to_h
13
+ {
14
+ 'code' => @code,
15
+ 'message' => @message
16
+ }
17
+ end
18
+
19
+ class ParseError < Error
20
+ def initialize
21
+ super(-32700, 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.')
22
+ end
23
+ end
24
+
25
+ class InvalidRequest < Error
26
+ def initialize
27
+ super(-32600, 'The JSON sent is not a valid Request object.')
28
+ end
29
+ end
30
+
31
+ class MethodNotFound < Error
32
+ def initialize(method)
33
+ super(-32601, "Method '#{method}' not found.")
34
+ end
35
+ end
36
+
37
+ class InvalidParams < Error
38
+ def initialize
39
+ super(-32602, 'Invalid method parameter(s).')
40
+ end
41
+ end
42
+
43
+ class InternalError < Error
44
+ def initialize(e)
45
+ super(-32603, "Internal server error: #{e}")
46
+ end
47
+ end
48
+
49
+ class ApplicationError < Error
50
+ def initialize(err)
51
+ super(-32099, "Application error: #{err} #{err.backtrace.join("\n")}")
52
+ end
53
+ end
54
+
55
+ CODES = {
56
+ -32700 => ParseError,
57
+ -32600 => InvalidRequest,
58
+ -32601 => MethodNotFound,
59
+ -32602 => InvalidParams,
60
+ -32603 => InternalError
61
+ }
62
+ end
63
+ end
64
+ end
data/spec/client_spec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  module Jimson
2
4
  describe Client do
3
5
  BOILERPLATE = {'jsonrpc' => '2.0', 'id' => 1}
@@ -10,6 +12,42 @@ module Jimson
10
12
  after(:each) do
11
13
  end
12
14
 
15
+ describe "hidden methods" do
16
+ it "should reveal inspect" do
17
+ Client.new(SPEC_URL).inspect.should match /Jimson::Client/
18
+ end
19
+
20
+ it "should reveal to_s" do
21
+ Client.new(SPEC_URL).to_s.should match /Jimson::Client/
22
+ end
23
+ end
24
+
25
+ describe "#[]" do
26
+ before(:each) do
27
+ expected = {
28
+ 'jsonrpc' => '2.0',
29
+ 'method' => 'foo',
30
+ 'params' => [1,2,3],
31
+ 'id' => 1
32
+ }.to_json
33
+ response = BOILERPLATE.merge({'result' => 42}).to_json
34
+ RestClient.should_receive(:post).with(SPEC_URL, expected, {:content_type => 'application/json'}).and_return(@resp_mock)
35
+ @resp_mock.should_receive(:body).at_least(:once).and_return(response)
36
+ @client = Client.new(SPEC_URL)
37
+ end
38
+
39
+ context "when using an array of args" do
40
+ it "sends a request with the correct method and args" do
41
+ @client['foo', [1,2,3]].should == 42
42
+ end
43
+ end
44
+ context "when using a splat for args" do
45
+ it "sends a request with the correct method and args" do
46
+ @client['foo', 1, 2, 3].should == 42
47
+ end
48
+ end
49
+ end
50
+
13
51
  describe "sending a single request" do
14
52
  context "when using positional parameters" do
15
53
  before(:each) do
@@ -73,5 +111,17 @@ module Jimson
73
111
  end
74
112
  end
75
113
 
114
+ describe "error handling" do
115
+ context "when an error occurs in the Jimson::Client code" do
116
+ it "tags the raised exception with Jimson::Client::Error" do
117
+ client_helper = ClientHelper.new(SPEC_URL)
118
+ ClientHelper.stub!(:new).and_return(client_helper)
119
+ client = Client.new(SPEC_URL)
120
+ client_helper.stub!(:send_single_request).and_raise "intentional error"
121
+ lambda { client.foo }.should raise_error(Jimson::Client::Error)
122
+ end
123
+ end
124
+ end
125
+
76
126
  end
77
127
  end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ module Jimson
4
+ describe Handler do
5
+
6
+ class FooHandler
7
+ extend Jimson::Handler
8
+
9
+ jimson_expose :to_s, :bye
10
+
11
+ jimson_exclude :hi, :bye
12
+
13
+ def hi
14
+ 'hi'
15
+ end
16
+
17
+ def bye
18
+ 'bye'
19
+ end
20
+
21
+ def to_s
22
+ 'foo'
23
+ end
24
+
25
+ def so_exposed
26
+ "I'm so exposed!"
27
+ end
28
+ end
29
+
30
+ let(:foo) { FooHandler.new }
31
+
32
+ describe "#jimson_expose" do
33
+ it "exposes a method even if it was defined on Object" do
34
+ foo.class.jimson_exposed_methods.should include(:to_s)
35
+ end
36
+ end
37
+
38
+ describe "#jimson_exclude" do
39
+ context "when a method was not explicitly exposed" do
40
+ it "excludes the method" do
41
+ foo.class.jimson_exposed_methods.should_not include(:hi)
42
+ end
43
+ end
44
+ context "when a method was explicitly exposed" do
45
+ it "does not exclude the method" do
46
+ foo.class.jimson_exposed_methods.should include(:bye)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#jimson_exposed_methods" do
52
+ it "doesn't include methods defined on Object" do
53
+ foo.class.jimson_exposed_methods.should_not include(:object_id)
54
+ end
55
+ it "includes methods defined on the extending class but not on Object" do
56
+ foo.class.jimson_exposed_methods.should include(:so_exposed)
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+
data/spec/server_spec.rb CHANGED
@@ -1,7 +1,37 @@
1
1
  require 'spec_helper'
2
+ require 'rack/test'
2
3
 
3
4
  module Jimson
4
5
  describe Server do
6
+ include Rack::Test::Methods
7
+
8
+ class TestHandler
9
+ extend Jimson::Handler
10
+
11
+ def subtract(a, b = nil)
12
+ if a.is_a?(Hash)
13
+ return a['minuend'] - a['subtrahend']
14
+ else
15
+ return a - b
16
+ end
17
+ end
18
+
19
+ def sum(a,b,c)
20
+ a + b + c
21
+ end
22
+
23
+ def notify_hello(*args)
24
+ # notification, doesn't do anything
25
+ end
26
+
27
+ def update(*args)
28
+ # notification, doesn't do anything
29
+ end
30
+
31
+ def get_data
32
+ ['hello', 5]
33
+ end
34
+ end
5
35
 
6
36
  INVALID_RESPONSE_EXPECTATION = {
7
37
  'jsonrpc' => '2.0',
@@ -11,6 +41,14 @@ module Jimson
11
41
  },
12
42
  'id' => nil
13
43
  }
44
+ def app
45
+ Server.new(TestHandler.new)
46
+ end
47
+
48
+ def post_json(hash)
49
+ post '/', hash.to_json, {'Content-Type' => 'application/json'}
50
+ end
51
+
14
52
  before(:each) do
15
53
  @url = SPEC_URL
16
54
  end
@@ -24,7 +62,10 @@ module Jimson
24
62
  'params' => [24, 20],
25
63
  'id' => 1
26
64
  }
27
- resp = JSON.parse(RestClient.post(@url, req.to_json).body)
65
+ post_json(req)
66
+
67
+ last_response.should be_ok
68
+ resp = JSON.parse(last_response.body)
28
69
  resp.should == {
29
70
  'jsonrpc' => '2.0',
30
71
  'result' => 4,
@@ -43,7 +84,10 @@ module Jimson
43
84
  'params' => {'subtrahend'=> 20, 'minuend' => 24},
44
85
  'id' => 1
45
86
  }
46
- resp = JSON.parse(RestClient.post(@url, req.to_json).body)
87
+ post_json(req)
88
+
89
+ last_response.should be_ok
90
+ resp = JSON.parse(last_response.body)
47
91
  resp.should == {
48
92
  'jsonrpc' => '2.0',
49
93
  'result' => 4,
@@ -61,8 +105,8 @@ module Jimson
61
105
  'method' => 'update',
62
106
  'params' => [1,2,3,4,5]
63
107
  }
64
- resp = RestClient.post(@url, req.to_json).body
65
- resp.should be_empty
108
+ post_json(req)
109
+ last_response.body.should be_empty
66
110
  end
67
111
  end
68
112
  end
@@ -74,12 +118,35 @@ module Jimson
74
118
  'method' => 'foobar',
75
119
  'id' => 1
76
120
  }
77
- resp = JSON.parse(RestClient.post(@url, req.to_json).body)
121
+ post_json(req)
122
+
123
+ resp = JSON.parse(last_response.body)
124
+ resp.should == {
125
+ 'jsonrpc' => '2.0',
126
+ 'error' => {
127
+ 'code' => -32601,
128
+ 'message' => "Method 'foobar' not found."
129
+ },
130
+ 'id' => 1
131
+ }
132
+ end
133
+ end
134
+
135
+ describe "receiving a call for a method which exists but is not exposed" do
136
+ it "returns an error response" do
137
+ req = {
138
+ 'jsonrpc' => '2.0',
139
+ 'method' => 'object_id',
140
+ 'id' => 1
141
+ }
142
+ post_json(req)
143
+
144
+ resp = JSON.parse(last_response.body)
78
145
  resp.should == {
79
146
  'jsonrpc' => '2.0',
80
147
  'error' => {
81
148
  'code' => -32601,
82
- 'message' => 'Method not found.'
149
+ 'message' => "Method 'object_id' not found."
83
150
  },
84
151
  'id' => 1
85
152
  }
@@ -94,7 +161,9 @@ module Jimson
94
161
  'params' => [1,2,3],
95
162
  'id' => 1
96
163
  }
97
- resp = JSON.parse(RestClient.post(@url, req.to_json).body)
164
+ post_json(req)
165
+
166
+ resp = JSON.parse(last_response.body)
98
167
  resp.should == {
99
168
  'jsonrpc' => '2.0',
100
169
  'error' => {
@@ -114,7 +183,9 @@ module Jimson
114
183
  'id' => 1
115
184
  }.to_json
116
185
  req += '}' # make the json invalid
117
- resp = JSON.parse(RestClient.post(@url, req).body)
186
+ post '/', req, {'Content-type' => 'application/json'}
187
+
188
+ resp = JSON.parse(last_response.body)
118
189
  resp.should == {
119
190
  'jsonrpc' => '2.0',
120
191
  'error' => {
@@ -132,24 +203,27 @@ module Jimson
132
203
  req = {
133
204
  'jsonrpc' => '2.0',
134
205
  'method' => 1 # method as int is invalid
135
- }.to_json
136
- resp = JSON.parse(RestClient.post(@url, req).body)
206
+ }
207
+ post_json(req)
208
+ resp = JSON.parse(last_response.body)
137
209
  resp.should == INVALID_RESPONSE_EXPECTATION
138
210
  end
139
211
  end
140
212
 
141
213
  context "when the request is an empty batch" do
142
214
  it "returns an error response" do
143
- req = [].to_json
144
- resp = JSON.parse(RestClient.post(@url, req).body)
215
+ req = []
216
+ post_json(req)
217
+ resp = JSON.parse(last_response.body)
145
218
  resp.should == INVALID_RESPONSE_EXPECTATION
146
219
  end
147
220
  end
148
221
 
149
222
  context "when the request is an invalid batch" do
150
223
  it "returns an error response" do
151
- req = [1,2].to_json
152
- resp = JSON.parse(RestClient.post(@url, req).body)
224
+ req = [1,2]
225
+ post_json(req)
226
+ resp = JSON.parse(last_response.body)
153
227
  resp.should == [INVALID_RESPONSE_EXPECTATION, INVALID_RESPONSE_EXPECTATION]
154
228
  end
155
229
  end
@@ -165,13 +239,14 @@ module Jimson
165
239
  {'foo' => 'boo'},
166
240
  {'jsonrpc' => '2.0', 'method' => 'foo.get', 'params' => {'name' => 'myself'}, 'id' => '5'},
167
241
  {'jsonrpc' => '2.0', 'method' => 'get_data', 'id' => '9'}
168
- ].to_json
169
- resp = JSON.parse(RestClient.post(@url, reqs).body)
242
+ ]
243
+ post_json(reqs)
244
+ resp = JSON.parse(last_response.body)
170
245
  resp.should == [
171
246
  {'jsonrpc' => '2.0', 'result' => 7, 'id' => '1'},
172
247
  {'jsonrpc' => '2.0', 'result' => 19, 'id' => '2'},
173
248
  {'jsonrpc' => '2.0', 'error' => {'code' => -32600, 'message' => 'The JSON sent is not a valid Request object.'}, 'id' => nil},
174
- {'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' => 'Method not found.'}, 'id' => '5'},
249
+ {'jsonrpc' => '2.0', 'error' => {'code' => -32601, 'message' => "Method 'foo.get' not found."}, 'id' => '5'},
175
250
  {'jsonrpc' => '2.0', 'result' => ['hello', 5], 'id' => '9'}
176
251
  ]
177
252
  end
@@ -191,11 +266,51 @@ module Jimson
191
266
  'params' => [1,2,3,4,5]
192
267
  }
193
268
  ]
194
- resp = RestClient.post(@url, req.to_json).body
195
- resp.should be_empty
269
+ post_json(req)
270
+ last_response.body.should be_empty
196
271
  end
197
272
  end
198
273
  end
199
274
 
275
+ describe "receiving a 'system.' request" do
276
+ context "when the request is 'isAlive'" do
277
+ it "returns response 'true'" do
278
+ req = {
279
+ 'jsonrpc' => '2.0',
280
+ 'method' => 'system.isAlive',
281
+ 'params' => [],
282
+ 'id' => 1
283
+ }
284
+ post_json(req)
285
+
286
+ last_response.should be_ok
287
+ resp = JSON.parse(last_response.body)
288
+ resp.should == {
289
+ 'jsonrpc' => '2.0',
290
+ 'result' => true,
291
+ 'id' => 1
292
+ }
293
+ end
294
+ end
295
+ context "when the request is 'listMethods'" do
296
+ it "returns response with all listMethods on the handler as strings" do
297
+ req = {
298
+ 'jsonrpc' => '2.0',
299
+ 'method' => 'system.listMethods',
300
+ 'params' => [],
301
+ 'id' => 1
302
+ }
303
+ post_json(req)
304
+
305
+ last_response.should be_ok
306
+ resp = JSON.parse(last_response.body)
307
+ resp.should == {
308
+ 'jsonrpc' => '2.0',
309
+ 'result' => ['subtract', 'sum', 'notify_hello', 'update', 'get_data'].sort,
310
+ 'id' => 1
311
+ }
312
+ end
313
+ end
314
+ end
200
315
  end
201
316
  end
data/spec/spec_helper.rb CHANGED
@@ -2,18 +2,6 @@ require 'rubygems'
2
2
  $:.unshift(File.dirname(__FILE__) + '/../lib/')
3
3
  require 'jimson/server'
4
4
  require 'jimson/client'
5
- require 'open-uri'
6
5
  require 'json'
7
6
 
8
7
  SPEC_URL = 'http://localhost:8999'
9
-
10
- pid = Process.spawn(File.dirname(__FILE__) + '/server_helper.rb')
11
-
12
- RSpec.configure do |config|
13
- config.after(:all) do
14
- Process.kill(9, pid)
15
- end
16
- end
17
-
18
- sleep 1
19
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jimson
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-11 00:00:00.000000000Z
12
+ date: 2011-09-07 00:00:00.000000000Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: blankslate
16
+ requirement: &13756100 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.1.2.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *13756100
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rest-client
16
- requirement: &12229180 !ruby/object:Gem::Requirement
27
+ requirement: &13755420 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ! '>='
@@ -21,10 +32,10 @@ dependencies:
21
32
  version: 1.6.3
22
33
  type: :runtime
23
34
  prerelease: false
24
- version_requirements: *12229180
35
+ version_requirements: *13755420
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: json
27
- requirement: &12228700 !ruby/object:Gem::Requirement
38
+ requirement: &13754820 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: 1.5.1
33
44
  type: :runtime
34
45
  prerelease: false
35
- version_requirements: *12228700
46
+ version_requirements: *13754820
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: rack
38
- requirement: &12225120 !ruby/object:Gem::Requirement
49
+ requirement: &13754200 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
@@ -43,29 +54,30 @@ dependencies:
43
54
  version: '1.3'
44
55
  type: :runtime
45
56
  prerelease: false
46
- version_requirements: *12225120
57
+ version_requirements: *13754200
47
58
  description:
48
59
  email:
49
60
  executables: []
50
61
  extensions: []
51
62
  extra_rdoc_files:
52
- - README.rdoc
63
+ - README.md
53
64
  files:
54
65
  - VERSION
55
66
  - LICENSE.txt
56
67
  - CHANGELOG.rdoc
57
- - README.rdoc
68
+ - README.md
58
69
  - Rakefile
59
70
  - lib/jimson.rb
60
- - lib/jimson/client_error.rb
71
+ - lib/jimson/server/error.rb
72
+ - lib/jimson/handler.rb
61
73
  - lib/jimson/server.rb
62
- - lib/jimson/server_error.rb
63
74
  - lib/jimson/response.rb
75
+ - lib/jimson/client/error.rb
64
76
  - lib/jimson/request.rb
65
77
  - lib/jimson/client.rb
78
+ - spec/handler_spec.rb
66
79
  - spec/server_spec.rb
67
80
  - spec/client_spec.rb
68
- - spec/server_helper.rb
69
81
  - spec/spec_helper.rb
70
82
  homepage: http://www.github.com/chriskite/jimson
71
83
  licenses: []
@@ -92,7 +104,7 @@ signing_key:
92
104
  specification_version: 3
93
105
  summary: JSON-RPC 2.0 client and server
94
106
  test_files:
107
+ - spec/handler_spec.rb
95
108
  - spec/server_spec.rb
96
109
  - spec/client_spec.rb
97
- - spec/server_helper.rb
98
110
  - spec/spec_helper.rb
data/README.rdoc DELETED
File without changes
@@ -1,31 +0,0 @@
1
- module Jimson
2
- module ClientError
3
- class Generic < Exception
4
- end
5
-
6
- class InvalidResponse < Generic
7
- def initialize()
8
- super('Invalid or empty response from server.')
9
- end
10
- end
11
-
12
- class InvalidJSON < Generic
13
- def initialize(json)
14
- super("Couldn't parse JSON string received from server:\n#{json}")
15
- end
16
- end
17
-
18
- class InternalError < Generic
19
- def initialize(e)
20
- super("An internal client error occurred when processing the request: #{e}\n#{e.backtrace.join("\n")}")
21
- end
22
- end
23
-
24
- class UnknownServerError < Generic
25
- def initialize(code, message)
26
- super("The server specified an error the client doesn't know about: #{code} #{message}")
27
- end
28
- end
29
-
30
- end
31
- end
@@ -1,64 +0,0 @@
1
- module Jimson
2
- module ServerError
3
- class Generic < Exception
4
- attr_accessor :code, :message
5
-
6
- def initialize(code, message)
7
- @code = code
8
- @message = message
9
- super(message)
10
- end
11
-
12
- def to_h
13
- {
14
- 'code' => @code,
15
- 'message' => @message
16
- }
17
- end
18
- end
19
-
20
- class ParseError < Generic
21
- def initialize
22
- super(-32700, 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.')
23
- end
24
- end
25
-
26
- class InvalidRequest < Generic
27
- def initialize
28
- super(-32600, 'The JSON sent is not a valid Request object.')
29
- end
30
- end
31
-
32
- class MethodNotFound < Generic
33
- def initialize
34
- super(-32601, 'Method not found.')
35
- end
36
- end
37
-
38
- class InvalidParams < Generic
39
- def initialize
40
- super(-32602, 'Invalid method parameter(s).')
41
- end
42
- end
43
-
44
- class InternalError < Generic
45
- def initialize
46
- super(-32603, 'Internal server error.')
47
- end
48
- end
49
-
50
- class ApplicationError < Generic
51
- def initialize(err)
52
- super(-32099, "The application being served raised an error: #{err}")
53
- end
54
- end
55
-
56
- CODES = {
57
- -32700 => ParseError,
58
- -32600 => InvalidRequest,
59
- -32601 => MethodNotFound,
60
- -32602 => InvalidParams,
61
- -32603 => InternalError
62
- }
63
- end
64
- end
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'rubygems'
4
- $:.unshift(File.dirname(__FILE__) + '/../lib/')
5
- require 'jimson'
6
-
7
- class TestHandler
8
- def subtract(a, b = nil)
9
- if a.is_a?(Hash)
10
- return a['minuend'] - a['subtrahend']
11
- else
12
- return a - b
13
- end
14
- end
15
-
16
- def sum(a,b,c)
17
- a + b + c
18
- end
19
-
20
- def notify_hello(*args)
21
- # notification, doesn't do anything
22
- end
23
-
24
- def update(*args)
25
- # notification, doesn't do anything
26
- end
27
-
28
- def get_data
29
- ['hello', 5]
30
- end
31
- end
32
-
33
- server = Jimson::Server.new(TestHandler.new)
34
- server.start