em-zeromq-mri 0.3.0.pre

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.
data/.bnsignore ADDED
@@ -0,0 +1,18 @@
1
+ # The list of files that should be ignored by Mr Bones.
2
+ # Lines that start with '#' are comments.
3
+ #
4
+ # A .gitignore file can be used instead by setting it as the ignore
5
+ # file in your Rakefile:
6
+ #
7
+ # Bones {
8
+ # ignore_file '.gitignore'
9
+ # }
10
+ #
11
+ # For a project with a C extension, the following would be a good set of
12
+ # exclude patterns (uncomment them if you want to use them):
13
+ # *.[oa]
14
+ # *~
15
+ announcement.txt
16
+ coverage
17
+ doc
18
+ pkg
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *.swp
2
+ *.swo
3
+ *.swn
4
+
5
+ *.rbc
6
+ *~
7
+
8
+ pkg
9
+
10
+ *.gem
11
+ .bundle
12
+ Gemfile.lock
13
+ pkg/*
14
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in em-zeromq.gemspec
4
+ gemspec :name => "em-zeromq"
5
+ gemspec :name => "em-zeromq-mri", :platform => :mri
6
+
7
+ gem "ffi", :platforms => [:jruby, :mri]
8
+
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2011-01-29
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # em-zeromq #
2
+
3
+ ## Description: ##
4
+
5
+ EventMachine support for ZeroMQ
6
+
7
+ ## Usage: ##
8
+
9
+ Tested and functional with Rubinius, jRuby, and MRI 1.9.2.
10
+
11
+ MRI 1.8.7 is not supported because of it's poor thread support.
12
+
13
+ The code ships as two alternative gems:
14
+
15
+ * em-zeromq (based on the ffi-rzmq gem, available for all platforms)
16
+ * em-zeromq-mri (based on the zmq gem, which is only available for MRI rubies)
17
+
18
+ The main reason for shipping two different gems is that rubygems version requirements
19
+ don't support the specification dependent on the ruby being used to install the gem.
20
+
21
+ If you are using MRI 1.9.2 and insist on using the ffi version, you must ensure that the
22
+ ffi gem is installed for ffi-zeromq to work.
23
+
24
+ Want to help out? Ask!
25
+
26
+ ## Requiring the code
27
+ Either
28
+
29
+ ```ruby
30
+ require 'em-zeromq-mri'
31
+ ```
32
+ or
33
+
34
+ ```ruby
35
+ require 'zmq'
36
+ require 'em-zeromq'
37
+ ```
38
+
39
+ will work.
40
+
41
+ If you prefer the ffi version:
42
+
43
+ ```ruby
44
+ require 'em-zeromq-ffi'
45
+ ```
46
+ or
47
+
48
+ ```ruby
49
+ require 'ffi-rzmq'
50
+ require 'em-zeromq'
51
+ ```
52
+ will do the trick.
53
+
54
+ ## Example ##
55
+ ```ruby
56
+ Thread.abort_on_exception = true
57
+
58
+ class EMTestPullHandler
59
+ attr_reader :received
60
+ def on_readable(socket, messages)
61
+ messages.each do |m|
62
+ puts m.copy_out_string
63
+ end
64
+ end
65
+ end
66
+
67
+ EM.run do
68
+ ctx = EM::ZeroMQ::Context.new(1)
69
+
70
+ # setup push sockets
71
+ push_socket1 = ctx.bind( ZMQ::PUSH, 'tcp://127.0.0.1:2091')
72
+ push_socket2 = ctx.bind( ZMQ::PUSH, 'ipc:///tmp/a')
73
+ push_socket3 = ctx.bind( ZMQ::PUSH, 'inproc://simple_test')
74
+
75
+ # setup one pull sockets listening to both push sockets
76
+ pull_socket = ctx.connect( ZMQ::PULL, 'tcp://127.0.0.1:2091', EMTestPullHandler.new)
77
+ pull_socket.connect('ipc:///tmp/a')
78
+ pull_socket.connect('inproc://simple_test')
79
+
80
+ n = 0
81
+
82
+ # push_socket.hwm = 40
83
+ # puts push_socket.hwm
84
+ # puts pull_socket.hwm
85
+
86
+ EM::PeriodicTimer.new(0.1) do
87
+ puts '.'
88
+ push_socket1.send_msg("t#{n += 1}_")
89
+ push_socket2.send_msg("i#{n += 1}_")
90
+ push_socket3.send_msg("p#{n += 1}_")
91
+ end
92
+ end
93
+ ```
94
+ ## License: ##
95
+
96
+ (The MIT License)
97
+
98
+ Copyright (c) 2011
99
+
100
+ Permission is hereby granted, free of charge, to any person obtaining
101
+ a copy of this software and associated documentation files (the
102
+ 'Software'), to deal in the Software without restriction, including
103
+ without limitation the rights to use, copy, modify, merge, publish,
104
+ distribute, sublicense, and/or sell copies of the Software, and to
105
+ permit persons to whom the Software is furnished to do so, subject to
106
+ the following conditions:
107
+
108
+ The above copyright notice and this permission notice shall be
109
+ included in all copies or substantial portions of the Software.
110
+
111
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
112
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
113
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
114
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
115
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
116
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
117
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ namespace :ffi do
5
+ Bundler::GemHelper.install_tasks :name => 'em-zeromq'
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.rspec_opts = ['--color']
8
+ t.pattern = "spec/**/*_{spec,ffi}.rb"
9
+ end
10
+ end
11
+
12
+ namespace :mri do
13
+ Bundler::GemHelper.install_tasks :name => 'em-zeromq-mri'
14
+ RSpec::Core::RakeTask.new(:spec) do |t|
15
+ t.rspec_opts = ['--color']
16
+ t.pattern = "spec/**/*_{spec,mri}.rb"
17
+ end
18
+ end
19
+
20
+ [:spec, :build, :install, :release].each do |t|
21
+ desc "#{t} all versions"
22
+ task t => ["ffi:#{t}", "mri:#{t}"]
23
+ end
24
+
25
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-zeromq/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "em-zeromq-mri"
7
+ s.version = EmZeromq::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Andrew Cholakian", "Stefan Kaes"]
10
+ s.email = ["andrew@andrewvc.com", "skaes@railsexpress.de"]
11
+ s.homepage = "https://github.com/andrewvc/em-zeromq"
12
+ s.summary = %q{Low level event machine support for ZeroMQ}
13
+ s.description = %q{Low level event machine support for ZeroMQ}
14
+ s.rdoc_options = ["--main", "README.md"]
15
+
16
+ s.rubyforge_project = "em-zeromq"
17
+
18
+ s.add_dependency 'eventmachine', '>= 1.0.0.beta.4'
19
+ s.add_dependency 'zmq', '>= 2.1.4'
20
+
21
+ s.add_development_dependency 'rspec', '>= 2.5.0'
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
data/em-zeromq.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-zeromq/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "em-zeromq"
7
+ s.version = EmZeromq::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Andrew Cholakian", "Stefan Kaes"]
10
+ s.email = ["andrew@andrewvc.com", "skaes@railsexpress.de"]
11
+ s.homepage = "https://github.com/andrewvc/em-zeromq"
12
+ s.summary = %q{Low level event machine support for ZeroMQ}
13
+ s.description = %q{Low level event machine support for ZeroMQ}
14
+ s.rdoc_options = ["--main", "README.md"]
15
+
16
+ s.rubyforge_project = "em-zeromq"
17
+
18
+ s.add_dependency 'eventmachine', '>= 1.0.0.beta.4'
19
+ s.add_dependency 'ffi-rzmq', '>= 0.8.2'
20
+
21
+ s.add_development_dependency 'rspec', '>= 2.5.0'
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
data/example/simple.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
3
+ require 'em-zeromq'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ class EMTestPullHandler
8
+ attr_reader :received
9
+ def on_readable(socket, messages)
10
+ messages.each do |m|
11
+ puts m.copy_out_string
12
+ end
13
+ end
14
+ end
15
+
16
+ EM.run do
17
+ ctx = EM::ZeroMQ::Context.new(1)
18
+
19
+ # setup push sockets
20
+ push_socket1 = ctx.bind( ZMQ::PUSH, 'tcp://127.0.0.1:2091')
21
+ push_socket2 = ctx.bind( ZMQ::PUSH, 'ipc:///tmp/a')
22
+ push_socket3 = ctx.bind( ZMQ::PUSH, 'inproc://simple_test')
23
+
24
+ # setup one pull sockets listening to both push sockets
25
+ pull_socket = ctx.connect( ZMQ::PULL, 'tcp://127.0.0.1:2091', EMTestPullHandler.new)
26
+ pull_socket.connect('ipc:///tmp/a')
27
+ pull_socket.connect('inproc://simple_test')
28
+
29
+ n = 0
30
+
31
+ # push_socket.hwm = 40
32
+ # puts push_socket.hwm
33
+ # puts pull_socket.hwm
34
+
35
+ EM::PeriodicTimer.new(0.1) do
36
+ puts '.'
37
+ push_socket1.send_msg("t#{n += 1}_")
38
+ push_socket2.send_msg("i#{n += 1}_")
39
+ push_socket3.send_msg("p#{n += 1}_")
40
+ end
41
+ end
@@ -0,0 +1,179 @@
1
+ module EventMachine
2
+ module ZeroMQ
3
+ class Connection < EventMachine::Connection
4
+ attr_accessor :on_readable, :on_writable, :handler
5
+ attr_reader :socket, :socket_type, :address
6
+
7
+ def initialize(socket, socket_type, address, handler)
8
+ @socket = socket
9
+ @socket_type = socket_type
10
+ @handler = handler
11
+ @address = address
12
+ end
13
+
14
+ def self.map_sockopt(opt, name)
15
+ define_method(name){ @socket.getsockopt(opt) }
16
+ define_method("#{name}="){|val| @socket.setsockopt(opt, val) }
17
+ end
18
+
19
+ map_sockopt(ZMQ::HWM, :hwm)
20
+ map_sockopt(ZMQ::SWAP, :swap)
21
+ map_sockopt(ZMQ::IDENTITY, :identity)
22
+ map_sockopt(ZMQ::AFFINITY, :affinity)
23
+ map_sockopt(ZMQ::SNDBUF, :sndbuf)
24
+ map_sockopt(ZMQ::RCVBUF, :rcvbuf)
25
+
26
+ # pgm
27
+ map_sockopt(ZMQ::RATE, :rate)
28
+ map_sockopt(ZMQ::RECOVERY_IVL, :recovery_ivl)
29
+ map_sockopt(ZMQ::MCAST_LOOP, :mcast_loop)
30
+
31
+ # User method
32
+ def bind(address)
33
+ @socket.bind(address)
34
+ end
35
+
36
+ def connect(address)
37
+ @socket.connect(address)
38
+ end
39
+
40
+ def subscribe(what = '')
41
+ raise "only valid on sub socket type (was #{@socket.name})" unless @socket.name == 'SUB'
42
+ @socket.setsockopt(ZMQ::SUBSCRIBE, what)
43
+ end
44
+
45
+ def unsubscribe(what)
46
+ raise "only valid on sub socket type (was #{@socket.name})" unless @socket.name == 'SUB'
47
+ @socket.setsockopt(ZMQ::UNSUBSCRIBE, what)
48
+ end
49
+
50
+ # send a non blocking message
51
+ # parts: if only one argument is given a signle part message is sent
52
+ # if more than one arguments is given a multipart message is sent
53
+ #
54
+ # return: true is message was queued, false otherwise
55
+ #
56
+ def send_msg(*parts)
57
+ parts = Array(parts[0]) if parts.size == 0
58
+ sent = true
59
+
60
+ # multipart
61
+ parts[0...-1].each do |msg|
62
+ sent = @socket.send_string(msg, ZMQ::NOBLOCK | ZMQ::SNDMORE)
63
+ if sent == false
64
+ break
65
+ end
66
+ end
67
+
68
+ if sent
69
+ # all the previous parts were queued, send
70
+ # the last one
71
+ @socket.send_string(parts[-1], ZMQ::NOBLOCK)
72
+ else
73
+ # error while sending the previous parts
74
+ # register the socket for writability
75
+ self.notify_writable = true
76
+ false
77
+ end
78
+ end
79
+
80
+ def setsockopt(opt, value)
81
+ @socket.setsockopt(opt, value)
82
+ end
83
+
84
+ # cleanup when ending loop
85
+ def unbind
86
+ detach_and_close
87
+ end
88
+
89
+ # Make this socket available for reads
90
+ def register_readable
91
+ # Since ZMQ is event triggered I think this is necessary
92
+ if readable?
93
+ notify_readable
94
+ end
95
+ # Subscribe to EM read notifications
96
+ self.notify_readable = true
97
+ end
98
+
99
+ # Trigger on_readable when socket is readable
100
+ def register_writable
101
+ # Subscribe to EM write notifications
102
+ self.notify_writable = true
103
+ end
104
+
105
+ def notify_readable
106
+ # Not sure if this is actually necessary. I suppose it prevents us
107
+ # from having to to instantiate a ZMQ::Message unnecessarily.
108
+ # I'm leaving this is because its in the docs, but it could probably
109
+ # be taken out.
110
+ return unless readable?
111
+
112
+ loop do
113
+ msg_parts = []
114
+ msg = get_message
115
+ if msg
116
+ msg_parts << msg
117
+ while @socket.more_parts?
118
+ msg = get_message
119
+ if msg
120
+ msg_parts << msg
121
+ else
122
+ raise "Multi-part message missing a message!"
123
+ end
124
+ end
125
+
126
+ @handler.on_readable(self, msg_parts)
127
+ else
128
+ break
129
+ end
130
+ end
131
+ end
132
+
133
+ def notify_writable
134
+ return unless writable?
135
+
136
+ # one a writable event is successfully received the socket
137
+ # should be accepting messages again so stop triggering
138
+ # write events
139
+ self.notify_writable = false
140
+
141
+ if @handler.respond_to?(:on_writable)
142
+ @handler.on_writable(self)
143
+ end
144
+ end
145
+ def readable?
146
+ (@socket.getsockopt(ZMQ::EVENTS) & ZMQ::POLLIN) == ZMQ::POLLIN
147
+ end
148
+
149
+ def writable?
150
+ return true
151
+ # ZMQ::EVENTS has issues in ZMQ HEAD, we'll ignore this till they're fixed
152
+ # (@socket.getsockopt(ZMQ::EVENTS) & ZMQ::POLLOUT) == ZMQ::POLLOUT
153
+ end
154
+
155
+ private
156
+
157
+ # internal methods
158
+
159
+ if defined?(ZMQ::Message)
160
+ def get_message
161
+ msg = ZMQ::Message.new
162
+ msg_recvd = @socket.recv(msg, ZMQ::NOBLOCK)
163
+ msg_recvd ? msg : nil
164
+ end
165
+ else
166
+ def get_message
167
+ @socket.recv(ZMQ::NOBLOCK)
168
+ end
169
+ end
170
+
171
+ # Detaches the socket from the EM loop,
172
+ # then closes the socket
173
+ def detach_and_close
174
+ detach
175
+ @socket.close
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,86 @@
1
+ #
2
+ # different ways to create a socket:
3
+ # ctx.bind(:router, 'tcp://127.0.0.1:6666')
4
+ # ctx.bind('router', 'tcp://127.0.0.1:6666')
5
+ # ctx.bind(ZMQ::ROUTER, 'tcp://127.0.0.1:6666')
6
+ #
7
+ module EventMachine
8
+ module ZeroMQ
9
+ class Context
10
+ READABLES = [ ZMQ::SUB, ZMQ::PULL, ZMQ::ROUTER, ZMQ::DEALER, ZMQ::REP, ZMQ::REQ ]
11
+ WRITABLES = [ ZMQ::PUB, ZMQ::PUSH, ZMQ::ROUTER, ZMQ::DEALER, ZMQ::REP, ZMQ::REQ ]
12
+ def initialize(threads_or_context)
13
+ if threads_or_context.is_a?(ZMQ::Context)
14
+ @context = threads_or_context
15
+ else
16
+ @context = ZMQ::Context.new(threads_or_context)
17
+ end
18
+ end
19
+
20
+ def socket(socket_type)
21
+ socket_type = find_type(socket_type)
22
+ @context.socket(socket_type)
23
+ end
24
+
25
+ def bind(socket_or_type, address, handler = nil, opts = {})
26
+ socket, type = create(socket_or_type, opts)
27
+ socket.bind(address)
28
+ watch(socket, type, address, handler, opts)
29
+ end
30
+
31
+ def connect(socket_or_type, address, handler = nil, opts = {})
32
+ socket, type = create(socket_or_type, opts)
33
+ socket.connect(address)
34
+ watch(socket, type, address, handler, opts)
35
+ end
36
+
37
+ def watch(socket, socket_type, address, handler, opts = {})
38
+ fd = socket.getsockopt(ZMQ::FD)
39
+ conn = EM.watch(fd, EventMachine::ZeroMQ::Connection, socket, socket_type, address, handler)
40
+
41
+ if READABLES.include?(socket_type)
42
+ conn.register_readable
43
+ end
44
+
45
+ if WRITABLES.include?(socket_type)
46
+ conn.register_writable
47
+ end
48
+
49
+ conn
50
+ end
51
+
52
+ private
53
+
54
+ def create(socket_or_type, opts = {})
55
+ if socket_or_type.is_a?(ZMQ::Socket)
56
+ socket = socket_or_type
57
+ type = socket_or_type.getsockopt(ZMQ::TYPE)
58
+ else
59
+ type = find_type(socket_or_type)
60
+ socket = @context.socket(type)
61
+ end
62
+
63
+ ident = opts.delete(:identity)
64
+ if ident
65
+ socket.setsockopt(ZMQ::IDENTITY, ident)
66
+ end
67
+
68
+ unless opts.empty?
69
+ raise "unknown keys: #{opts.keys.join(', ')}"
70
+ end
71
+
72
+ [socket, type]
73
+ end
74
+
75
+ def find_type(type)
76
+ if type.is_a?(Symbol) or type.is_a?(String)
77
+ ZMQ.const_get(type.to_s.upcase)
78
+ else
79
+ type
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ module EmZeromq
3
+ VERSION = "0.3.0.pre"
4
+ end
@@ -0,0 +1,2 @@
1
+ require 'zmq'
2
+ require 'em-zeromq'
@@ -0,0 +1,2 @@
1
+ require 'ffi-rzmq'
2
+ require 'em-zeromq'
data/lib/em-zeromq.rb ADDED
@@ -0,0 +1,111 @@
1
+ require 'eventmachine'
2
+
3
+ # determine which zeromq library to load
4
+ unless defined?(ZMQ)
5
+ begin
6
+ require 'ffi-rzmq'
7
+ rescue LoadError
8
+ require 'zmq'
9
+ end
10
+ end
11
+
12
+ unless defined?(ZMQ::Message)
13
+
14
+ module ZMQ
15
+ POLLIN = 1
16
+ POLLOUT = 2
17
+ POLLERR = 4
18
+
19
+ SocketTypeNameMap = {
20
+ PAIR=>"PAIR",
21
+ PUB=>"PUB",
22
+ SUB=>"SUB",
23
+ REQ=>"REQ",
24
+ REP=>"REP",
25
+ DEALER=>"DEALER",
26
+ ROUTER=>"ROUTER",
27
+ PULL=>"PULL",
28
+ PUSH=>"PUSH",
29
+ #XPUB=>"XPUB",
30
+ #XSUB=>"XSUB"
31
+ }
32
+
33
+ class Socket
34
+ def name
35
+ @name ||= SocketTypeNameMap[getsockopt(TYPE)]
36
+ end
37
+ alias send_string send
38
+
39
+ def more_parts?
40
+ getsockopt(RCVMORE)
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ class String
47
+ def copy_out_string
48
+ self
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ module EmZeromq
55
+
56
+ # :stopdoc:
57
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
58
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
59
+ # :startdoc:
60
+
61
+ # Returns the library path for the module. If any arguments are given,
62
+ # they will be joined to the end of the libray path using
63
+ # <tt>File.join</tt>.
64
+ #
65
+ def self.libpath( *args, &block )
66
+ rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
67
+ if block
68
+ begin
69
+ $LOAD_PATH.unshift LIBPATH
70
+ rv = block.call
71
+ ensure
72
+ $LOAD_PATH.shift
73
+ end
74
+ end
75
+ return rv
76
+ end
77
+
78
+ # Returns the lpath for the module. If any arguments are given,
79
+ # they will be joined to the end of the path using
80
+ # <tt>File.join</tt>.
81
+ #
82
+ def self.path( *args, &block )
83
+ rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
84
+ if block
85
+ begin
86
+ $LOAD_PATH.unshift PATH
87
+ rv = block.call
88
+ ensure
89
+ $LOAD_PATH.shift
90
+ end
91
+ end
92
+ return rv
93
+ end
94
+
95
+ # Utility method used to require all files ending in .rb that lie in the
96
+ # directory below this file that has the same name as the filename passed
97
+ # in. Optionally, a specific _directory_ name can be passed in such that
98
+ # the _filename_ does not have to be equivalent to the directory.
99
+ #
100
+ def self.require_all_libs_relative_to( fname, dir = nil )
101
+ dir ||= ::File.basename(fname, '.*')
102
+ search_me = ::File.expand_path(
103
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
104
+
105
+ Dir.glob(search_me).sort.each {|rb| require rb}
106
+ end
107
+
108
+ end # module EmZeromq
109
+
110
+ EmZeromq.require_all_libs_relative_to(__FILE__)
111
+
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe 'Context' do
4
+ before do
5
+ @ctx = EM::ZeroMQ::Context.new(1)
6
+ end
7
+
8
+ it 'can be created with a context' do
9
+ zmq_ctx = ZMQ::Context.new(1)
10
+ ctx = EM::ZeroMQ::Context.new( zmq_ctx )
11
+ ctx.instance_variable_get('@context').should == zmq_ctx
12
+ end
13
+
14
+ it 'can create socket' do
15
+ EM::run do
16
+ s1 = @ctx.bind(:router, 'tcp://127.0.0.1:5555')
17
+ s2 = @ctx.bind('router', 'tcp://127.0.0.1:5556')
18
+ s3 = @ctx.bind(ZMQ::ROUTER, 'tcp://127.0.0.1:5557')
19
+
20
+ expected_socket_type_name = ZMQ::SocketTypeNameMap[ZMQ::ROUTER]
21
+
22
+ s1.instance_variable_get('@socket').name.should == expected_socket_type_name
23
+ EM::stop_event_loop
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,60 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe EventMachine::ZeroMQ do
4
+ class EMTestSubHandler
5
+ attr_reader :received
6
+ def initialize
7
+ @received = []
8
+ end
9
+ def on_readable(socket, messages)
10
+ @received += messages
11
+ end
12
+ end
13
+
14
+ it "Should instantiate a connection given valid opts" do
15
+ sub_conn = nil
16
+ run_reactor(1) do
17
+ sub_conn = SPEC_CTX.bind(ZMQ::PUB, rand_addr, EMTestSubHandler.new)
18
+ end
19
+ sub_conn.should be_a(EventMachine::ZeroMQ::Connection)
20
+ end
21
+
22
+ describe "sending/receiving a single message via PUB/SUB" do
23
+ before(:all) do
24
+ results = {}
25
+ @test_message = test_message = "TMsg#{rand(999)}"
26
+
27
+ run_reactor(0.5) do
28
+ results[:sub_hndlr] = pull_hndlr = EMTestSubHandler.new
29
+ sub_conn = SPEC_CTX.bind(ZMQ::SUB, rand_addr, pull_hndlr)
30
+ sub_conn.subscribe('')
31
+
32
+ pub_conn = SPEC_CTX.connect(ZMQ::PUB, sub_conn.address, EMTestSubHandler.new)
33
+
34
+ pub_conn.socket.send_string test_message, ZMQ::NOBLOCK
35
+
36
+ EM::Timer.new(0.1) { results[:specs_ran] = true }
37
+ end
38
+
39
+ @results = results
40
+ end
41
+
42
+ it "should run completely" do
43
+ @results[:specs_ran].should be_true
44
+ end
45
+
46
+ it "should receive one message" do
47
+ @results[:sub_hndlr].received.length.should == 1
48
+ end
49
+
50
+ if defined?(ZMQ::Message)
51
+ it "should receive the message as a ZMQ::Message" do
52
+ @results[:sub_hndlr].received.first.should be_a(ZMQ::Message)
53
+ end
54
+ end
55
+
56
+ it "should receive the message intact" do
57
+ @results[:sub_hndlr].received.first.copy_out_string.should == @test_message
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe EventMachine::ZeroMQ do
4
+ class EMTestPullHandler
5
+ attr_reader :received
6
+ def initialize
7
+ @received = []
8
+ end
9
+ def on_readable(socket, messages)
10
+ @received += messages
11
+ end
12
+ end
13
+
14
+ it "Should instantiate a connection given valid opts" do
15
+ pull_conn = nil
16
+ run_reactor do
17
+ pull_conn = SPEC_CTX.bind(ZMQ::PULL, rand_addr, EMTestPullHandler.new)
18
+ end
19
+ pull_conn.should be_a(EventMachine::ZeroMQ::Connection)
20
+ end
21
+
22
+ describe "sending/receiving a single message via PUB/SUB" do
23
+ before(:all) do
24
+ results = {}
25
+ @test_message = test_message = "TMsg#{rand(999)}"
26
+
27
+ run_reactor(0.5) do
28
+ results[:pull_hndlr] = pull_hndlr = EMTestPullHandler.new
29
+ pull_conn = SPEC_CTX.bind(ZMQ::PULL, rand_addr, pull_hndlr)
30
+ push_conn = SPEC_CTX.connect(ZMQ::PUSH, pull_conn.address)
31
+
32
+ push_conn.socket.send_string test_message, ZMQ::NOBLOCK
33
+
34
+ EM::Timer.new(0.1) { results[:specs_ran] = true }
35
+ end
36
+
37
+ @results = results
38
+ end
39
+
40
+ it "should run completely" do
41
+ @results[:specs_ran].should be_true
42
+ end
43
+
44
+ it "should receive the message intact" do
45
+ @results[:pull_hndlr].received.should_not be_empty
46
+ @results[:pull_hndlr].received.first.should be_a(ZMQ::Message) if defined?(ZMQ::Message)
47
+ @results[:pull_hndlr].received.first.copy_out_string.should == @test_message
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,80 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe EventMachine::ZeroMQ do
4
+ class EMTestRouterHandler
5
+ attr_reader :received
6
+ def initialize
7
+ @received = []
8
+ end
9
+ def on_writable(socket)
10
+ end
11
+ def on_readable(socket, messages)
12
+ @received += messages
13
+ end
14
+ end
15
+
16
+ class EMTestDealerHandler
17
+ attr_reader :received
18
+ def initialize(&block)
19
+ @received = []
20
+ @on_writable_callback = block
21
+ end
22
+ def on_writable(socket)
23
+ @on_writable_callback.call(socket) if @on_writable_callback
24
+ end
25
+ def on_readable(socket, messages)
26
+ ident, delim, message = messages.map(&:copy_out_string)
27
+ ident.should == "dealer1"
28
+ @received += [ident, delim, message].map {|s| ZMQ::Message.new(s)}
29
+
30
+ socket.send_msg(ident, delim, "re:#{message}")
31
+ end
32
+ end
33
+
34
+ it "Should instantiate a connection given valid opts for Router/Dealer" do
35
+ router_conn = nil
36
+ run_reactor(1) do
37
+ router_conn = SPEC_CTX.bind(ZMQ::ROUTER, rand_addr, EMTestRouterHandler.new)
38
+ end
39
+ router_conn.should be_a(EventMachine::ZeroMQ::Connection)
40
+ end
41
+
42
+ describe "sending/receiving a single message via Router/Dealer" do
43
+ before(:all) do
44
+ results = {}
45
+ @test_message = test_message = "M#{rand(999)}"
46
+
47
+ run_reactor(0.5) do
48
+ results[:dealer_hndlr] = dealer_hndlr = EMTestDealerHandler.new
49
+ results[:router_hndlr] = router_hndlr = EMTestRouterHandler.new
50
+
51
+ addr = rand_addr
52
+ dealer_conn = SPEC_CTX.bind(ZMQ::DEALER, addr, dealer_hndlr, :identity => "dealer1")
53
+ router_conn = SPEC_CTX.connect(ZMQ::ROUTER, addr, router_hndlr, :identity => "router1")
54
+ router_conn.send_msg('x', test_message)
55
+
56
+ EM::Timer.new(0.1) do
57
+ results[:specs_ran] = true
58
+ end
59
+ end
60
+
61
+ @results = results
62
+ end
63
+
64
+ it "should run completely" do
65
+ @results[:specs_ran].should be_true
66
+ end
67
+
68
+ xit "should receive the message intact on the dealer" do
69
+ @results[:dealer_hndlr].received.should_not be_empty
70
+ @results[:dealer_hndlr].received.last.should be_a(ZMQ::Message) if defined?(ZMQ::Message)
71
+ @results[:dealer_hndlr].received.last.copy_out_string.should == @test_message
72
+ end
73
+
74
+ xit "the router should be echoed its original message" do
75
+ @results[:router_hndlr].received.should_not be_empty
76
+ @results[:router_hndlr].received.last.should be_a(ZMQ::Message) if defined?(ZMQ::Message)
77
+ @results[:router_hndlr].received.last.copy_out_string.should == "re:#{@test_message}"
78
+ end
79
+ end
80
+ end
data/spec/setup_ffi.rb ADDED
@@ -0,0 +1 @@
1
+ # when passed along as part of the tests to run, the spec helper will load ffi-rzmq instead of zmq
data/spec/setup_mri.rb ADDED
@@ -0,0 +1 @@
1
+ # when passed along as part of the tests to run, the spec helper will load zmq instead of ffi-rzmq
@@ -0,0 +1,51 @@
1
+ require 'rspec'
2
+ require 'set'
3
+ Thread.abort_on_exception = true
4
+
5
+ if ARGV.detect{|a| a =~ /setup_mri.rb$/}
6
+ puts "loading zmq"
7
+ require "zmq"
8
+ elsif ARGV.detect{|a| a =~ /setup_ffi.rb$/}
9
+ puts "loading ffi-rzmq"
10
+ require "ffi-rzmq"
11
+ end
12
+
13
+ require File.expand_path(
14
+ File.join(File.dirname(__FILE__), %w[.. lib em-zeromq]))
15
+
16
+ def run_reactor(time=0.2,&block)
17
+ Thread.new do
18
+ EM.run do
19
+ yield
20
+ end
21
+ end
22
+ sleep time
23
+ EM.stop rescue nil
24
+ sleep 0.1
25
+ end
26
+
27
+ USED_RAND_ADDRS = Set.new
28
+ def rand_addr(scheme='tcp')
29
+ addr = nil
30
+ loop do
31
+ case scheme
32
+ when 'tcp'
33
+ addr = "tcp://127.0.0.1:#{rand(10_000) + 20_000}"
34
+ when 'inproc'
35
+ addr = "inproc://testinp-#{rand(10_000) + 20_000}"
36
+ end
37
+
38
+ if USED_RAND_ADDRS.include? addr
39
+ next
40
+ else
41
+ USED_RAND_ADDRS << addr
42
+ break
43
+ end
44
+ end
45
+ addr
46
+ end
47
+
48
+ SPEC_CTX = EM::ZeroMQ::Context.new(1)
49
+ def spec_ctx
50
+ SPEC_CTX
51
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-zeromq-mri
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Cholakian
9
+ - Stefan Kaes
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-10-14 00:00:00.000000000 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: eventmachine
18
+ requirement: &2151446960 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0.beta.4
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *2151446960
27
+ - !ruby/object:Gem::Dependency
28
+ name: zmq
29
+ requirement: &2151445760 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 2.1.4
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *2151445760
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &2151444700 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.5.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *2151444700
49
+ description: Low level event machine support for ZeroMQ
50
+ email:
51
+ - andrew@andrewvc.com
52
+ - skaes@railsexpress.de
53
+ executables: []
54
+ extensions: []
55
+ extra_rdoc_files: []
56
+ files:
57
+ - .bnsignore
58
+ - .gitignore
59
+ - Gemfile
60
+ - History.txt
61
+ - README.md
62
+ - Rakefile
63
+ - em-zeromq-mri.gemspec
64
+ - em-zeromq.gemspec
65
+ - example/simple.rb
66
+ - lib/em-zeromq-c.rb
67
+ - lib/em-zeromq-ffi.rb
68
+ - lib/em-zeromq.rb
69
+ - lib/em-zeromq/connection.rb
70
+ - lib/em-zeromq/context.rb
71
+ - lib/em-zeromq/version.rb
72
+ - spec/context_spec.rb
73
+ - spec/pub_sub_spec.rb
74
+ - spec/push_pull_spec.rb
75
+ - spec/router_dealer_spec.rb
76
+ - spec/setup_ffi.rb
77
+ - spec/setup_mri.rb
78
+ - spec/spec_helper.rb
79
+ has_rdoc: true
80
+ homepage: https://github.com/andrewvc/em-zeromq
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --main
85
+ - README.md
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>'
98
+ - !ruby/object:Gem::Version
99
+ version: 1.3.1
100
+ requirements: []
101
+ rubyforge_project: em-zeromq
102
+ rubygems_version: 1.6.2
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Low level event machine support for ZeroMQ
106
+ test_files:
107
+ - spec/context_spec.rb
108
+ - spec/pub_sub_spec.rb
109
+ - spec/push_pull_spec.rb
110
+ - spec/router_dealer_spec.rb
111
+ - spec/setup_ffi.rb
112
+ - spec/setup_mri.rb
113
+ - spec/spec_helper.rb