czmq 0.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 +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: []
|