zmqjsonrpc 0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 275b2e7bd02313197bd72c9791bfb20774a8f0c7
4
+ data.tar.gz: 32c9c8d5bba102217c2f19472575e8d893821de2
5
+ SHA512:
6
+ metadata.gz: 3d636755631636c90c151348753be54440f0b2ff212a1a712f111514bcf2a1a615c48e81a366ad7d08a9a5c4c3ffc496e038bda0a7903cbd03215c81650af5bf
7
+ data.tar.gz: b6c77e276528cf85b0040aa7e71ef0fceb3105bd5caadf8511b573aad9e90923c4905df61cc56d1b72858d411f84a6f37649756e71676d0f17904405215b3c3d
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ .vagrant
3
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simplejsonrpc.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,23 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simplejsonrpc (0.1)
5
+ ffi-rzmq (~> 2.0.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ffi (1.9.6)
11
+ ffi-rzmq (2.0.4)
12
+ ffi-rzmq-core (>= 1.0.1)
13
+ ffi-rzmq-core (1.0.3)
14
+ ffi (~> 1.9)
15
+ rake (10.4.2)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ bundler (~> 1.3)
22
+ rake
23
+ simplejsonrpc!
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # zmqjsonrpc
2
+
3
+ This gem implements a very simple [JSON RPC 2.0](http://www.jsonrpc.org/specification) client and server which uses zeroMQ for transport.
4
+ Let's not talk to much, let's see some code:
5
+
6
+ ```ruby
7
+ require 'rubygems'
8
+ require 'zmqjsonrpc'
9
+
10
+ # client request to a running server
11
+ client = ZmqJsonRpc::Client.new("tcp://127.0.0.1:49200")
12
+ client.some_method(1, "b", [1,{a:1}])
13
+ ```
14
+
15
+ ```ruby
16
+ require 'rubygems'
17
+ require 'zmqjsonrpc'
18
+
19
+ class Proxy
20
+ def some_method(a,b,c)
21
+ # do you thing
22
+ return ["xyz", 77]
23
+ end
24
+ end
25
+
26
+ # blocking server
27
+ proxy = Proxy.new()
28
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200")
29
+ server.server_loop
30
+
31
+ # -or- dispatch a thread
32
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200")
33
+ thread = Thread.new {
34
+ server.server_loop
35
+ }
36
+ # and cancel the thread if you want to shut the server down
37
+ thread.exit
38
+ ```
39
+
40
+ ## Resources
41
+
42
+ * The [JSON RPC 2.0 Spec](http://www.jsonrpc.org/specification)
43
+ * The used [ZeroMQ gem](https://github.com/chuckremes/ffi-rzmq) and [good examples](http://github.com/andrewvc/learn-ruby-zeromq)
44
+ * [Gem making](http://guides.rubygems.org/make-your-own-gem/)
45
+
46
+ ## Stuff left to do
47
+
48
+ * Add support for by-name parameters (see [the spec](http://www.jsonrpc.org/specification#parameter_structures))
49
+ * Add individual exception classes in the client.
50
+ * Send different error codes if something goes wrong in the server.
51
+ * Keep the client connection alive instead of re-establishing every time.
52
+ * Add more tests.
53
+
54
+ ## Licence
55
+
56
+ This code is released under the terms of MIT License.
57
+
58
+ ## Contribute
59
+
60
+ Please do so! Just send a messahe or send a pull request.
61
+ Especially, adding webrick for transport would be nice.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
data/Vagrantfile ADDED
@@ -0,0 +1,31 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ provision = <<SCRIPT
5
+ # Update system
6
+ sudo apt-get -y update
7
+ sudo apt-get -y upgrade
8
+ sudo apt-get -y install build-essential git
9
+
10
+ # ruby
11
+ sudo apt-get -y install ruby2.0 ruby2.0-dev bundler rake
12
+ sudo ln -sf /usr/bin/ruby2.0 /usr/bin/ruby # make ruby 2 default
13
+ sudo ln -sf /usr/bin/gem2.0 /usr/bin/gem
14
+ sudo ln -sf /usr/bin/irb2.0 /usr/bin/irb
15
+
16
+ # install tools for development
17
+ sudo apt-get -y install vim man
18
+
19
+ # install dependencies
20
+ sudo apt-get install libzmq3 libzmq3-dev
21
+ SCRIPT
22
+
23
+ Vagrant.configure("2") do |config|
24
+ config.vm.box = "ubuntu/trusty64"
25
+ config.vm.hostname = "simplejsonrpc"
26
+ config.vm.provision :shell, inline: provision
27
+
28
+ config.vm.provider "virtualbox" do |vb|
29
+ vb.name = "simplejsonrpc"
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ require 'json'
2
+ require 'securerandom'
3
+
4
+ module ZmqJsonRpc
5
+ class ClientError < RuntimeError
6
+ end
7
+
8
+ # You shall instanciate this class with a connect string which follows the zeroMQ conventions: http://api.zeromq.org/2-1:zmq-connect
9
+ # After connecting you can call any method on this class and it will be sent to the server.
10
+ # If something goes wrong ZmqJsonRpc::ClientError is thrown.
11
+ # timeout is measured in milliseconds.
12
+ #
13
+ # NOTE
14
+ # The client does not keep the connection alive. For each request, a new connection is esablished and torn down after the response.
15
+ # This could become a performance issue.
16
+ class Client
17
+ def initialize(connect="tcp://127.0.0.1:49200", timeout=10000)
18
+ @connect = connect
19
+ @timeout = timeout
20
+ end
21
+
22
+ def send_rpc(method, params=[])
23
+ begin
24
+ # connect socket
25
+ @context = ZMQ::Context.new(1)
26
+ @socket = @context.socket(ZMQ::REQ)
27
+ @socket.connect(@connect)
28
+ @socket.setsockopt(ZMQ::SNDTIMEO, @timeout)
29
+ @socket.setsockopt(ZMQ::RCVTIMEO, @timeout)
30
+
31
+ # build and send request
32
+ req_id = SecureRandom.uuid
33
+ request = {
34
+ id: req_id,
35
+ jsonrpc: "2.0",
36
+ method: method.to_s,
37
+ params: params
38
+ }
39
+ rc = @socket.send_string(request.to_json) # this will always succeed, even if the server is not reachable.
40
+
41
+ # interpret response
42
+ response = ''
43
+ rc = @socket.recv_string(response)
44
+ raise "Could talk to the server (server unreachable? time out?)" if rc < 0
45
+ resjson = JSON.parse(response)
46
+ # check response
47
+ raise "Response's id did not match the sent id" if resjson["id"] != req_id
48
+ raise "Response's version number is not supported (#{resjson["jsonrpc"]})" if resjson["jsonrpc"].strip != "2.0"
49
+ raise "Server returned error (#{resjson["error"]["code"] || "?"}): #{resjson["error"]["message"]}\n#{resjson["error"]["data"]}" if resjson["error"]
50
+
51
+ return resjson["result"]
52
+ rescue => e
53
+ raise ClientError, e.message
54
+ ensure
55
+ @socket.close rescue ''
56
+ end
57
+ end
58
+
59
+ def method_missing(meth, *args, &block)
60
+ self.send_rpc(meth, args)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,87 @@
1
+ require 'ffi-rzmq'
2
+ require 'json'
3
+
4
+ module ZmqJsonRpc
5
+ # This class implements the server:
6
+ #
7
+ # USE
8
+ # class Proxy
9
+ # def some_method(a,b)
10
+ # return [a,b,{"XXXX"=> 1}]
11
+ # end
12
+ # end
13
+ # server = ZmqJsonRpc::Server.new(Proxy.new())
14
+ # server.server_loop
15
+ #
16
+ #
17
+ # If you want a non blocking server do
18
+ # server = ZmqJsonRpc::Server.new(Proxy.new())
19
+ # thread = Thread.new {
20
+ # server.server_loop
21
+ # }
22
+ # # later either do
23
+ # thread.join # waiting for the server to finish -- which is never!
24
+ # # or
25
+ # thread.exit # or end the thread
26
+ class Server
27
+ # For errors the spec says:
28
+ # The error codes from and including -32768 to -32000 are reserved for pre-defined errors. Any code within this range, but not defined explicitly below is reserved for future use. The error codes are nearly the same as those suggested for XML-RPC at the following url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
29
+ #
30
+ # For details on the zmq gem, please see https://github.com/chuckremes/ffi-rzmq (best to look in the code)
31
+ def initialize(proxy, connect="tcp://*:49200", logger=nil)
32
+ @connect = connect
33
+ @proxy = proxy
34
+ @logger = logger
35
+ end
36
+
37
+ def handle_request(request)
38
+ begin
39
+ req_id = nil
40
+ rpc = JSON.parse(request)
41
+ raise "Received unsupprted jsonrpc version (#{rpc['jsonrpc']})" if rpc["jsonrpc"].strip != "2.0"
42
+ rid = rpc["id"]
43
+ method = rpc["method"]
44
+ params = rpc["params"]
45
+
46
+ @logger.info "Received JSON RPC request: #{method}(#{params.collect {|p| p.inspect}.join(", ")})" unless @logger.nil?
47
+ result = @proxy.send(method.to_sym, *params)
48
+ response = {
49
+ id: rid,
50
+ jsonrpc: "2.0",
51
+ result: result
52
+ }
53
+ return response.to_json
54
+ rescue => e
55
+ # If there is more time to spare, we could implement the actual error codes here.
56
+ @logger.warn "Returning error for RPC request: #{e.message})" unless @logger.nil?
57
+ response = {
58
+ id: rid,
59
+ jsonrpc: "2.0",
60
+ error: {
61
+ code: 32603,
62
+ message: e.message,
63
+ data: e.backtrace.inspect
64
+ }
65
+ }
66
+ return response.to_json
67
+ end
68
+ end
69
+
70
+ def server_loop
71
+ @context = ZMQ::Context.new(1)
72
+ @socket = @context.socket(ZMQ::REP)
73
+ @socket.bind(@connect)
74
+ begin
75
+ loop do
76
+ request = ''
77
+ rc = @socket.recv_string(request)
78
+ response = handle_request(request)
79
+ @socket.send_string(response)
80
+ end
81
+ ensure
82
+ @socket.close
83
+ # @context.terminate
84
+ end
85
+ end
86
+ end
87
+ end
data/lib/zmqjsonrpc.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require "simple_json_rpc/client"
3
+ require "simple_json_rpc/server"
4
+
5
+ module ZmqJsonRpc
6
+ end
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require "logger"
3
+ require "stringio"
4
+ require_relative '../lib/zmqjsonrpc'
5
+
6
+ class TestClientServer < Test::Unit::TestCase
7
+ def setup
8
+ @buffer = StringIO.new
9
+ @logger = Logger.new(@buffer)
10
+ @logger.level = Logger::WARN
11
+ end
12
+
13
+ def assert_empty_log
14
+ @buffer.seek(0)
15
+ assert_equal @buffer.read, "", "Server log included warnings."
16
+ end
17
+
18
+ def test_client_with_server
19
+ proxy_class = Class.new() do
20
+ def initialize(test_case)
21
+ @test_case = test_case
22
+ @method_was_called = false
23
+ end
24
+ def method_was_called?
25
+ return @method_was_called
26
+ end
27
+
28
+ def some_method(a,b,c)
29
+ @test_case.assert_equal a, 1
30
+ @test_case.assert_equal b, "b"
31
+ @test_case.assert_equal c, [1,{"a" => 1}]
32
+ @method_was_called = true
33
+ return "abc"
34
+ end
35
+ end
36
+
37
+ proxy = proxy_class.new(self)
38
+ server = ZmqJsonRpc::Server.new(proxy, "tcp://*:49200", @logger)
39
+ thread = Thread.new {
40
+ server.server_loop
41
+ }
42
+ client = ZmqJsonRpc::Client.new("tcp://127.0.0.1:49200")
43
+ assert_equal client.some_method(1, "b", [1,{a:1}]), "abc"
44
+ assert proxy.method_was_called?, "Server method was not called"
45
+
46
+ assert_raise ZmqJsonRpc::ClientError do
47
+ client.fishy_method()
48
+ end
49
+
50
+ thread.exit
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ # more info here: http://guides.rubygems.org/specification-reference/
2
+
3
+ # coding: utf-8
4
+ lib = File.expand_path('../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "zmqjsonrpc"
9
+ spec.version = "0.1"
10
+ spec.authors = ["Tom Rothe"]
11
+ spec.email = ["tom@bisdn.de"]
12
+ spec.description = 'Simple JSON RPC 2.0 client and server via zmq.'
13
+ spec.summary = 'ZeroMQ JSON RPC client and server.'
14
+ spec.homepage = "https://github.com/bisdn/zmqjsonrpc"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.test_files = Dir.glob('test/*.rb')
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency("ffi-rzmq", "~> 2.0.4")
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "rake"
24
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zmqjsonrpc
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Tom Rothe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi-rzmq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple JSON RPC 2.0 client and server via zmq.
56
+ email:
57
+ - tom@bisdn.de
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - README.md
66
+ - Rakefile
67
+ - Vagrantfile
68
+ - lib/simple_json_rpc/client.rb
69
+ - lib/simple_json_rpc/server.rb
70
+ - lib/zmqjsonrpc.rb
71
+ - test/test_client_server.rb
72
+ - zmqjsonrpc.gemspec
73
+ homepage: https://github.com/bisdn/zmqjsonrpc
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.0.14
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: ZeroMQ JSON RPC client and server.
97
+ test_files:
98
+ - test/test_client_server.rb