czmq 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +66 -0
- data/Rakefile +1 -0
- data/czmq.gemspec +26 -0
- data/examples/simple_dealerrep_example/dealer_node.rb +26 -0
- data/examples/simple_dealerrep_example/rep_node_7000.rb +15 -0
- data/examples/simple_dealerrep_example/rep_node_8000.rb +15 -0
- data/examples/simple_dealerrep_example/rep_node_9000.rb +15 -0
- data/examples/simple_reqrep_example/client.rb +13 -0
- data/examples/simple_reqrep_example/server.rb +14 -0
- data/examples/simple_zbeacon_example/client.rb +18 -0
- data/examples/simple_zbeacon_example/server.rb +20 -0
- data/examples/zloop_reqrep_example/client.rb +19 -0
- data/examples/zloop_reqrep_example/server.rb +29 -0
- data/examples/zloop_timer_example/zloop_timer.rb +17 -0
- data/lib/czmq.rb +9 -0
- data/lib/czmq/context.rb +68 -0
- data/lib/czmq/version.rb +3 -0
- data/lib/czmq/zbeacon.rb +112 -0
- data/lib/czmq/zframe.rb +71 -0
- data/lib/czmq/zloop.rb +89 -0
- data/lib/czmq/zmsg.rb +112 -0
- data/lib/czmq/zpoller.rb +85 -0
- data/lib/czmq/zsocket.rb +151 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7eabd4190bbf945fbd382a438ef2db82175b33d1
|
4
|
+
data.tar.gz: 005591101a505c8ab35675056b9e9250d4ed01df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cb1d5f8ce7967c628969678b8eaa1cfce4f1c174994a24ccb3ff9607f874dfd69ca9f140dca4510829da5a6b18ecf330211e4521a0fe077b2d6983bf40bd044e
|
7
|
+
data.tar.gz: a3120e91b841fe3abc8fdb190b06cc60d488f0b59814a7ca2410e6b36ae768ff654af5d770a892f2c57cc71217a646e41ef20b8bd3abd209d231d154993e12a6
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*~
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
.ruby-version
|
7
|
+
.ruby-gemset
|
8
|
+
coverage
|
9
|
+
Gemfile.lock
|
10
|
+
InstalledFiles
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
|
19
|
+
# YARD artifacts
|
20
|
+
.yardoc
|
21
|
+
_yardoc
|
22
|
+
doc/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mauro Tortonesi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# ruby-czmq
|
2
|
+
|
3
|
+
ruby-czmq is my attempt at building a Ruby bindings gem for the
|
4
|
+
[CZMQ](http://czmq.zeromq.org/) library.
|
5
|
+
|
6
|
+
ruby-czmq realizes a convenient and relatively elegant API on top of the
|
7
|
+
FFI-based [ruby-czmq-ffi](https://github.com/mtortonesi/ruby-czmq-ffi) gem,
|
8
|
+
which provides low-level bindings to CZMQ and works on all the main Ruby VMs:
|
9
|
+
YARV/MRI, JRuby, and Rubinius.
|
10
|
+
|
11
|
+
|
12
|
+
## Motivation
|
13
|
+
|
14
|
+
One could wonder whether there was actually the need for a third gem (in
|
15
|
+
addition to [ffi-rzmq](https://github.com/chuckremes/ffi-rzmq) and the more
|
16
|
+
outdated and YARV/MRI-specific [rbzmq](https://github.com/zeromq/rbzmq))
|
17
|
+
providing Ruby bindings for [ZeroMQ](http://zeromq.org/).
|
18
|
+
|
19
|
+
The fact is that both ffi-rzmq and rzmq are bindings for the ZeroMQ library.
|
20
|
+
ZeroMQ is an awesome library, but it provides a rather low-level API designed
|
21
|
+
for C/C++ and relatively difficult to make available to Ruby applications
|
22
|
+
through a convenient and elegant interface. Instead, ruby-czmq provides
|
23
|
+
bindings to [CZMQ](http://czmq.zeromq.org/), a library that was specifically
|
24
|
+
designed to provide a higher-level API to the ZeroMQ functions and that is
|
25
|
+
significantly more Ruby-friendly than ZeroMQ. In addition, CZMQ provides
|
26
|
+
functions, such as service discovery and cryptography, that are not present in
|
27
|
+
ZeroMQ.
|
28
|
+
|
29
|
+
By interfacing with CZMQ, instead of ZeroMQ, ruby-czmq can provide many of the
|
30
|
+
functions of ffi-rzmq and rbzmq with significantly less code.
|
31
|
+
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
### Stable version
|
36
|
+
|
37
|
+
You can get the stable version of ruby-czmq by installing the czmq gem from
|
38
|
+
RubyGems:
|
39
|
+
|
40
|
+
gem install czmq
|
41
|
+
|
42
|
+
### Development version
|
43
|
+
|
44
|
+
If you want to try the development version of ruby-czmq, instead, just place
|
45
|
+
these lines:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 'czmq-ffi', git: 'https://github.com/mtortonesi/ruby-czmq-ffi.git'
|
49
|
+
gem 'czmq', git: 'https://github.com/mtortonesi/ruby-czmq.git'
|
50
|
+
```
|
51
|
+
|
52
|
+
in your Gemfile and run:
|
53
|
+
|
54
|
+
bundle install
|
55
|
+
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
You can find some simple examples that demonstrate how to use the ruby-czmq api
|
60
|
+
in the [examples directory](https://github.com/mtortonesi/ruby-czmq/tree/master/examples)
|
61
|
+
of this project.
|
62
|
+
|
63
|
+
|
64
|
+
## License
|
65
|
+
|
66
|
+
MIT
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/czmq.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'czmq/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'czmq'
|
8
|
+
spec.version = CZMQ::VERSION
|
9
|
+
spec.authors = ['Mauro Tortonesi']
|
10
|
+
spec.email = ['mauro.tortonesi@unife.it']
|
11
|
+
spec.description = %q{Ruby bindings for CZMQ}
|
12
|
+
spec.summary = %q{Ruby gem that provides bindings for the CZMQ library.
|
13
|
+
This is a pure Ruby gem that interfaces with CZMQ using FFI, so it
|
14
|
+
should work under MRI, JRuby, and Rubinius.}
|
15
|
+
spec.homepage = 'https://github.com/mtortonesi/ruby-czmq'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'czmq-ffi'
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
zsocket = ctx.create_zsocket(CZMQ::DEALER)
|
6
|
+
zsocket.connect('tcp://localhost:7000')
|
7
|
+
zsocket.connect('tcp://localhost:8000')
|
8
|
+
zsocket.connect('tcp://localhost:9000')
|
9
|
+
|
10
|
+
3.times do
|
11
|
+
# in order to talk with REP sockets, we need to prepend each request with
|
12
|
+
# a null string frame
|
13
|
+
zsocket.send_string('', :more)
|
14
|
+
zsocket.send_string('request')
|
15
|
+
|
16
|
+
# rep will be an array of strings, with rep[0] containing an empty string
|
17
|
+
# and rep[1] containing the actual reply
|
18
|
+
rep = zsocket.receive_strings
|
19
|
+
puts rep[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
zsocket.close
|
23
|
+
ctx.close
|
24
|
+
else
|
25
|
+
STDERR.puts 'Context allocation failed.'
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
6
|
+
zsocket.bind('tcp://*:7000')
|
7
|
+
|
8
|
+
puts zsocket.receive_string
|
9
|
+
zsocket.send_string('reply')
|
10
|
+
|
11
|
+
zsocket.close
|
12
|
+
ctx.close
|
13
|
+
else
|
14
|
+
STDERR.puts 'Context allocation failed.'
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
6
|
+
zsocket.bind('tcp://*:8000')
|
7
|
+
|
8
|
+
puts zsocket.receive_string
|
9
|
+
zsocket.send_string('reply')
|
10
|
+
|
11
|
+
zsocket.close
|
12
|
+
ctx.close
|
13
|
+
else
|
14
|
+
STDERR.puts 'Context allocation failed.'
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
6
|
+
zsocket.bind('tcp://*:9000')
|
7
|
+
|
8
|
+
puts zsocket.receive_string
|
9
|
+
zsocket.send_string('reply')
|
10
|
+
|
11
|
+
zsocket.close
|
12
|
+
ctx.close
|
13
|
+
else
|
14
|
+
STDERR.puts 'Context allocation failed.'
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
zsocket = ctx.create_zsocket(CZMQ::REQ)
|
6
|
+
zsocket.connect('tcp://localhost:8000')
|
7
|
+
zsocket.send_string('request')
|
8
|
+
puts zsocket.receive_string
|
9
|
+
zsocket.close
|
10
|
+
ctx.close
|
11
|
+
else
|
12
|
+
STDERR.puts 'Context allocation failed.'
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'czmq'
|
2
|
+
|
3
|
+
ctx = CZMQ::Context.new
|
4
|
+
if ctx
|
5
|
+
puts CZMQ::REP
|
6
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
7
|
+
puts zsocket.bind('tcp://*:8000')
|
8
|
+
puts zsocket.receive_string
|
9
|
+
zsocket.send_string('response')
|
10
|
+
zsocket.close
|
11
|
+
ctx.close
|
12
|
+
else
|
13
|
+
STDERR.puts 'Context allocation failed.'
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'czmq'
|
7
|
+
|
8
|
+
ctx = CZMQ::Context.new
|
9
|
+
if ctx
|
10
|
+
zsocket = ctx.create_zsocket(CZMQ::REQ)
|
11
|
+
zsocket.connect('tcp://localhost:8000')
|
12
|
+
zsocket.send_string('request')
|
13
|
+
puts zsocket.receive_string
|
14
|
+
zsocket.close
|
15
|
+
ctx.close
|
16
|
+
else
|
17
|
+
STDERR.puts 'Context allocation failed.'
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'czmq'
|
7
|
+
|
8
|
+
ctx = CZMQ::Context.new
|
9
|
+
if ctx
|
10
|
+
puts CZMQ::REP
|
11
|
+
zbeacon = ctx.create_zbeacon(9999) # use port 9999
|
12
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
13
|
+
puts zsocket.bind('tcp://*:8000')
|
14
|
+
puts zsocket.receive_string
|
15
|
+
zsocket.send_string('response')
|
16
|
+
zsocket.close
|
17
|
+
ctx.close
|
18
|
+
else
|
19
|
+
STDERR.puts 'Context allocation failed.'
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'czmq'
|
7
|
+
|
8
|
+
begin
|
9
|
+
ctx = CZMQ::Context.new
|
10
|
+
zsocket = ctx.create_zsocket(CZMQ::REQ)
|
11
|
+
zsocket.connect('tcp://localhost:8000')
|
12
|
+
puts 'sending string'
|
13
|
+
zsocket.send_string('request')
|
14
|
+
puts zsocket.receive_string
|
15
|
+
zsocket.close
|
16
|
+
ctx.close
|
17
|
+
rescue => e
|
18
|
+
STDERR.puts e.backtrace
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'czmq'
|
7
|
+
|
8
|
+
ctx = CZMQ::Context.new
|
9
|
+
abort 'Context allocation failed.' unless ctx
|
10
|
+
|
11
|
+
zsocket = ctx.create_zsocket(CZMQ::REP)
|
12
|
+
|
13
|
+
begin
|
14
|
+
zsocket.bind('tcp://*:8000')
|
15
|
+
zloop = CZMQ::ZLoop.new
|
16
|
+
pi = zsocket.to_pollitem
|
17
|
+
zloop.poller(pi) do |zlp,socket|
|
18
|
+
str = socket.receive_string
|
19
|
+
puts "Received on REQ/REP ZSocket: #{str}"; STDOUT.flush
|
20
|
+
socket.send_string(str.reverse)
|
21
|
+
end
|
22
|
+
zloop.start
|
23
|
+
rescue => e
|
24
|
+
STDERR.puts e.inspect
|
25
|
+
STDERR.puts e.backtrace
|
26
|
+
end
|
27
|
+
|
28
|
+
zsocket.close
|
29
|
+
ctx.close
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'czmq'
|
7
|
+
|
8
|
+
zloop = CZMQ::ZLoop.new
|
9
|
+
zloop.add_timer(2_000, 2) do |*args|
|
10
|
+
puts "#{Time.now} called block inside first callback"
|
11
|
+
puts "args = #{args.inspect}"
|
12
|
+
end
|
13
|
+
zloop.add_timer(10_000, 1) do |*args|
|
14
|
+
puts "#{Time.now} called block inside second callback"
|
15
|
+
puts "args = #{args.inspect}"
|
16
|
+
end
|
17
|
+
zloop.start
|
data/lib/czmq.rb
ADDED
data/lib/czmq/context.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module CZMQ
|
4
|
+
class Context
|
5
|
+
|
6
|
+
def initialize(opts = {})
|
7
|
+
# TODO: check this code
|
8
|
+
_DEFAULT_OPTS = { io_threads: 1, linger: 0 }
|
9
|
+
opts = _DEFAULT_OPTS.merge(opts)
|
10
|
+
|
11
|
+
@zctx = LibCZMQ.zctx_new
|
12
|
+
# TODO: check that this is not null
|
13
|
+
|
14
|
+
# Setup multiple I/O threads if requested
|
15
|
+
if opts[:io_threads].is_a? Numeric and opts[:io_threads] > 1
|
16
|
+
LibCZMQ.zctx_set_iothreads(@zctx, opts[:io_threads])
|
17
|
+
end
|
18
|
+
|
19
|
+
setup_finalizer
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
if @zctx
|
24
|
+
# Since we explicitly close the zctx, we have to remove the finalizer.
|
25
|
+
remove_finalizer
|
26
|
+
LibCZMQ.zctx_destroy(@zctx)
|
27
|
+
@zctx = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_zsocket(type, opts={})
|
32
|
+
ZSocket.new(@zctx, type, opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_zbeacon(port, opts={})
|
36
|
+
ZBeacon.new(@zctx, port, opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: consider whether to provide the set_iothreads and set_linger methods as well.
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# After object destruction, make sure that the corresponding zctx is
|
45
|
+
# destroyed as well.
|
46
|
+
#
|
47
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
48
|
+
# deallocation request comes from the process that allocated it in the first place.
|
49
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
50
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
51
|
+
def setup_finalizer
|
52
|
+
ObjectSpace.define_finalizer(self, self.class.close_zctx(@zctx))
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_finalizer
|
56
|
+
ObjectSpace.undefine_finalizer self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
60
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
61
|
+
def self.close_zctx(zctx)
|
62
|
+
Proc.new do
|
63
|
+
LibCZMQ.zctx_destroy(zctx)
|
64
|
+
zctx = nil # Just in case
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/czmq/version.rb
ADDED
data/lib/czmq/zbeacon.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
module CZMQ
|
2
|
+
class ZBeacon
|
3
|
+
def initialize(ctx, port, opts={})
|
4
|
+
@ctx = ctx
|
5
|
+
@zbeacon = LibCZMQ.zbeacon_new(port)
|
6
|
+
|
7
|
+
# zbeacon_new returns NULL in case of failure
|
8
|
+
raise 'Could not create ZBeacon' if @zbeacon.null?
|
9
|
+
|
10
|
+
# Setup no_echo option if requested
|
11
|
+
if opts[:no_echo]
|
12
|
+
LibCZMQ.zbeacon_no_echo(@zbeacon)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Setup the finalizer to free the memory allocated by the CZMQ library
|
16
|
+
setup_finalizer
|
17
|
+
end
|
18
|
+
|
19
|
+
def close
|
20
|
+
if @zbeacon
|
21
|
+
# Since we explicitly close the ZBeacon, we have to remove the finalizer.
|
22
|
+
remove_finalizer
|
23
|
+
|
24
|
+
# Destroy the ZBeacon
|
25
|
+
LibCZMQ.zbeacon_destroy(@zbeacon)
|
26
|
+
|
27
|
+
# Unset @zbeacon
|
28
|
+
@zbeacon = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def hostname
|
33
|
+
raise "Can't get the hostname of a closed ZBeacon!" unless @zbeacon
|
34
|
+
LibCZMQ.zbeacon_hostname(@zbeacon)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_interval(interval)
|
38
|
+
raise "Can't set the advertisement interval of a closed ZBeacon!" unless @zbeacon
|
39
|
+
LibCZMQ.zbeacon_set_interval(@zbeacon, interval)
|
40
|
+
end
|
41
|
+
|
42
|
+
def publish(data)
|
43
|
+
raise "Can't publish advertisements on a closed ZBeacon!" unless @zbeacon
|
44
|
+
|
45
|
+
# Transform data into a bytes array
|
46
|
+
bytes = to_bytearray(data)
|
47
|
+
|
48
|
+
LibCZMQ.zbeacon_publish(@zbeacon, bytes)
|
49
|
+
end
|
50
|
+
|
51
|
+
def silence
|
52
|
+
raise "Can't silence an uninitialized ZBeacon!" unless @zbeacon
|
53
|
+
LibCZMQ.zbeacon_silence(@zbeacon)
|
54
|
+
end
|
55
|
+
|
56
|
+
def subscribe(match=nil)
|
57
|
+
raise "Can't subscribe to an uninitialized ZBeacon!" unless @zbeacon
|
58
|
+
LibCZMQ.zbeacon_subscribe(@zbeacon, match)
|
59
|
+
end
|
60
|
+
|
61
|
+
def unsubscribe
|
62
|
+
raise "Can't unsubscribe from an uninitialized ZBeacon!" unless @zbeacon
|
63
|
+
LibCZMQ.zbeacon_unsubscribe(@zbeacon)
|
64
|
+
end
|
65
|
+
|
66
|
+
def socket
|
67
|
+
raise "Can't get socket of an uninitialized ZBeacon!" unless @zbeacon
|
68
|
+
ZSocket.new(@ctx, LibCZMQ.zbeacon_socket(@zbeacon))
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# After object destruction, make sure that the corresponding zbeacon is
|
75
|
+
# destroyed as well.
|
76
|
+
#
|
77
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
78
|
+
# deallocation request comes from the process that allocated it in the first place.
|
79
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
80
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
81
|
+
def setup_finalizer
|
82
|
+
ObjectSpace.define_finalizer(self, self.class.close_zbeacon(@zbeacon))
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_finalizer
|
86
|
+
ObjectSpace.undefine_finalizer self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
90
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
91
|
+
def self.close_zbeacon(zbeacon)
|
92
|
+
Proc.new do
|
93
|
+
LibCZMQ.zbeacon_destroy(zbeacon)
|
94
|
+
zbeacon = nil # Just in case
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_bytearray(data)
|
99
|
+
bytes = nil
|
100
|
+
if data.is_a? String
|
101
|
+
# String to byte array conversion using the default UTF-8 encoding
|
102
|
+
# bytes = data.bytes.to_a
|
103
|
+
bytes = data.encode("UTF-8").bytes.to_a
|
104
|
+
elsif data.respond_to? :to_a and !data.is_a? Array
|
105
|
+
bytes = data
|
106
|
+
else
|
107
|
+
raise "Don't know how to deal with data" unless data.is_a? Array
|
108
|
+
end
|
109
|
+
bytes
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/czmq/zframe.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module CZMQ
|
4
|
+
class ZFrame
|
5
|
+
def initialize(obj=nil)
|
6
|
+
if obj.is_a? FFI::Pointer
|
7
|
+
@zframe = obj
|
8
|
+
else
|
9
|
+
@zframe = LibCZMQ.zframe_new(obj)
|
10
|
+
end
|
11
|
+
|
12
|
+
setup_finalizer
|
13
|
+
end
|
14
|
+
|
15
|
+
# Need to provide a copy constructor that implements deep copy
|
16
|
+
def initialize_copy(orig)
|
17
|
+
super
|
18
|
+
unless @zframe.nil?
|
19
|
+
@zframe = LibCZMQ.zframe_dup(@zframe)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy
|
24
|
+
if @zframe
|
25
|
+
# Since we explicitly close the zframe, we have to remove the finalizer.
|
26
|
+
remove_finalizer
|
27
|
+
LibCZMQ.zframe_destroy(@zframe)
|
28
|
+
raise 'Error!' unless @zframe.nil?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def __extract__
|
33
|
+
raise 'Trying to extract internal @zframe pointer from an unitialized ZFrame!' if @zframe.nil?
|
34
|
+
|
35
|
+
# Since we explicitly hand over the @zframe pointer, we have to remove the finalizer.
|
36
|
+
remove_finalizer
|
37
|
+
|
38
|
+
# Return content of @zframe pointer and reset it to nil
|
39
|
+
zframe = @zframe
|
40
|
+
@zframe = nil
|
41
|
+
zframe
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# After object destruction, make sure that the corresponding zframe is
|
48
|
+
# destroyed as well.
|
49
|
+
#
|
50
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
51
|
+
# deallocation request comes from the process that allocated it in the first place.
|
52
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
53
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
54
|
+
def setup_finalizer
|
55
|
+
ObjectSpace.define_finalizer(self, self.class.close_zframe(@zframe))
|
56
|
+
end
|
57
|
+
|
58
|
+
def remove_finalizer
|
59
|
+
ObjectSpace.undefine_finalizer self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
63
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
64
|
+
def self.close_zframe(zframe)
|
65
|
+
Proc.new do
|
66
|
+
LibCZMQ.zframe_destroy(zframe)
|
67
|
+
zframe = nil # Just in case
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/czmq/zloop.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module CZMQ
|
2
|
+
class ZLoop
|
3
|
+
def initialize(zloop=nil)
|
4
|
+
@zloop = zloop || LibCZMQ.zloop_new
|
5
|
+
|
6
|
+
# LibCZMQ.zloop_set_verbose(@zloop, true)
|
7
|
+
|
8
|
+
unless zloop
|
9
|
+
setup_finalizer
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def destroy
|
14
|
+
if @zloop
|
15
|
+
# Since we explicitly close the zloop, we have to remove the finalizer.
|
16
|
+
remove_finalizer
|
17
|
+
rc = LibCZMQ.zloop_destroy(@zloop)
|
18
|
+
raise 'Error!' unless @zloop.nil?
|
19
|
+
rc
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
raise 'Trying to start an unitialized ZLoop!' if @zloop.nil?
|
25
|
+
LibCZMQ.zloop_start(@zloop)
|
26
|
+
end
|
27
|
+
|
28
|
+
def poller(poll_item, func=nil, &block)
|
29
|
+
raise 'Trying to add a poller to an unitialized ZLoop!' if @zloop.nil?
|
30
|
+
raise ArgumentError, 'You need to provide a block or a proc/lambda!' unless block_given? or func.responds_to :call
|
31
|
+
|
32
|
+
the_proc = block_given? ? block.to_proc : func
|
33
|
+
|
34
|
+
# need to preserve this callback from the garbage collector
|
35
|
+
@callback = LibCZMQ.create_zloop_callback(
|
36
|
+
lambda do |zloopbuf, zpollitembuf, arg|
|
37
|
+
zpollitem = LibCZMQ::ZPollItem.new(zpollitembuf)
|
38
|
+
zlp = ZLoop.new(zloopbuf)
|
39
|
+
zsk = ZSocket.new(nil, zpollitem[:socket])
|
40
|
+
the_proc.call(zlp, zsk)
|
41
|
+
end
|
42
|
+
)
|
43
|
+
|
44
|
+
LibCZMQ.zloop_poller(@zloop, poll_item, @callback, nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method :add_poller, :poller
|
48
|
+
|
49
|
+
def poller_end(poll_item)
|
50
|
+
LibCZMQ.zloop_poller_end(@zloop, poll_item)
|
51
|
+
@callback = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method :remove_poller, :poller_end
|
55
|
+
|
56
|
+
def add_timer(delay, times, &block)
|
57
|
+
if block
|
58
|
+
LibCZMQ.zloop_timer(@zloop, delay, times, block.to_proc, nil)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# After object destruction, make sure that the corresponding zloop is
|
66
|
+
# destroyed as well.
|
67
|
+
#
|
68
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
69
|
+
# deallocation request comes from the process that allocated it in the first place.
|
70
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
71
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
72
|
+
def setup_finalizer
|
73
|
+
ObjectSpace.define_finalizer(self, self.class.close_zloop(@zloop))
|
74
|
+
end
|
75
|
+
|
76
|
+
def remove_finalizer
|
77
|
+
ObjectSpace.undefine_finalizer self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
81
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
82
|
+
def self.close_zloop(zloop)
|
83
|
+
Proc.new do
|
84
|
+
LibCZMQ.zloop_destroy(zloop)
|
85
|
+
zloop = nil # Just in case
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/czmq/zmsg.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
module CZMQ
|
2
|
+
class ZMessage
|
3
|
+
def initialize(zmsg=nil)
|
4
|
+
@zmsg = zmsg || LibCZMQ.zmsg_new
|
5
|
+
|
6
|
+
setup_finalizer
|
7
|
+
end
|
8
|
+
|
9
|
+
# Need to provide a copy constructor that implements deep copy
|
10
|
+
def initialize_copy(orig)
|
11
|
+
super
|
12
|
+
unless @zmsg.nil?
|
13
|
+
@zmsg = LibCZMQ.zmsg_dup(@zmsg)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy
|
18
|
+
if @zmsg
|
19
|
+
# Since we explicitly close the zmsg, we have to remove the finalizer.
|
20
|
+
remove_finalizer
|
21
|
+
LibCZMQ.zmsg_destroy(@zmsg)
|
22
|
+
raise 'Error!' unless @zmsg.nil?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def __send_over(zsocket)
|
27
|
+
raise 'Trying to transmit an unitialized ZMessage!' if @zmsg.nil?
|
28
|
+
# Since by sending it over a zsocket we explicitly close the zmsg, we
|
29
|
+
# have to remove the finalizer.
|
30
|
+
remove_finalizer
|
31
|
+
rc = LibCZMQ.zmsg_send(@zmsg, zsocket)
|
32
|
+
raise 'Error!' unless @zmsg.nil?
|
33
|
+
rc
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
LibCZMQ.zmsg_size(@zmsg)
|
38
|
+
end
|
39
|
+
|
40
|
+
def content_size
|
41
|
+
LibCZMQ.zmsg_content_size(@zmsg)
|
42
|
+
end
|
43
|
+
|
44
|
+
def pop(type=:zframe)
|
45
|
+
raise 'Trying to pop from an unitialized ZMessage!' if @zmsg.nil?
|
46
|
+
|
47
|
+
case type
|
48
|
+
when :zframe
|
49
|
+
ZFrame.new(LibCZMQ.zmsg_pop(@zmsg))
|
50
|
+
when :string
|
51
|
+
LibCZMQ.zmsg_popstr(@zmsg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Push new frame in front of message
|
56
|
+
def push(obj)
|
57
|
+
raise 'Trying to push in front of an unitialized ZMessage!' if @zmsg.nil?
|
58
|
+
|
59
|
+
if obj.is_a? String
|
60
|
+
LibCZMQ.zmsg_pushstr(@zmsg, obj)
|
61
|
+
elsif obj.is_a? Array
|
62
|
+
LibCZMQ.zmsg_pushmem(@zmsg, obj)
|
63
|
+
elsif obj.is_a? ZFrame
|
64
|
+
LibCZMQ.zmsg_push(@zmsg, obj.__extract__)
|
65
|
+
else
|
66
|
+
raise ArgumentError, 'Unknown object type!'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Append new frame at the end of message
|
71
|
+
def append(obj)
|
72
|
+
raise 'Trying to append to an unitialized ZMessage!' if @zmsg.nil?
|
73
|
+
|
74
|
+
if obj.is_a? String
|
75
|
+
LibCZMQ.zmsg_addstr(@zmsg, obj)
|
76
|
+
elsif obj.is_a? ZFrame
|
77
|
+
LibCZMQ.zmsg_append(@zmsg, obj.__extract__)
|
78
|
+
else
|
79
|
+
raise ArgumentError, 'Unknown object type!'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
alias_method :<<, :append
|
84
|
+
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# After object destruction, make sure that the corresponding zmsg is
|
89
|
+
# destroyed as well.
|
90
|
+
#
|
91
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
92
|
+
# deallocation request comes from the process that allocated it in the first place.
|
93
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
94
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
95
|
+
def setup_finalizer
|
96
|
+
ObjectSpace.define_finalizer(self, self.class.close_zmsg(@zmsg))
|
97
|
+
end
|
98
|
+
|
99
|
+
def remove_finalizer
|
100
|
+
ObjectSpace.undefine_finalizer self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
104
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
105
|
+
def self.close_zmsg(zmsg)
|
106
|
+
Proc.new do
|
107
|
+
LibCZMQ.zmsg_destroy(zmsg)
|
108
|
+
zmsg = nil # Just in case
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/czmq/zpoller.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module CZMQ
|
2
|
+
class ZPoller
|
3
|
+
|
4
|
+
def initialize(zctx, *zobjs)
|
5
|
+
raise ArgumentError, 'Must provide at least one ZSocket!' if zobjs.empty?
|
6
|
+
|
7
|
+
# # Need to save context for ZSocket operations
|
8
|
+
# @zctx = zctx
|
9
|
+
@zsockets = zobjs
|
10
|
+
|
11
|
+
# We need to setup the ZPoller from zsocket pointers, not ZSocket objects
|
12
|
+
zsocks = zobjs.map{|zo| zo.__get_zsocket_pointer__ }
|
13
|
+
|
14
|
+
# Create zpoller by calling zpoller_new
|
15
|
+
first_arg = zsocks.shift
|
16
|
+
if zsocks.empty?
|
17
|
+
# We just need to pass a single argument
|
18
|
+
@zpoller = LibCZMQ.zpoller_new(first_arg)
|
19
|
+
else
|
20
|
+
# We also need to pass additional arguments, using the horrible varargs interface
|
21
|
+
other_args = ([ :pointer ] * zsocks.size).zip(zsocks).flatten
|
22
|
+
@zpoller = LibCZMQ.zpoller_new(first_arg, *other_args)
|
23
|
+
end
|
24
|
+
|
25
|
+
setup_finalizer
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def destroy
|
30
|
+
if @zpoller
|
31
|
+
# Since we explicitly close the zpoller, we have to remove the finalizer.
|
32
|
+
remove_finalizer
|
33
|
+
LibCZMQ.zpoller_destroy(@zpoller)
|
34
|
+
raise 'Error!' unless @zpoller.nil?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def wait(timeout)
|
40
|
+
raise "Can't wait on an uninitialized ZPoller!" unless @zpoller
|
41
|
+
# TODO: should we return a newly created ZSocket or an existing one?
|
42
|
+
zsockptr = LibCZMQ.zpoller_wait(@zpoller, timeout)
|
43
|
+
yield zsockets.select{|zsock| zsock.__get_zsocket_pointer__ == zsockptr }
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def expired?
|
48
|
+
raise "Can't check if an uninitialized ZPoller is expired!" unless @zpoller
|
49
|
+
LibCZMQ.zpoller_expired(@zpoller)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def terminated?
|
54
|
+
raise "Can't check if an uninitialized ZPoller is terminated!" unless @zpoller
|
55
|
+
LibCZMQ.zpoller_terminated(@zpoller)
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# After object destruction, make sure that the corresponding zpoller is
|
62
|
+
# destroyed as well.
|
63
|
+
#
|
64
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
65
|
+
# deallocation request comes from the process that allocated it in the first place.
|
66
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
67
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
68
|
+
def setup_finalizer
|
69
|
+
ObjectSpace.define_finalizer(self, self.class.close_zpoller(@zpoller))
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_finalizer
|
73
|
+
ObjectSpace.undefine_finalizer self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
77
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
78
|
+
def self.close_zpoller(zpoller)
|
79
|
+
Proc.new do
|
80
|
+
LibCZMQ.zpoller_destroy(zpoller)
|
81
|
+
zpoller = nil # Just in case
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/czmq/zsocket.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'czmq-ffi'
|
2
|
+
|
3
|
+
|
4
|
+
module CZMQ
|
5
|
+
class ZSocket
|
6
|
+
|
7
|
+
def initialize(zctx, obj, opts = {})
|
8
|
+
@zctx = zctx
|
9
|
+
|
10
|
+
if obj.is_a? FFI::Pointer
|
11
|
+
@zsocket = obj
|
12
|
+
else
|
13
|
+
@zsocket = LibCZMQ.zsocket_new(zctx, obj)
|
14
|
+
# TODO: Maybe check that zsocket is not null?
|
15
|
+
end
|
16
|
+
|
17
|
+
setup_finalizer
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
if @zsocket
|
22
|
+
# Since we explicitly close the zsocket, we have to remove the finalizer.
|
23
|
+
remove_finalizer
|
24
|
+
LibCZMQ.zsocket_destroy(@zctx, @zsocket)
|
25
|
+
@zsocket = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def bind(address, opts={})
|
30
|
+
raise "Can't bind a closed ZSocket!" unless @zsocket
|
31
|
+
# TODO: pass opts to zsocket_bind using varargs
|
32
|
+
LibCZMQ.zsocket_bind(@zsocket, address)
|
33
|
+
end
|
34
|
+
|
35
|
+
def unbind(address, opts={})
|
36
|
+
raise "Can't unbind a closed ZSocket!" unless @zsocket
|
37
|
+
# TODO: pass opts to zsocket_unbind using varargs
|
38
|
+
LibCZMQ.zsocket_unbind(@zsocket, address)
|
39
|
+
end
|
40
|
+
|
41
|
+
def connect(address, opts={})
|
42
|
+
raise "Can't connect a closed ZSocket!" unless @zsocket
|
43
|
+
# TODO: pass opts to zsocket_connect using varargs
|
44
|
+
LibCZMQ.zsocket_connect(@zsocket, address)
|
45
|
+
end
|
46
|
+
|
47
|
+
def disconnect(address, opts={})
|
48
|
+
raise "Can't disconnect a closed ZSocket!" unless @zsocket
|
49
|
+
# TODO: pass opts to zsocket_disconnect using varargs
|
50
|
+
LibCZMQ.zsocket_disconnect(@zsocket, address)
|
51
|
+
end
|
52
|
+
|
53
|
+
def poll(msecs)
|
54
|
+
raise "Can't poll a closed ZSocket!" unless @zsocket
|
55
|
+
LibCZMQ.zsocket_poll(@zsocket, msecs)
|
56
|
+
end
|
57
|
+
|
58
|
+
def type
|
59
|
+
raise "Can't read type of a closed ZSocket!" unless @zsocket
|
60
|
+
LibCZMQ.zsocket_type_str(@zsocket)
|
61
|
+
end
|
62
|
+
|
63
|
+
def receive_string(*opts)
|
64
|
+
if opts.include? :no_wait
|
65
|
+
# NOTE: There is no need to raise exception if zstr_recv_nowait returns
|
66
|
+
# NULL. That's a perfectly fine result, meaning that we don't have any
|
67
|
+
# strings in the CZMQ RX buffers.
|
68
|
+
LibCZMQ.zstr_recv_nowait(@zsocket)
|
69
|
+
else
|
70
|
+
str = LibCZMQ.zstr_recv(@zsocket)
|
71
|
+
# TODO: Do we really need to raise an exception if the string is nil?
|
72
|
+
raise "Can't read string from ZSocket" if str.nil?
|
73
|
+
str
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def receive_strings
|
78
|
+
strings = []
|
79
|
+
zmsg = self.receive_message
|
80
|
+
str = zmsg.pop(:string)
|
81
|
+
while str
|
82
|
+
strings << str
|
83
|
+
str = zmsg.pop(:string)
|
84
|
+
end
|
85
|
+
strings
|
86
|
+
end
|
87
|
+
|
88
|
+
def send_string(str, *opts)
|
89
|
+
if opts.include? :more
|
90
|
+
LibCZMQ.zstr_sendm(@zsocket, str)
|
91
|
+
# TODO: check the code returned by zstr_sendm?
|
92
|
+
elsif opts.include? :multipart
|
93
|
+
# TODO: call zstr_sendx
|
94
|
+
else
|
95
|
+
LibCZMQ.zstr_send(@zsocket, str)
|
96
|
+
# TODO: check the code returned by zstr_send?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def receive_message
|
101
|
+
ZMessage.new(LibCZMQ.zmsg_recv(@zsocket))
|
102
|
+
end
|
103
|
+
|
104
|
+
def send_message(zmsg)
|
105
|
+
zmsg.__send_over(@zsocket)
|
106
|
+
end
|
107
|
+
|
108
|
+
# TODO: implement this
|
109
|
+
# def sendmem
|
110
|
+
# raise "Can't sendmem to a closed ZSocket!" unless @zsocket
|
111
|
+
# end
|
112
|
+
|
113
|
+
def __get_zsocket_pointer__
|
114
|
+
@zsocket
|
115
|
+
end
|
116
|
+
|
117
|
+
alias_method :to_ptr, :__get_zsocket_pointer__
|
118
|
+
|
119
|
+
def to_pollitem(polling_type=CZMQ::POLLIN)
|
120
|
+
raise "Can't convert an uninitialized/closed ZSocket to a pollitem!" unless @zsocket
|
121
|
+
# TODO: check what to do in case we have a pollitem with a different poll type
|
122
|
+
LibCZMQ.create_pollitem(socket: @zsocket, events: polling_type)
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# After object destruction, make sure that the corresponding zsocket is
|
129
|
+
# destroyed as well.
|
130
|
+
#
|
131
|
+
# NOTE: We don't care about ensuring, as ffi-rmzq does, that the resource
|
132
|
+
# deallocation request comes from the process that allocated it in the first place.
|
133
|
+
# In fact, the CZMQ documentation at http://zeromq.org/area:faq explicitly states
|
134
|
+
# "It is not safe to share a context or sockets between a parent and its child."
|
135
|
+
def setup_finalizer
|
136
|
+
ObjectSpace.define_finalizer(self, self.class.close_zsocket(@zctx, @zsocket))
|
137
|
+
end
|
138
|
+
|
139
|
+
def remove_finalizer
|
140
|
+
ObjectSpace.undefine_finalizer self
|
141
|
+
end
|
142
|
+
|
143
|
+
# Need to make this a class method, or the deallocation won't take place. See:
|
144
|
+
# http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
|
145
|
+
def self.close_zsocket(zctx, zsocket)
|
146
|
+
Proc.new do
|
147
|
+
LibCZMQ.zsocket_destroy(zctx, zsocket)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: czmq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mauro Tortonesi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: czmq-ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
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: Ruby bindings for CZMQ
|
56
|
+
email:
|
57
|
+
- mauro.tortonesi@unife.it
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- czmq.gemspec
|
68
|
+
- examples/simple_dealerrep_example/dealer_node.rb
|
69
|
+
- examples/simple_dealerrep_example/rep_node_7000.rb
|
70
|
+
- examples/simple_dealerrep_example/rep_node_8000.rb
|
71
|
+
- examples/simple_dealerrep_example/rep_node_9000.rb
|
72
|
+
- examples/simple_reqrep_example/client.rb
|
73
|
+
- examples/simple_reqrep_example/server.rb
|
74
|
+
- examples/simple_zbeacon_example/client.rb
|
75
|
+
- examples/simple_zbeacon_example/server.rb
|
76
|
+
- examples/zloop_reqrep_example/client.rb
|
77
|
+
- examples/zloop_reqrep_example/server.rb
|
78
|
+
- examples/zloop_timer_example/zloop_timer.rb
|
79
|
+
- lib/czmq.rb
|
80
|
+
- lib/czmq/context.rb
|
81
|
+
- lib/czmq/version.rb
|
82
|
+
- lib/czmq/zbeacon.rb
|
83
|
+
- lib/czmq/zframe.rb
|
84
|
+
- lib/czmq/zloop.rb
|
85
|
+
- lib/czmq/zmsg.rb
|
86
|
+
- lib/czmq/zpoller.rb
|
87
|
+
- lib/czmq/zsocket.rb
|
88
|
+
homepage: https://github.com/mtortonesi/ruby-czmq
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.2.2
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Ruby gem that provides bindings for the CZMQ library. This is a pure Ruby
|
112
|
+
gem that interfaces with CZMQ using FFI, so it should work under MRI, JRuby, and
|
113
|
+
Rubinius.
|
114
|
+
test_files: []
|