rack-rpc 0.0.2 → 0.0.3
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/AUTHORS +1 -0
- data/README +145 -1
- data/VERSION +1 -1
- data/lib/rack/rpc/endpoint/xmlrpc.rb +44 -0
- data/lib/rack/rpc/server.rb +76 -0
- data/lib/rack/rpc/version.rb +1 -1
- metadata +19 -5
data/AUTHORS
CHANGED
data/README
CHANGED
@@ -1,4 +1,148 @@
|
|
1
1
|
JSON-RPC/XML-RPC Server for Rack Applications
|
2
2
|
=============================================
|
3
3
|
|
4
|
-
|
4
|
+
This is a [Rack][] middleware that facilitates the creation of
|
5
|
+
protocol-agnostic RPC servers. The current implementation provides support
|
6
|
+
for [JSON-RPC 2.0][] and [XML-RPC][].
|
7
|
+
|
8
|
+
* <https://github.com/datagraph/rack-rpc>
|
9
|
+
|
10
|
+
Features
|
11
|
+
--------
|
12
|
+
|
13
|
+
* Handles JSON-RPC and XML-RPC requests with the same code.
|
14
|
+
* Compatible with any Rack application and any Rack-based framework.
|
15
|
+
* Provides Rails-style controller filtering for your RPC methods.
|
16
|
+
|
17
|
+
Examples
|
18
|
+
--------
|
19
|
+
|
20
|
+
### A basic RPC server
|
21
|
+
|
22
|
+
require 'rack/rpc'
|
23
|
+
|
24
|
+
class Server < Rack::RPC::Server
|
25
|
+
def hello_world
|
26
|
+
"Hello, world!"
|
27
|
+
end
|
28
|
+
rpc 'hello_world' => :hello_world
|
29
|
+
end
|
30
|
+
|
31
|
+
### Simple filtering
|
32
|
+
|
33
|
+
require 'rack/rpc'
|
34
|
+
|
35
|
+
class Server < Rack::RPC::Server
|
36
|
+
before_filter :check_auth
|
37
|
+
|
38
|
+
def hello_world
|
39
|
+
"Hello, world!"
|
40
|
+
end
|
41
|
+
rpc 'hello_world' => :hello_world
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def check_auth
|
46
|
+
raise "Not authorized" unless authorized
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
### Filtering via a proc with more options
|
51
|
+
|
52
|
+
require 'rack/rpc'
|
53
|
+
|
54
|
+
class Server < Rack::RPC::Server
|
55
|
+
before_filter :check_auth, :only => :super_secret_hello_world do
|
56
|
+
raise "Not authorized" unless authorized
|
57
|
+
end
|
58
|
+
|
59
|
+
def hello_world
|
60
|
+
"Hello, world!"
|
61
|
+
end
|
62
|
+
rpc 'hello_world' => :hello_world
|
63
|
+
|
64
|
+
def super_secret_hello_world
|
65
|
+
'super_secret_hello_world'
|
66
|
+
end
|
67
|
+
rpc 'super_secret_hello_world' => :super_secret_hello_world
|
68
|
+
end
|
69
|
+
|
70
|
+
### Running the server
|
71
|
+
|
72
|
+
# config.ru
|
73
|
+
use Rack::RPC::Endpoint, Server.new
|
74
|
+
|
75
|
+
run MyApplication
|
76
|
+
|
77
|
+
### Customizing the default RPC path
|
78
|
+
|
79
|
+
# config.ru
|
80
|
+
use Rack::RPC::Endpoint, Server.new, :path => '/api'
|
81
|
+
|
82
|
+
run MyApplication
|
83
|
+
|
84
|
+
More on Filters
|
85
|
+
---------------
|
86
|
+
|
87
|
+
The `:only` and `:except` options for filters can take a single method or an
|
88
|
+
array of methods.
|
89
|
+
|
90
|
+
You can halt execution in a filter by raising an exception. An error
|
91
|
+
response will be returned with the exception's message set as the error
|
92
|
+
object's message text.
|
93
|
+
|
94
|
+
Communicationg with the Server
|
95
|
+
------------------------------
|
96
|
+
|
97
|
+
By default, methods will only be invoked on `POST` requests to "/rpc". The
|
98
|
+
default path can be overridden by sending a `:path` option when creating
|
99
|
+
your middleware (see example above).
|
100
|
+
|
101
|
+
The protocol used is determined by the `CONTENT_TYPE` header
|
102
|
+
("application/xml" and "text/xml" for XML and "application/json" for JSON).
|
103
|
+
|
104
|
+
Dependencies
|
105
|
+
------------
|
106
|
+
|
107
|
+
* [Rack](http://rubygems.org/gems/rack) (>= 1.0.0)
|
108
|
+
|
109
|
+
Installation
|
110
|
+
------------
|
111
|
+
|
112
|
+
The recommended installation method is via [RubyGems](http://rubygems.org/).
|
113
|
+
To install the latest official release of the gem, do:
|
114
|
+
|
115
|
+
% [sudo] gem install rack-rpc
|
116
|
+
|
117
|
+
Download
|
118
|
+
--------
|
119
|
+
|
120
|
+
To get a local working copy of the development repository, do:
|
121
|
+
|
122
|
+
% git clone git://github.com/datagraph/rack-rpc.git
|
123
|
+
|
124
|
+
Alternatively, download the latest development version as a tarball as
|
125
|
+
follows:
|
126
|
+
|
127
|
+
% wget http://github.com/datagraph/rack-rpc/tarball/master
|
128
|
+
|
129
|
+
Mailing List
|
130
|
+
------------
|
131
|
+
|
132
|
+
* <http://groups.google.com/group/rack-devel>
|
133
|
+
|
134
|
+
Authors
|
135
|
+
-------
|
136
|
+
|
137
|
+
* [Arto Bendiken](https://github.com/bendiken) - <http://ar.to/>
|
138
|
+
* [Josh Huckabee](https://github.com/jhuckabee) - <http://joshhuckabee.com/>
|
139
|
+
|
140
|
+
License
|
141
|
+
-------
|
142
|
+
|
143
|
+
This is free and unencumbered public domain software. For more information,
|
144
|
+
see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
|
145
|
+
|
146
|
+
[Rack]: http://rack.rubyforge.org/
|
147
|
+
[JSON-RPC 2.0]: http://groups.google.com/group/json-rpc/web/json-rpc-2-0
|
148
|
+
[XML-RPC]: http://www.xmlrpc.com/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
@@ -38,6 +38,18 @@ class Rack::RPC::Endpoint
|
|
38
38
|
})
|
39
39
|
end
|
40
40
|
|
41
|
+
##
|
42
|
+
# Process requests and ensure errors are handled properly
|
43
|
+
#
|
44
|
+
# @param [String] request body
|
45
|
+
def process(request_body)
|
46
|
+
begin
|
47
|
+
super(request_body)
|
48
|
+
rescue RuntimeError => e
|
49
|
+
error_response(-32500, "application error - #{e.message}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
41
53
|
##
|
42
54
|
# Implements the `system.getCapabilities` standard method, enabling
|
43
55
|
# clients to determine whether a given capability is supported by this
|
@@ -62,6 +74,38 @@ class Rack::RPC::Endpoint
|
|
62
74
|
end
|
63
75
|
self
|
64
76
|
end
|
77
|
+
|
78
|
+
|
79
|
+
##
|
80
|
+
# Create a valid error response for a given code and message
|
81
|
+
#
|
82
|
+
# @param [Int] error code
|
83
|
+
# @param [String] error message
|
84
|
+
# @return [String] response xml string
|
85
|
+
def error_response(code, message)
|
86
|
+
xml = Builder::XmlMarkup.new
|
87
|
+
xml.instruct! :xml, :version=>"1.0"
|
88
|
+
xml.methodResponse{
|
89
|
+
xml.fault {
|
90
|
+
xml.value{
|
91
|
+
xml.struct{
|
92
|
+
xml.member{
|
93
|
+
xml.name('faultCode')
|
94
|
+
xml.value{
|
95
|
+
xml.int(code)
|
96
|
+
}
|
97
|
+
}
|
98
|
+
xml.member{
|
99
|
+
xml.name('faultString')
|
100
|
+
xml.value{
|
101
|
+
xml.string(message)
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
end
|
65
109
|
end # Server
|
66
110
|
end # XMLRPC
|
67
111
|
end # Rack::RPC::Endpoint
|
data/lib/rack/rpc/server.rb
CHANGED
@@ -9,10 +9,34 @@ module Rack; module RPC
|
|
9
9
|
if mappings.empty?
|
10
10
|
@mappings
|
11
11
|
else
|
12
|
+
# Store the mappings
|
12
13
|
@mappings.merge!(mappings)
|
14
|
+
|
15
|
+
# Wrap each method so we can inject before and after callbacks
|
16
|
+
mappings.each do |rpc_method_name, server_method|
|
17
|
+
self.send(:alias_method, :"#{server_method}_without_callbacks", server_method.to_sym)
|
18
|
+
self.send(:define_method, server_method) do
|
19
|
+
self.class.hooks[:before].each{|command| command.call(self) if command.callable?(server_method)}
|
20
|
+
out = self.send(:"#{server_method}_without_callbacks")
|
21
|
+
self.class.hooks[:after].each{|command| command.call(self) if command.callable?(server_method)}
|
22
|
+
out
|
23
|
+
end
|
24
|
+
end
|
13
25
|
end
|
14
26
|
end
|
15
27
|
|
28
|
+
def self.hooks
|
29
|
+
@hooks ||= {:before => [], :after => []}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.before_filter(method_sym = nil, options = {}, &block)
|
33
|
+
setup_hook(:before, method_sym, options, block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.after_filter(method_sym = nil, options = {}, &block)
|
37
|
+
setup_hook(:after, method_sym, options, block)
|
38
|
+
end
|
39
|
+
|
16
40
|
# @return [Hash]
|
17
41
|
attr_reader :options
|
18
42
|
|
@@ -22,5 +46,57 @@ module Rack; module RPC
|
|
22
46
|
@options = options.dup
|
23
47
|
block.call(self) if block_given?
|
24
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def self.setup_hook(type, method, options, proc)
|
53
|
+
hooks[type] << if proc
|
54
|
+
ProcCommand.new(proc, options)
|
55
|
+
else
|
56
|
+
MethodCommand.new(method, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
25
60
|
end # Server
|
61
|
+
|
62
|
+
class Command
|
63
|
+
attr_reader :options
|
64
|
+
|
65
|
+
def initialize(options)
|
66
|
+
@options = options
|
67
|
+
|
68
|
+
# Convert non-array options to arrays
|
69
|
+
[:only, :except].each do |option|
|
70
|
+
options[option] = [options[option]] if !options[option].nil? && !options[option].is_a?(Array)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def callable?(method)
|
75
|
+
options.empty? ||
|
76
|
+
(!options[:only].nil? && options[:only].include?(method)) ||
|
77
|
+
(!options[:except].nil? && !options[:except].include?(method))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class ProcCommand < Command
|
82
|
+
def initialize(proc, options)
|
83
|
+
@proc = proc.to_proc
|
84
|
+
super(options)
|
85
|
+
end
|
86
|
+
|
87
|
+
def call(server)
|
88
|
+
server.instance_eval(&@proc)
|
89
|
+
end
|
90
|
+
end # ProcCommand
|
91
|
+
|
92
|
+
class MethodCommand < Command
|
93
|
+
def initialize(method, options)
|
94
|
+
@method = method.to_sym
|
95
|
+
super(options)
|
96
|
+
end
|
97
|
+
|
98
|
+
def call(server)
|
99
|
+
server.__send__(@method)
|
100
|
+
end
|
101
|
+
end # MethodCommand
|
26
102
|
end; end # Rack::RPC
|
data/lib/rack/rpc/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Datagraph
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-13 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -52,10 +52,10 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
segments:
|
55
|
+
- 2
|
55
56
|
- 1
|
56
|
-
- 3
|
57
57
|
- 0
|
58
|
-
version: 1.
|
58
|
+
version: 2.1.0
|
59
59
|
type: :development
|
60
60
|
version_requirements: *id003
|
61
61
|
- !ruby/object:Gem::Dependency
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
version: 0.5.6
|
73
73
|
type: :development
|
74
74
|
version_requirements: *id004
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: nokogiri
|
77
|
+
prerelease: false
|
78
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 1
|
84
|
+
- 4
|
85
|
+
- 4
|
86
|
+
version: 1.4.4
|
87
|
+
type: :development
|
88
|
+
version_requirements: *id005
|
75
89
|
description: Rack middleware for serving up RPC endpoints.
|
76
90
|
email: datagraph@googlegroups.com
|
77
91
|
executables: []
|