czmq 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []