Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 6 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
View
7 Rakefile
@@ -14,7 +14,12 @@ end
task :default => :rspec
-require 'rake/rdoctask'
+begin
+ require 'rdoc/task'
+rescue LoadError
+ require 'rake/rdoctask'
+end
+
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
View
1  lib/jimson.rb
@@ -1,5 +1,6 @@
require 'rubygems'
require 'jimson/handler'
+require 'jimson/router'
require 'jimson/server'
require 'jimson/client'
View
24 lib/jimson/router.rb
@@ -0,0 +1,24 @@
+require 'jimson/router/map'
+require 'forwardable'
+
+module Jimson
+ class Router
+ extend Forwardable
+
+ def_delegators :@map, :handler_for_method,
+ :root,
+ :namespace,
+ :jimson_methods,
+ :strip_method_namespace
+
+ def initialize
+ @map = Map.new
+ end
+
+ def draw(&block)
+ @map.instance_eval &block
+ self
+ end
+
+ end
+end
View
73 lib/jimson/router/map.rb
@@ -0,0 +1,73 @@
+module Jimson
+ class Router
+
+ #
+ # Provides a DSL for routing method namespaces to handlers.
+ # Only handles root-level and non-nested namespaces, e.g. 'foo.bar' or 'foo'.
+ #
+ class Map
+
+ def initialize
+ @routes = {}
+ end
+
+ #
+ # Set the root handler, i.e. the handler used for a bare method like 'foo'
+ #
+ def root(handler)
+ @routes[''] = handler
+ end
+
+ #
+ # Define the handler for a namespace
+ #
+ def namespace(ns, handler = nil, &block)
+ if !!handler
+ @routes[ns.to_s] = handler
+ else
+ # passed a block for nested namespacing
+ map = Jimson::Router::Map.new
+ @routes[ns.to_s] = map
+ map.instance_eval &block
+ end
+ end
+
+ #
+ # Return the handler for a (possibly namespaced) method name
+ #
+ def handler_for_method(method)
+ parts = method.split('.')
+ ns = (method.index('.') == nil ? '' : parts.first)
+ handler = @routes[ns]
+ if handler.is_a?(Jimson::Router::Map)
+ return handler.handler_for_method(parts[1..-1].join('.'))
+ end
+ handler
+ end
+
+ #
+ # Strip off the namespace part of a method and return the bare method name
+ #
+ def strip_method_namespace(method)
+ method.split('.').last
+ end
+
+ #
+ # Return an array of all methods on handlers in the map, fully namespaced
+ #
+ def jimson_methods
+ arr = @routes.keys.map do |ns|
+ prefix = (ns == '' ? '' : "#{ns}.")
+ handler = @routes[ns]
+ if handler.is_a?(Jimson::Router::Map)
+ handler.jimson_methods
+ else
+ handler.class.jimson_exposed_methods.map { |method| prefix + method }
+ end
+ end
+ arr.flatten
+ end
+
+ end
+ end
+end
View
50 lib/jimson/server.rb
@@ -3,6 +3,7 @@
require 'rack/response'
require 'multi_json'
require 'jimson/handler'
+require 'jimson/router'
require 'jimson/server/error'
module Jimson
@@ -11,12 +12,12 @@ class Server
class System
extend Handler
- def initialize(handler)
- @handler = handler
+ def initialize(router)
+ @router = router
end
def listMethods
- @handler.class.jimson_exposed_methods
+ @router.jimson_methods
end
def isAlive
@@ -26,10 +27,10 @@ def isAlive
JSON_RPC_VERSION = '2.0'
- attr_accessor :handler, :host, :port, :opts
+ attr_accessor :router, :host, :port, :opts
#
- # +handler+ is an instance of the class to expose as a JSON-RPC server
+ # +router_or_handler+ is an instance of Jimson::Router or extends Jimson::Handler
#
# +opts+ may include:
# * :host - the hostname or ip to bind to
@@ -38,8 +39,17 @@ def isAlive
#
# Remaining options are forwarded to the underlying Rack server.
#
- def initialize(handler, opts = {})
- @handler = handler
+ def initialize(router_or_handler, opts = {})
+ if !router_or_handler.is_a?(Router)
+ # arg is a handler, wrap it in a Router
+ @router = Router.new
+ @router.root router_or_handler
+ else
+ # arg is a router
+ @router = router_or_handler
+ end
+ @router.namespace 'system', System.new(@router)
+
@host = opts.delete(:host) || '0.0.0.0'
@port = opts.delete(:port) || 8999
@opts = opts
@@ -153,30 +163,22 @@ def create_response(request)
end
def dispatch_request(method, params)
- # normally route requests to the user-suplied handler
- handler = @handler
-
- # switch to the System handler if a system method was called
- sys_regex = /^system\./
- if method =~ sys_regex
- handler = System.new(@handler)
- # remove the 'system.' prefix before from the method name
- method.gsub!(sys_regex, '')
- end
-
- method = method.to_s
+ method_name = method.to_s
+ handler = @router.handler_for_method(method_name)
+ method_name = @router.strip_method_namespace(method_name)
- if !handler.class.jimson_exposed_methods.include?(method) \
- || !handler.respond_to?(method)
+ if handler.nil? \
+ || !handler.class.jimson_exposed_methods.include?(method_name) \
+ || !handler.respond_to?(method_name)
raise Server::Error::MethodNotFound.new(method)
end
if params.nil?
- return handler.send(method)
+ return handler.send(method_name)
elsif params.is_a?(Hash)
- return handler.send(method, params)
+ return handler.send(method_name, params)
else
- return handler.send(method, *params)
+ return handler.send(method_name, *params)
end
end
View
67 spec/router_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+module Jimson
+ describe Router do
+
+ let(:router) { Router.new }
+
+ class RouterFooHandler
+ extend Jimson::Handler
+
+ def hi
+ 'hi'
+ end
+ end
+
+ class RouterBarHandler
+ extend Jimson::Handler
+
+ def bye
+ 'bye'
+ end
+ end
+
+
+ describe '#draw' do
+ context 'when given non-nested namespaces' do
+ it 'takes a block with a DSL to set the root and namespaces' do
+ router.draw do
+ root 'foo'
+ namespace 'ns', 'bar'
+ end
+
+ router.handler_for_method('hi').should == 'foo'
+ router.handler_for_method('ns.hi').should == 'bar'
+ end
+ end
+
+ context 'when given nested namespaces' do
+ it 'takes a block with a DSL to set the root and namespaces' do
+ router.draw do
+ root 'foo'
+ namespace 'ns1' do
+ root 'blah'
+ namespace 'ns2', 'bar'
+ end
+ end
+
+ router.handler_for_method('hi').should == 'foo'
+ router.handler_for_method('ns1.hi').should == 'blah'
+ router.handler_for_method('ns1.ns2.hi').should == 'bar'
+ end
+ end
+ end
+
+ describe '#jimson_methods' do
+ it 'returns an array of namespaced method names from all registered handlers' do
+ router.draw do
+ root RouterFooHandler.new
+ namespace 'foo', RouterBarHandler.new
+ end
+
+ router.jimson_methods.should == ['hi', 'foo.bye']
+ end
+ end
+
+ end
+end
View
32 spec/server_spec.rb
@@ -37,6 +37,14 @@ def ugly_method
end
end
+ class OtherHandler
+ extend Jimson::Handler
+
+ def multiply(a,b)
+ a * b
+ end
+ end
+
INVALID_RESPONSE_EXPECTATION = {
'jsonrpc' => '2.0',
'error' => {
@@ -45,8 +53,15 @@ def ugly_method
},
'id' => nil
}
- def app
- Server.new(TestHandler.new, :environment => "production")
+ let(:router) do
+ router = Router.new.draw do
+ root TestHandler.new
+ namespace 'other', OtherHandler.new
+ end
+ end
+
+ let(:app) do
+ Server.new(router, :environment => "production")
end
def post_json(hash)
@@ -339,8 +354,8 @@ def post_json(hash)
}
end
end
- context "when the request is 'listMethods'" do
- it "returns response with all listMethods on the handler as strings" do
+ context "when the request is 'system.listMethods'" do
+ it "returns response with all jimson_exposed_methods on the handler(s) as strings" do
req = {
'jsonrpc' => '2.0',
'method' => 'system.listMethods',
@@ -351,11 +366,10 @@ def post_json(hash)
last_response.should be_ok
resp = MultiJson.decode(last_response.body)
- resp.should == {
- 'jsonrpc' => '2.0',
- 'result' => ['subtract', 'sum', 'notify_hello', 'update', 'get_data', 'ugly_method'].sort,
- 'id' => 1
- }
+ resp['jsonrpc'].should == '2.0'
+ resp['id'].should == 1
+ expected = ['get_data', 'notify_hello', 'subtract', 'sum', 'ugly_method', 'update', 'system.isAlive', 'system.listMethods', 'other.multiply']
+ (resp['result'] - expected).should == []
end
end
end
View
3  spec/spec_helper.rb
@@ -1,7 +1,6 @@
require 'rubygems'
$:.unshift(File.dirname(__FILE__) + '/../lib/')
-require 'jimson/server'
-require 'jimson/client'
+require 'jimson'
require 'bundler/setup'
require 'multi_json'

No commit comments for this range

Something went wrong with that request. Please try again.