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.
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby-czmq.gemspec
4
+ gemspec
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.
@@ -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
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'czmq-ffi'
2
+
3
+ require 'czmq/context'
4
+ require 'czmq/version'
5
+ require 'czmq/zbeacon'
6
+ require 'czmq/zframe'
7
+ require 'czmq/zmsg'
8
+ require 'czmq/zloop'
9
+ require 'czmq/zsocket'
@@ -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
@@ -0,0 +1,3 @@
1
+ module CZMQ
2
+ VERSION = "0.0.1"
3
+ end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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: []