rack-bert-rpc 0.1.0

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/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
+