rack-bert-rpc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Crystal Commerce
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ LICENSE
2
+ README.md
3
+ Manifest.txt
4
+ lib/rack/bert_rpc.rb
5
+ lib/rack/bert_rpc/encoding.rb
6
+ lib/rack/bert_rpc/mod.rb
7
+ lib/rack/bert_rpc/server_error.rb
8
+ lib/rack/bert_rpc/server.rb
9
+ lib/rack/bert_rpc/version.rb
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ rack-bert-rpc
2
+ =============
3
+
4
+ rack-bert-rpc a Rack middleware BERT-RPC Server
5
+ implementation. HTTP requests matching the specified path are
6
+ intercepted by the middleware and interpreted as BERT-RPC requests.
7
+
8
+ For more information about the BERT-RPC specification go to
9
+ [bert-rpc.org](http://bert-rpc.org).
10
+
11
+ rack-bert-rpc varies from other BERT-RPC server implementations in
12
+ that it requires the requests to be made over HTTP. This increases the
13
+ complexity and size of the requests, but allows for them to be handled
14
+ by the same server that is serving a web application. For this reason
15
+ services exposed through rack-bert-rpc cannot be called using
16
+ traditional BERT-RPC libraries such as
17
+ [bertrpc](http://github.com/mojombo/bertrpc)
18
+
19
+ Currently rack-bert-rpc only supports the following BERT-RPC features:
20
+
21
+ * `call` requests
22
+ * `cast` requests
23
+
24
+ The design and implementation of rack-bert-rpc derives heavily from
25
+ [ernie](http://github.com/mojombo/ernie)
26
+
27
+ Installation
28
+ ------------
29
+
30
+ $ [sudo] gem install rack-bert-rpc
31
+
32
+ Usage
33
+ -----
34
+
35
+ Install rack-bert-rpc as part of your application's middleware stack.
36
+
37
+ use Rack::BertRpc, :expose => {
38
+ :mod => Mod
39
+ }
40
+
41
+ The `BertRpc` middle takes several configuration parameters:
42
+
43
+ * `expose`
44
+ Takes a `Hash` of module name to actual module
45
+ * `path`
46
+ Any requests coming in to this path will be handled as RPC
47
+ requests. Defaults to '/rpc'
48
+ * `server`
49
+ Used for testing, switches out the backend `BertRpc::Server` so the
50
+ requests can be handled by a different one
51
+
52
+ Example
53
+ -------
54
+
55
+ First define a module containing the function that you want to call:
56
+
57
+ module HelloWorld
58
+ def say_hello(name)
59
+ "Hello, #{name}!"
60
+ end
61
+ end
62
+
63
+ Next in your application's `config.ru` install rack-bert-rpc and
64
+ expose the `HelloWorld` module
65
+
66
+ require 'rack/bert_rpc'
67
+ require 'hello_world'
68
+
69
+ use Rack::BertRpc, :expose => {
70
+ :hello => HelloWorld
71
+ }
72
+ run lambda{ |env| [200, {}, "success"] }
73
+
74
+ Since we are calling BERT-RPC over HTTP we can't use the default
75
+ `bertrpc` gem. Instead here's a sample script to send a BERT-RPC
76
+ request over HTTP to test our module
77
+
78
+ require 'bert'
79
+ require 'net/http'
80
+
81
+ def encode(msg)
82
+ m = BERT.encode(msg)
83
+ [m.length].pack('N') + m
84
+ end
85
+
86
+ def decode(msg)
87
+ io = StringIO.new(body)
88
+ length = io.read(4).unpack('N').first
89
+ BERT.decode(io.read(length))
90
+ end
91
+
92
+ req = Net::HTTP::Post.new('/rpc')
93
+ req.body = encode(t[:call, :hello, :say_hello, ["Ryan"]])
94
+ resp = nil
95
+ Net::HTTP.start('localhost', 3001){ |http| resp = http.request(req) }
96
+
97
+ puts decode(resp.body)[1]
98
+
99
+ Next start up your rack server on the appropriate port
100
+
101
+ $ rackup -p 3001 config.ru
102
+
103
+ And finally run the script to make the BERT-RPC request
104
+
105
+ $ ruby -rubygems call_bert.rb
106
+ Hello, Ryan!
107
+
108
+ Contribute
109
+ ---------
110
+
111
+ If you'd like to contribue to rack-bert-rpc fork the project on
112
+ GitHub:
113
+
114
+ http://github.com/crystalcommerce/rack-bert-rpc
115
+
116
+ When your changes are ready, push the code to GitHub and send us a
117
+ pull request.
@@ -0,0 +1,48 @@
1
+ # We prefer to not define a global 't' method so we will use Crystal Commerce's
2
+ # fork of Bert if it is available.
3
+ # http://github.com/crystalcommerce/bert
4
+ begin
5
+ require 'bert/core'
6
+ rescue LoadError
7
+ require 'bert'
8
+ end
9
+
10
+ module Rack
11
+ class BertRpc
12
+ module Encoding
13
+ def read_rpc(input)
14
+ raw = input.read(4)
15
+ return nil unless raw
16
+ length = raw.unpack('N').first
17
+ return nil unless length
18
+ bert = input.read(length)
19
+ BERT.decode(bert)
20
+ end
21
+
22
+ def error_response(type, error)
23
+ if error.is_a? String
24
+ berpify(BERT.encode(BERT::Tuple[:error,
25
+ BERT::Tuple[type, 0, error]]))
26
+ else
27
+ berpify(BERT.encode(BERT::Tuple[:error,
28
+ BERT::Tuple[type, 0, error.class.to_s,
29
+ error.message, error.backtrace]]))
30
+ end
31
+ end
32
+
33
+ def reply_response(response)
34
+ berpify(BERT.encode(BERT::Tuple[:reply, response]))
35
+ end
36
+
37
+ def noreply_response
38
+ berpify(BERT.encode(BERT::Tuple[:noreply]))
39
+ end
40
+
41
+ private
42
+
43
+ def berpify(msg)
44
+ [msg.length].pack("N") + msg
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module Rack
2
+ class BertRpc
3
+ class Mod
4
+ attr_reader :source_module, :funs
5
+ attr_writer :loaded
6
+
7
+ def initialize(source_module)
8
+ @source_module = source_module
9
+ @loaded = false
10
+ @funs = {}
11
+ end
12
+
13
+ def call_fun(fun, args)
14
+ unless loaded?
15
+ load_module
16
+ end
17
+
18
+ if funs[fun].nil?
19
+ raise(ServerError.new("No such function " +
20
+ "'#{source_module.name}##{fun}'"))
21
+ end
22
+
23
+ funs[fun].call(*args)
24
+ end
25
+
26
+ def loaded?
27
+ @loaded
28
+ end
29
+
30
+ def load_module
31
+ @context = Object.new
32
+ @context.extend source_module
33
+ source_module.public_instance_methods.each do |meth|
34
+ funs[meth.to_sym] = @context.method(meth)
35
+ end
36
+ self.loaded = true
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,53 @@
1
+ require 'rack/bert_rpc/mod'
2
+ require 'rack/bert_rpc/encoding'
3
+ require 'rack/bert_rpc/server_error'
4
+
5
+ module Rack
6
+ class BertRpc
7
+ class Server
8
+ include Encoding
9
+
10
+ attr_reader :mods
11
+
12
+ def initialize
13
+ @mods = {}
14
+ end
15
+
16
+ def expose(sym, mod)
17
+ mods[sym] = Mod.new(mod)
18
+ end
19
+
20
+ def handle(input)
21
+ rpc = read_rpc(input)
22
+
23
+ if rpc.size == 4 && rpc[0] == :call
24
+ begin
25
+ resp = dispatch(*rpc[1..3])
26
+ reply_response(resp)
27
+ rescue ServerError => e
28
+ error_response(:server, e)
29
+ rescue Object => e
30
+ error_response(:user, e)
31
+ end
32
+ elsif rpc.size == 4 && rpc[0] == :cast
33
+ begin
34
+ dispatch(*rpc[1..3])
35
+ rescue Object => e
36
+ # Just ignore errors here
37
+ end
38
+ noreply_response
39
+ else
40
+ error_response(:server, "Invalid request: #{rpc.inspect}")
41
+ end
42
+ end
43
+
44
+ def dispatch(mod, fun, args)
45
+ if mods[mod].nil?
46
+ raise(ServerError.new("No such module '#{mod}'"))
47
+ end
48
+
49
+ mods[mod].call_fun(fun, args)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module Rack
2
+ class BertRpc
3
+ class ServerError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ class BertRpc
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ require 'rack'
2
+
3
+ module Rack
4
+ class BertRpc
5
+ attr_reader :path
6
+
7
+ def initialize(app, options = {})
8
+ @path = options[:path] || '/rpc'
9
+ @app = app
10
+ @server = options[:server] || Server.new
11
+ options[:expose].each do |sym, mod|
12
+ @server.expose(sym, mod)
13
+ end unless options[:expose].nil?
14
+ end
15
+
16
+ def call(env)
17
+ if path == env["PATH_INFO"]
18
+ response = @server.handle(env["rack.input"])
19
+ [200, { "Content-Type" => "application/bert" }, response]
20
+ else
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ require 'rack/bert_rpc/server'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-bert-rpc
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ryan Burrows
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-12 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bert
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: rack
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :runtime
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: rspec
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :development
55
+ version_requirements: *id003
56
+ - !ruby/object:Gem::Dependency
57
+ name: rack-test
58
+ prerelease: false
59
+ requirement: &id004 !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ type: :development
67
+ version_requirements: *id004
68
+ description: rack-bert-rpc is rack middleware that provides a BERT-RPC server implemenation for using BERT-RPC over HTTP
69
+ email: rhburrows@gmail.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - Manifest.txt
80
+ - lib/rack/bert_rpc.rb
81
+ - lib/rack/bert_rpc/encoding.rb
82
+ - lib/rack/bert_rpc/mod.rb
83
+ - lib/rack/bert_rpc/server_error.rb
84
+ - lib/rack/bert_rpc/server.rb
85
+ - lib/rack/bert_rpc/version.rb
86
+ has_rdoc: true
87
+ homepage: http://github.com/crystalcommerce/rack-bert-rpc
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options: []
92
+
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ segments:
107
+ - 1
108
+ - 3
109
+ - 6
110
+ version: 1.3.6
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.3.6
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Rack middleware BERT-RPC server
118
+ test_files: []
119
+