jimson 0.6.0 → 0.7.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/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.7.0 / 2012-04-13
2
+
3
+ * Major enhancements
4
+
5
+ * Add namespaced routing
6
+
7
+ * Bug fixes
8
+
9
+ * Fix deprecation warning about RDoc task in Rakefile
10
+
1
11
  == 0.6.0 / 2012-03-14
2
12
 
3
13
  * Minor enhancements
data/Rakefile CHANGED
@@ -14,7 +14,12 @@ end
14
14
 
15
15
  task :default => :rspec
16
16
 
17
- require 'rake/rdoctask'
17
+ begin
18
+ require 'rdoc/task'
19
+ rescue LoadError
20
+ require 'rake/rdoctask'
21
+ end
22
+
18
23
  Rake::RDocTask.new do |rdoc|
19
24
  version = File.exist?('VERSION') ? File.read('VERSION') : ""
20
25
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/lib/jimson.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'jimson/handler'
3
+ require 'jimson/router'
3
4
  require 'jimson/server'
4
5
  require 'jimson/client'
5
6
 
@@ -0,0 +1,24 @@
1
+ require 'jimson/router/map'
2
+ require 'forwardable'
3
+
4
+ module Jimson
5
+ class Router
6
+ extend Forwardable
7
+
8
+ def_delegators :@map, :handler_for_method,
9
+ :root,
10
+ :namespace,
11
+ :jimson_methods,
12
+ :strip_method_namespace
13
+
14
+ def initialize
15
+ @map = Map.new
16
+ end
17
+
18
+ def draw(&block)
19
+ @map.instance_eval &block
20
+ self
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,73 @@
1
+ module Jimson
2
+ class Router
3
+
4
+ #
5
+ # Provides a DSL for routing method namespaces to handlers.
6
+ # Only handles root-level and non-nested namespaces, e.g. 'foo.bar' or 'foo'.
7
+ #
8
+ class Map
9
+
10
+ def initialize
11
+ @routes = {}
12
+ end
13
+
14
+ #
15
+ # Set the root handler, i.e. the handler used for a bare method like 'foo'
16
+ #
17
+ def root(handler)
18
+ @routes[''] = handler
19
+ end
20
+
21
+ #
22
+ # Define the handler for a namespace
23
+ #
24
+ def namespace(ns, handler = nil, &block)
25
+ if !!handler
26
+ @routes[ns.to_s] = handler
27
+ else
28
+ # passed a block for nested namespacing
29
+ map = Jimson::Router::Map.new
30
+ @routes[ns.to_s] = map
31
+ map.instance_eval &block
32
+ end
33
+ end
34
+
35
+ #
36
+ # Return the handler for a (possibly namespaced) method name
37
+ #
38
+ def handler_for_method(method)
39
+ parts = method.split('.')
40
+ ns = (method.index('.') == nil ? '' : parts.first)
41
+ handler = @routes[ns]
42
+ if handler.is_a?(Jimson::Router::Map)
43
+ return handler.handler_for_method(parts[1..-1].join('.'))
44
+ end
45
+ handler
46
+ end
47
+
48
+ #
49
+ # Strip off the namespace part of a method and return the bare method name
50
+ #
51
+ def strip_method_namespace(method)
52
+ method.split('.').last
53
+ end
54
+
55
+ #
56
+ # Return an array of all methods on handlers in the map, fully namespaced
57
+ #
58
+ def jimson_methods
59
+ arr = @routes.keys.map do |ns|
60
+ prefix = (ns == '' ? '' : "#{ns}.")
61
+ handler = @routes[ns]
62
+ if handler.is_a?(Jimson::Router::Map)
63
+ handler.jimson_methods
64
+ else
65
+ handler.class.jimson_exposed_methods.map { |method| prefix + method }
66
+ end
67
+ end
68
+ arr.flatten
69
+ end
70
+
71
+ end
72
+ end
73
+ end
data/lib/jimson/server.rb CHANGED
@@ -3,6 +3,7 @@ require 'rack/request'
3
3
  require 'rack/response'
4
4
  require 'multi_json'
5
5
  require 'jimson/handler'
6
+ require 'jimson/router'
6
7
  require 'jimson/server/error'
7
8
 
8
9
  module Jimson
@@ -11,12 +12,12 @@ module Jimson
11
12
  class System
12
13
  extend Handler
13
14
 
14
- def initialize(handler)
15
- @handler = handler
15
+ def initialize(router)
16
+ @router = router
16
17
  end
17
18
 
18
19
  def listMethods
19
- @handler.class.jimson_exposed_methods
20
+ @router.jimson_methods
20
21
  end
21
22
 
22
23
  def isAlive
@@ -26,10 +27,10 @@ module Jimson
26
27
 
27
28
  JSON_RPC_VERSION = '2.0'
28
29
 
29
- attr_accessor :handler, :host, :port, :opts
30
+ attr_accessor :router, :host, :port, :opts
30
31
 
31
32
  #
32
- # +handler+ is an instance of the class to expose as a JSON-RPC server
33
+ # +router_or_handler+ is an instance of Jimson::Router or extends Jimson::Handler
33
34
  #
34
35
  # +opts+ may include:
35
36
  # * :host - the hostname or ip to bind to
@@ -38,8 +39,17 @@ module Jimson
38
39
  #
39
40
  # Remaining options are forwarded to the underlying Rack server.
40
41
  #
41
- def initialize(handler, opts = {})
42
- @handler = handler
42
+ def initialize(router_or_handler, opts = {})
43
+ if !router_or_handler.is_a?(Router)
44
+ # arg is a handler, wrap it in a Router
45
+ @router = Router.new
46
+ @router.root router_or_handler
47
+ else
48
+ # arg is a router
49
+ @router = router_or_handler
50
+ end
51
+ @router.namespace 'system', System.new(@router)
52
+
43
53
  @host = opts.delete(:host) || '0.0.0.0'
44
54
  @port = opts.delete(:port) || 8999
45
55
  @opts = opts
@@ -153,30 +163,22 @@ module Jimson
153
163
  end
154
164
 
155
165
  def dispatch_request(method, params)
156
- # normally route requests to the user-suplied handler
157
- handler = @handler
158
-
159
- # switch to the System handler if a system method was called
160
- sys_regex = /^system\./
161
- if method =~ sys_regex
162
- handler = System.new(@handler)
163
- # remove the 'system.' prefix before from the method name
164
- method.gsub!(sys_regex, '')
165
- end
166
-
167
- method = method.to_s
166
+ method_name = method.to_s
167
+ handler = @router.handler_for_method(method_name)
168
+ method_name = @router.strip_method_namespace(method_name)
168
169
 
169
- if !handler.class.jimson_exposed_methods.include?(method) \
170
- || !handler.respond_to?(method)
170
+ if handler.nil? \
171
+ || !handler.class.jimson_exposed_methods.include?(method_name) \
172
+ || !handler.respond_to?(method_name)
171
173
  raise Server::Error::MethodNotFound.new(method)
172
174
  end
173
175
 
174
176
  if params.nil?
175
- return handler.send(method)
177
+ return handler.send(method_name)
176
178
  elsif params.is_a?(Hash)
177
- return handler.send(method, params)
179
+ return handler.send(method_name, params)
178
180
  else
179
- return handler.send(method, *params)
181
+ return handler.send(method_name, *params)
180
182
  end
181
183
  end
182
184
 
@@ -48,7 +48,7 @@ module Jimson
48
48
 
49
49
  class ApplicationError < Error
50
50
  def initialize(err)
51
- super(-32099, "Application error: #{err} #{err.backtrace.join("\n")}")
51
+ super(-32099, "Server application error")
52
52
  end
53
53
  end
54
54
 
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module Jimson
4
+ describe Router do
5
+
6
+ let(:router) { Router.new }
7
+
8
+ class RouterFooHandler
9
+ extend Jimson::Handler
10
+
11
+ def hi
12
+ 'hi'
13
+ end
14
+ end
15
+
16
+ class RouterBarHandler
17
+ extend Jimson::Handler
18
+
19
+ def bye
20
+ 'bye'
21
+ end
22
+ end
23
+
24
+
25
+ describe '#draw' do
26
+ context 'when given non-nested namespaces' do
27
+ it 'takes a block with a DSL to set the root and namespaces' do
28
+ router.draw do
29
+ root 'foo'
30
+ namespace 'ns', 'bar'
31
+ end
32
+
33
+ router.handler_for_method('hi').should == 'foo'
34
+ router.handler_for_method('ns.hi').should == 'bar'
35
+ end
36
+ end
37
+
38
+ context 'when given nested namespaces' do
39
+ it 'takes a block with a DSL to set the root and namespaces' do
40
+ router.draw do
41
+ root 'foo'
42
+ namespace 'ns1' do
43
+ root 'blah'
44
+ namespace 'ns2', 'bar'
45
+ end
46
+ end
47
+
48
+ router.handler_for_method('hi').should == 'foo'
49
+ router.handler_for_method('ns1.hi').should == 'blah'
50
+ router.handler_for_method('ns1.ns2.hi').should == 'bar'
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#jimson_methods' do
56
+ it 'returns an array of namespaced method names from all registered handlers' do
57
+ router.draw do
58
+ root RouterFooHandler.new
59
+ namespace 'foo', RouterBarHandler.new
60
+ end
61
+
62
+ router.jimson_methods.should == ['hi', 'foo.bye']
63
+ end
64
+ end
65
+
66
+ end
67
+ end
data/spec/server_spec.rb CHANGED
@@ -31,6 +31,18 @@ module Jimson
31
31
  def get_data
32
32
  ['hello', 5]
33
33
  end
34
+
35
+ def ugly_method
36
+ raise RuntimeError
37
+ end
38
+ end
39
+
40
+ class OtherHandler
41
+ extend Jimson::Handler
42
+
43
+ def multiply(a,b)
44
+ a * b
45
+ end
34
46
  end
35
47
 
36
48
  INVALID_RESPONSE_EXPECTATION = {
@@ -41,8 +53,15 @@ module Jimson
41
53
  },
42
54
  'id' => nil
43
55
  }
44
- def app
45
- Server.new(TestHandler.new, :environment => "production")
56
+ let(:router) do
57
+ router = Router.new.draw do
58
+ root TestHandler.new
59
+ namespace 'other', OtherHandler.new
60
+ end
61
+ end
62
+
63
+ let(:app) do
64
+ Server.new(router, :environment => "production")
46
65
  end
47
66
 
48
67
  def post_json(hash)
@@ -197,6 +216,27 @@ module Jimson
197
216
  end
198
217
  end
199
218
 
219
+ describe "receiving a call for ugly method" do
220
+ it "returns only global error without stack trace" do
221
+ req = {
222
+ 'jsonrpc' => '2.0',
223
+ 'method' => 'ugly_method',
224
+ 'id' => 1
225
+ }
226
+ post_json(req)
227
+
228
+ resp = MultiJson.decode(last_response.body)
229
+ resp.should == {
230
+ 'jsonrpc' => '2.0',
231
+ 'error' => {
232
+ 'code' => -32099,
233
+ 'message' => 'Server application error'
234
+ },
235
+ 'id' => 1
236
+ }
237
+ end
238
+ end
239
+
200
240
  describe "receiving invalid JSON" do
201
241
  it "returns an error response" do
202
242
  req = MultiJson.encode({
@@ -314,8 +354,8 @@ module Jimson
314
354
  }
315
355
  end
316
356
  end
317
- context "when the request is 'listMethods'" do
318
- it "returns response with all listMethods on the handler as strings" do
357
+ context "when the request is 'system.listMethods'" do
358
+ it "returns response with all jimson_exposed_methods on the handler(s) as strings" do
319
359
  req = {
320
360
  'jsonrpc' => '2.0',
321
361
  'method' => 'system.listMethods',
@@ -326,11 +366,10 @@ module Jimson
326
366
 
327
367
  last_response.should be_ok
328
368
  resp = MultiJson.decode(last_response.body)
329
- resp.should == {
330
- 'jsonrpc' => '2.0',
331
- 'result' => ['subtract', 'sum', 'notify_hello', 'update', 'get_data'].sort,
332
- 'id' => 1
333
- }
369
+ resp['jsonrpc'].should == '2.0'
370
+ resp['id'].should == 1
371
+ expected = ['get_data', 'notify_hello', 'subtract', 'sum', 'ugly_method', 'update', 'system.isAlive', 'system.listMethods', 'other.multiply']
372
+ (resp['result'] - expected).should == []
334
373
  end
335
374
  end
336
375
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'rubygems'
2
2
  $:.unshift(File.dirname(__FILE__) + '/../lib/')
3
- require 'jimson/server'
4
- require 'jimson/client'
3
+ require 'jimson'
5
4
  require 'bundler/setup'
6
5
  require 'multi_json'
7
6
 
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.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-14 00:00:00.000000000Z
12
+ date: 2012-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: blankslate
16
- requirement: &18659080 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 2.1.2.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *18659080
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.1.2.3
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rest-client
27
- requirement: &18658140 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 1.6.3
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *18658140
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.3
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: multi_json
38
- requirement: &18657580 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: 1.1.0
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *18657580
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rack
49
- requirement: &18656860 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,7 +69,12 @@ dependencies:
54
69
  version: '1.3'
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *18656860
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
58
78
  description:
59
79
  email:
60
80
  executables: []
@@ -68,7 +88,9 @@ files:
68
88
  - README.md
69
89
  - Rakefile
70
90
  - lib/jimson/request.rb
91
+ - lib/jimson/router.rb
71
92
  - lib/jimson/response.rb
93
+ - lib/jimson/router/map.rb
72
94
  - lib/jimson/server.rb
73
95
  - lib/jimson/client/error.rb
74
96
  - lib/jimson/client.rb
@@ -76,6 +98,7 @@ files:
76
98
  - lib/jimson/server/error.rb
77
99
  - lib/jimson.rb
78
100
  - spec/spec_helper.rb
101
+ - spec/router_spec.rb
79
102
  - spec/client_spec.rb
80
103
  - spec/handler_spec.rb
81
104
  - spec/server_spec.rb
@@ -99,12 +122,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
122
  version: '0'
100
123
  requirements: []
101
124
  rubyforge_project:
102
- rubygems_version: 1.8.15
125
+ rubygems_version: 1.8.21
103
126
  signing_key:
104
127
  specification_version: 3
105
128
  summary: JSON-RPC 2.0 client and server
106
129
  test_files:
107
130
  - spec/spec_helper.rb
131
+ - spec/router_spec.rb
108
132
  - spec/client_spec.rb
109
133
  - spec/handler_spec.rb
110
134
  - spec/server_spec.rb