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 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: []