rack-rpc 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1 +1,2 @@
1
1
  * Arto Bendiken <arto.bendiken@gmail.com>
2
+ * Josh Huckabee <joshhuckabee@gmail.com>
data/README CHANGED
@@ -1,4 +1,148 @@
1
1
  JSON-RPC/XML-RPC Server for Rack Applications
2
2
  =============================================
3
3
 
4
- * <http://github.com/datagraph/rack-rpc>
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.2
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
@@ -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
@@ -2,7 +2,7 @@ module Rack; module RPC
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 2
5
+ TINY = 3
6
6
  EXTRA = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
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: 2010-10-30 00:00:00 +02:00
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.3.0
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: []