em-xs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ Gemfile.lock
5
+ coverage/
6
+ doc/
7
+ tests/
8
+
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+
7
+ group(:test) do
8
+ gem 'schmurfy-bacon', '~> 1.4.0'
9
+ gem 'mocha', '~> 0.10.0'
10
+ gem 'simplecov'
11
+ gem 'guard'
12
+ gem 'guard-bacon'
13
+ gem 'rb-fsevent'
14
+ gem 'growl'
15
+ end
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+
2
+ # parameters:
3
+ # output => the formatted to use
4
+ # backtrace => number of lines, nil = everything
5
+ guard 'bacon', :output => "BetterOutput", :backtrace => nil do
6
+ watch(%r{^lib/em-xs/(.+)\.rb$}) { |m| "specs/unit/#{m[1]}_spec.rb" }
7
+ watch(%r{specs/.+\.rb$})
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Julien Ammous
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # em-xs
2
+
3
+ Crossroads EventMachine library, I used em-zeromq as base but turned it into something slightly
4
+ different.
5
+
6
+ ## Prerequesites
7
+
8
+ You need to have libxs installed, on mac os x you can use homebrew:
9
+ ```bash
10
+ brew install crossroads
11
+ ```
12
+
13
+
14
+ ## Example
15
+
16
+ I decided to go for a long example to be closer than a "real" application, here it is:
17
+ you can view the source [here](https://github.com/schmurfy/em-xs/blob/master/examples/req_rep.rb) too
18
+
19
+ ```ruby
20
+ require 'rubygems'
21
+ require 'bundler/setup'
22
+
23
+ require 'em-xs'
24
+
25
+ ENDPOINT = 'inproc://socket'
26
+
27
+
28
+ class Client
29
+ def initialize(context, endpoint, delay)
30
+ @context = context
31
+ @endpoint = endpoint
32
+ @delay = delay
33
+
34
+ @socket = @context.socket(XS::REQ, self)
35
+ if @socket.connect(endpoint) == -1
36
+ raise XS::Util.error_string
37
+ end
38
+
39
+ send_query
40
+ end
41
+
42
+ def on_readable(s, parts)
43
+ puts "Client received: #{parts}"
44
+ send_query
45
+ end
46
+
47
+ private
48
+ def send_query
49
+ EM::add_timer(@delay) do
50
+ @socket.send_msg("ping")
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+
57
+ class EchoServer
58
+ def initialize(context, endpoint)
59
+ @context = context
60
+ @socket = @context.socket(XS::REP, self)
61
+ if @socket.bind(endpoint) == -1
62
+ raise XS::Util.error_string
63
+ end
64
+ end
65
+
66
+ def on_readable(s, parts)
67
+ puts "Server received: #{parts}"
68
+ s.send_msg("re: #{parts[0]}")
69
+ end
70
+
71
+ end
72
+
73
+
74
+ EM::run do
75
+ context = EM::XS::Context.new
76
+
77
+ EchoServer.new(context, ENDPOINT)
78
+ Client.new(context, ENDPOINT, 1)
79
+ end
80
+ ```
81
+
82
+
83
+ ## Contributing
84
+
85
+ first install dependencies (thanks bundler !)
86
+ ```bash
87
+ bundle
88
+ ```
89
+
90
+ then you can run the specs:
91
+
92
+ ```bash
93
+ bundle exec rake
94
+ ```
95
+
96
+ and if you can to do some changes you can start guard to
97
+ watch the files and restart associated specs in the background:
98
+ ```bash
99
+ bundle exec guard
100
+ ```
101
+
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require "bundler/gem_tasks"
4
+
5
+ task :default => :test
6
+
7
+ task :test do
8
+ require 'bacon'
9
+
10
+ ENV['COVERAGE'] = "1"
11
+
12
+ Dir[File.expand_path('../specs/**/*_spec.rb', __FILE__)].each do |file|
13
+ puts "File: #{file}:"
14
+ load(file)
15
+ end
16
+
17
+ end
18
+
data/em-xs.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/em-xs/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Julien Ammous"]
6
+ gem.email = ["schmurfy@gmail.com"]
7
+ gem.description = %q{EventMachine wrapper for crossroads}
8
+ gem.summary = %q{EventMachine wrapper for crossroads}
9
+ gem.homepage = "https://github.com/schmurfy/em-xs"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.name = "em-xs"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = EM::XS::VERSION
16
+
17
+ gem.add_dependency 'ffi-rxs'
18
+ gem.add_dependency 'eventmachine', '~> 1.0.0.beta4'
19
+ end
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'em-xs'
5
+
6
+ ENDPOINT = 'inproc://socket'
7
+
8
+
9
+ class Client
10
+ def initialize(context, endpoint, delay)
11
+ @context = context
12
+ @endpoint = endpoint
13
+ @delay = delay
14
+
15
+ @socket = @context.socket(XS::REQ, self)
16
+ if @socket.connect(endpoint) == -1
17
+ raise XS::Util.error_string
18
+ end
19
+
20
+ send_query
21
+ end
22
+
23
+ def on_readable(s, parts)
24
+ puts "Client received: #{parts}"
25
+ send_query
26
+ end
27
+
28
+ private
29
+ def send_query
30
+ EM::add_timer(@delay) do
31
+ @socket.send_msg("ping")
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+
38
+ class EchoServer
39
+ def initialize(context, endpoint)
40
+ @context = context
41
+ @socket = @context.socket(XS::REP, self)
42
+ if @socket.bind(endpoint) == -1
43
+ raise XS::Util.error_string
44
+ end
45
+ end
46
+
47
+ def on_readable(s, parts)
48
+ puts "Server received: #{parts}"
49
+ s.send_msg("re: #{parts[0]}")
50
+ end
51
+
52
+ end
53
+
54
+
55
+ EM::run do
56
+ context = EM::XS::Context.new
57
+
58
+ EchoServer.new(context, ENDPOINT)
59
+ Client.new(context, ENDPOINT, 1)
60
+ end
data/lib/em-xs.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'eventmachine'
2
+ require 'ffi-rxs'
3
+
4
+ require_relative 'em-xs/version'
5
+ require_relative 'em-xs/context'
6
+
7
+ require_relative 'em-xs/socket'
8
+ require_relative 'em-xs/patterns/req_rep_pattern'
9
+ require_relative 'em-xs/patterns/router_dealer_pattern'
10
+ require_relative 'em-xs/patterns/surveyor_pattern'
11
+ require_relative 'em-xs/patterns/push_pull_pattern'
12
+ require_relative 'em-xs/patterns/pub_sub_pattern'
13
+
14
+ require_relative 'em-xs/helpers'
@@ -0,0 +1,53 @@
1
+ module EventMachine
2
+ module XS
3
+
4
+ class Context
5
+ READABLES = [ ::XS::SUB, ::XS::PULL, ::XS::ROUTER, ::XS::DEALER, ::XS::REP, ::XS::REQ, ::XS::RESPONDENT, ::XS::SURVEYOR ]
6
+ WRITABLES = [ ::XS::PUB, ::XS::PUSH, ::XS::ROUTER, ::XS::DEALER, ::XS::REP, ::XS::REQ, ::XS::RESPONDENT, ::XS::SURVEYOR ]
7
+
8
+ def initialize(io_threads = nil)
9
+ @context = ::XS::Context.new
10
+ if io_threads
11
+ rc = @context.setctxopt(::XS::IO_THREADS, io_threads)
12
+ unless ::XS::Util.resultcode_ok?(rc)
13
+ ::XS::raise_error('xs_setctxopt', rc)
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ ##
20
+ # Create a socket in this context.
21
+ #
22
+ # @param [Integer] socket_type One of ZMQ::REQ, ZMQ::REP, ZMQ::PULL, ZMQ::PUSH,
23
+ # ZMQ::ROUTER, ZMQ::DEALER
24
+ #
25
+ # @param [Object] handler an object which respond to on_readable(socket, parts)
26
+ # and can respond to on_writeable(socket)
27
+ #
28
+ def socket(socket_type, handler = nil)
29
+ klass = Socket.get_class_for_type(socket_type)
30
+ unless klass
31
+ raise "Unsupported socket type: #{socket_type}"
32
+ end
33
+
34
+ socket = @context.socket(socket_type)
35
+
36
+ fd = []
37
+ rc = socket.getsockopt(::XS::FD, fd)
38
+ unless ::XS::Util.resultcode_ok?(rc)
39
+ raise "Unable to get socket FD: #{::XS::Util.error_string}"
40
+ end
41
+
42
+ EM.watch(fd[0], klass, socket, socket_type, handler).tap do |s|
43
+ s.register_readable if READABLES.include?(socket_type)
44
+ s.register_writable if WRITABLES.include?(socket_type)
45
+
46
+ yield(s) if block_given?
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ end
53
+ end
@@ -0,0 +1,24 @@
1
+ module EventMachine
2
+ module XS
3
+
4
+ class Socket
5
+ TYPES_MAPPING = {
6
+ ::XS::REQ => Sockets::Request,
7
+ ::XS::REP => Sockets::Reply,
8
+ ::XS::ROUTER => Sockets::Router,
9
+ ::XS::DEALER => Sockets::Dealer,
10
+ ::XS::SUB => Sockets::Subscriber,
11
+ ::XS::PUB => Sockets::Publisher,
12
+ ::XS::SURVEYOR => Sockets::Surveyor,
13
+ ::XS::RESPONDENT => Sockets::Respondent,
14
+ ::XS::PUSH => Sockets::Push,
15
+ ::XS::PULL => Sockets::Pull
16
+ }.freeze
17
+
18
+ def self.get_class_for_type(socket_type)
19
+ TYPES_MAPPING[socket_type]
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module EventMachine
2
+ module XS
3
+ module Sockets
4
+
5
+ class Publisher < Socket
6
+
7
+ end
8
+
9
+ class Subscriber < Socket
10
+
11
+ def subscribe(what = '')
12
+ raise "only valid on sub socket type (was #{@socket.name})" unless @socket.name == 'SUB'
13
+ @socket.setsockopt(::XS::SUBSCRIBE, what)
14
+ end
15
+
16
+ def unsubscribe(what)
17
+ raise "only valid on sub socket type (was #{@socket.name})" unless @socket.name == 'SUB'
18
+ @socket.setsockopt(::XS::UNSUBSCRIBE, what)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module EventMachine
2
+ module XS
3
+ module Sockets
4
+
5
+ class Push < Socket
6
+ end
7
+
8
+ class Pull < Socket
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module EventMachine
2
+ module XS
3
+ module Sockets
4
+
5
+ class Request < Socket
6
+ end
7
+
8
+ class Reply < Socket
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module EventMachine
2
+ module XS
3
+ module Sockets
4
+
5
+ class Router < Socket
6
+ end
7
+
8
+ class Dealer < Socket
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module EventMachine
2
+ module XS
3
+ module Sockets
4
+
5
+ class Surveyor < Socket
6
+ map_sockopt(::XS::SURVEY_TIMEOUT, :survey_timeout)
7
+
8
+ end
9
+
10
+ class Respondent < Surveyor
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,192 @@
1
+ module EventMachine
2
+ module XS
3
+ class Socket < EventMachine::Connection
4
+ attr_accessor :on_readable, :on_writable, :handler
5
+ attr_reader :socket, :socket_type
6
+
7
+ def initialize(socket, socket_type, handler)
8
+ @socket = socket
9
+ @socket_type = socket_type
10
+ @handler = handler
11
+ end
12
+
13
+ def self.map_sockopt(opt, name, writer = true)
14
+ define_method(name){ getsockopt(opt) }
15
+ if writer
16
+ define_method("#{name}="){|val| @socket.setsockopt(opt, val) }
17
+ end
18
+ end
19
+
20
+ map_sockopt(::XS::AFFINITY, :affinity)
21
+ map_sockopt(::XS::IDENTITY, :identity)
22
+ map_sockopt(::XS::SNDBUF, :sndbuf)
23
+ map_sockopt(::XS::RCVBUF, :rcvbuf)
24
+ map_sockopt(::XS::TYPE, :type, false)
25
+ map_sockopt(::XS::LINGER, :linger)
26
+ map_sockopt(::XS::RECONNECT_IVL, :reconnect_interval)
27
+ map_sockopt(::XS::RECONNECT_IVL_MAX, :max_reconnect_interval)
28
+ map_sockopt(::XS::BACKLOG, :backlog)
29
+ map_sockopt(::XS::MAXMSGSIZE, :max_msgsize)
30
+ map_sockopt(::XS::SNDHWM, :snd_hwm)
31
+ map_sockopt(::XS::RCVHWM, :rcv_hwm)
32
+ map_sockopt(::XS::RCVTIMEO, :rcv_timeout)
33
+ map_sockopt(::XS::SNDTIMEO, :snd_timeout)
34
+ map_sockopt(::XS::IPV4ONLY, :ipv4only)
35
+ map_sockopt(::XS::KEEPALIVE, :keepalive)
36
+
37
+
38
+ # pgm
39
+ map_sockopt(::XS::RATE, :rate)
40
+ map_sockopt(::XS::MULTICAST_HOPS, :max_multicast_hops)
41
+ map_sockopt(::XS::RECOVERY_IVL, :recovery_interval)
42
+
43
+ # User method
44
+ def bind(address)
45
+ @socket.bind(address)
46
+ end
47
+
48
+ def connect(address)
49
+ @socket.connect(address)
50
+ end
51
+
52
+ # send a non blocking message
53
+ # parts: if only one argument is given a signle part message is sent
54
+ # if more than one arguments is given a multipart message is sent
55
+ #
56
+ # return: true is message was queued, false otherwise
57
+ #
58
+ def send_msg(*parts)
59
+ parts = Array(parts[0]) if parts.size == 0
60
+ sent = true
61
+
62
+ # multipart
63
+ parts[0...-1].each do |msg|
64
+ sent = @socket.send_string(msg, ::XS::DONTWAIT | ::XS::SNDMORE)
65
+ if sent == false
66
+ break
67
+ end
68
+ end
69
+
70
+ if sent
71
+ # all the previous parts were queued, send
72
+ # the last one
73
+ ret = @socket.send_string(parts[-1], ::XS::DONTWAIT)
74
+ if ret < 0
75
+ raise "Unable to send message: #{::XS::Util.error_string}"
76
+ end
77
+ else
78
+ # error while sending the previous parts
79
+ # register the socket for writability
80
+ self.notify_writable = true
81
+ sent = false
82
+ end
83
+
84
+ notify_readable()
85
+
86
+ sent
87
+ end
88
+
89
+ def getsockopt(opt)
90
+ ret = []
91
+ rc = @socket.getsockopt(opt, ret)
92
+ unless ::XS::Util.resultcode_ok?(rc)
93
+ ::XS::Util.raise_error('getsockopt', rc)
94
+ end
95
+
96
+ (ret.size == 1) ? ret[0] : ret
97
+ end
98
+
99
+ def setsockopt(opt, value)
100
+ @socket.setsockopt(opt, value)
101
+ end
102
+
103
+ # cleanup when ending loop
104
+ def unbind
105
+ detach_and_close
106
+ end
107
+
108
+ # Make this socket available for reads
109
+ def register_readable
110
+ if readable?
111
+ notify_readable
112
+ end
113
+
114
+ # Subscribe to EM read notifications
115
+ self.notify_readable = true
116
+ end
117
+
118
+ # Trigger on_readable when socket is readable
119
+ def register_writable
120
+ # Subscribe to EM write notifications
121
+ self.notify_writable = true
122
+ end
123
+
124
+ def notify_readable
125
+ # Not sure if this is actually necessary. I suppose it prevents us
126
+ # from having to to instantiate a XS::Message unnecessarily.
127
+ # I'm leaving this is because its in the docs, but it could probably
128
+ # be taken out.
129
+ return unless readable?
130
+
131
+ loop do
132
+ msg_parts = []
133
+ msg = get_message
134
+ if msg
135
+ msg_parts << msg
136
+ while @socket.more_parts?
137
+ msg = get_message
138
+ if msg
139
+ msg_parts << msg
140
+ else
141
+ raise "Multi-part message missing a message!"
142
+ end
143
+ end
144
+
145
+ @handler.on_readable(self, msg_parts)
146
+ else
147
+ break
148
+ end
149
+ end
150
+ end
151
+
152
+ def notify_writable
153
+ return unless writable?
154
+
155
+ # once a writable event is successfully received the socket
156
+ # should be accepting messages again so stop triggering
157
+ # write events
158
+ self.notify_writable = false
159
+
160
+ if @handler.respond_to?(:on_writable)
161
+ @handler.on_writable(self)
162
+ end
163
+ end
164
+ def readable?
165
+ (getsockopt(::XS::EVENTS) & ::XS::POLLIN) == ::XS::POLLIN
166
+ end
167
+
168
+ def writable?
169
+ true
170
+ # return false
171
+ # (getsockopt(::XS::EVENTS) & ::XS::POLLOUT) == ::XS::POLLOUT
172
+ end
173
+
174
+
175
+ private
176
+
177
+ # internal methods
178
+ def get_message
179
+ msg = ""
180
+ msg_recvd = @socket.recv_string(msg, ::XS::DONTWAIT)
181
+ msg_recvd != -1 ? msg : nil
182
+ end
183
+
184
+ # Detaches the socket from the EM loop,
185
+ # then closes the socket
186
+ def detach_and_close
187
+ detach
188
+ @socket.close
189
+ end
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,5 @@
1
+ module EM
2
+ module XS
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'bacon'
5
+
6
+ if ENV['COVERAGE']
7
+ Bacon.allow_focused_run = false
8
+
9
+ require 'simplecov'
10
+ SimpleCov.start do
11
+ add_filter ".*_spec"
12
+ add_filter "/helpers/"
13
+ end
14
+
15
+ end
16
+
17
+ $LOAD_PATH.unshift( File.expand_path('../../lib' , __FILE__) )
18
+ require 'em-xs'
19
+
20
+ require 'bacon/ext/mocha'
21
+ require 'bacon/ext/em'
22
+
23
+ Thread.abort_on_exception = true
24
+
25
+ Bacon.summary_on_exit()
26
+
27
+ ENDPOINTS = {
28
+ 'inproc' => 'inproc://socket',
29
+ 'ipc' => 'ipc:///tmp/socket',
30
+ 'tcp' => 'tcp://127.0.0.1:1234'
31
+ }.freeze
@@ -0,0 +1,32 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'em-xs/context'
4
+
5
+ describe 'Context' do
6
+ before do
7
+ @ctx = EM::XS::Context.new
8
+ end
9
+
10
+ it 'can create socket' do
11
+ s = @ctx.socket(XS::ROUTER)
12
+ s.class.should == EM::XS::Sockets::Router
13
+ s.socket_type.should == XS::ROUTER
14
+ end
15
+
16
+ should 'set io threads count' do
17
+ proc{
18
+ EM::XS::Context.new(2)
19
+ }.should.not.raise
20
+ end
21
+
22
+ should 'raise an error if socket type is unsupported' do
23
+ err = proc {
24
+ @ctx.socket(99)
25
+ }.should.raise(RuntimeError)
26
+
27
+ err.message.should == "Unsupported socket type: 99"
28
+
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,24 @@
1
+ require_relative '../spec_helper'
2
+
3
+ require 'em-xs'
4
+
5
+ describe 'Socket' do
6
+
7
+ should 'return correct types' do
8
+ EM::XS::Socket.get_class_for_type(XS::REQ).should == EM::XS::Sockets::Request
9
+ EM::XS::Socket.get_class_for_type(XS::REP).should == EM::XS::Sockets::Reply
10
+
11
+ EM::XS::Socket.get_class_for_type(XS::ROUTER).should == EM::XS::Sockets::Router
12
+ EM::XS::Socket.get_class_for_type(XS::DEALER).should == EM::XS::Sockets::Dealer
13
+
14
+ EM::XS::Socket.get_class_for_type(XS::PUB).should == EM::XS::Sockets::Publisher
15
+ EM::XS::Socket.get_class_for_type(XS::SUB).should == EM::XS::Sockets::Subscriber
16
+
17
+ EM::XS::Socket.get_class_for_type(XS::SURVEYOR).should == EM::XS::Sockets::Surveyor
18
+ EM::XS::Socket.get_class_for_type(XS::RESPONDENT).should == EM::XS::Sockets::Respondent
19
+
20
+ EM::XS::Socket.get_class_for_type(XS::PUSH).should == EM::XS::Sockets::Push
21
+ EM::XS::Socket.get_class_for_type(XS::PULL).should == EM::XS::Sockets::Pull
22
+ end
23
+
24
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'Pattern: Publisher/Subscriber' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ describe 'PUB/SUB' do
9
+ before do
10
+ @client_handler = stub()
11
+ @server1_handler = stub()
12
+ @server2_handler = stub()
13
+ @client = @ctx.socket(XS::PUB, @client_handler)
14
+ @server1 = @ctx.socket(XS::SUB, @server1_handler)
15
+ @server2 = @ctx.socket(XS::SUB, @server2_handler)
16
+ end
17
+
18
+ after do
19
+ @client.send(:detach_and_close)
20
+ @server1.send(:detach_and_close)
21
+ @server2.send(:detach_and_close)
22
+ end
23
+
24
+ ENDPOINTS.each do |label, endpoint|
25
+ should "works with #{label} endpoints" do
26
+ @client.bind(endpoint).should >= 0
27
+ @server1.connect(endpoint).should >= 0
28
+ @server2.connect(endpoint).should >= 0
29
+
30
+ @server1.subscribe('')
31
+ @server2.subscribe('')
32
+
33
+ @server1_handler.expects(:on_readable).with(@server1, ['1: some message'])
34
+ @server2_handler.expects(:on_readable).with(@server2, ['3: some message'])
35
+
36
+ EM::add_timer(0.1) do
37
+ @client.send_msg("1: some message")
38
+ # @client.send_msg("2: some message")
39
+ end
40
+
41
+ wait(0.2)
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'Pattern: Push/Pull' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ describe 'PUSH/PULL' do
9
+ before do
10
+ @client_handler = stub()
11
+ @server_handler = stub()
12
+ @client = @ctx.socket(XS::PUSH, @client_handler)
13
+ @server = @ctx.socket(XS::PULL, @server_handler)
14
+ end
15
+
16
+ after do
17
+ @client.send(:detach_and_close)
18
+ @server.send(:detach_and_close)
19
+ end
20
+
21
+ ENDPOINTS.each do |label, endpoint|
22
+ should "works with #{label} endpoints" do
23
+ @server.bind(endpoint).should >= 0
24
+ @client.connect(endpoint).should >= 0
25
+
26
+ @server_handler.expects(:on_readable).with(@server, ['some message'])
27
+ @client.send_msg("some message")
28
+
29
+ wait(0.1)
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'Pattern: Request/Reply' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ describe 'REQ/REP' do
9
+ before do
10
+ @client_handler = stub()
11
+ @server_handler = stub()
12
+ @client = @ctx.socket(XS::REQ, @client_handler)
13
+ @server = @ctx.socket(XS::REP, @server_handler)
14
+ end
15
+
16
+ after do
17
+ @client.send(:detach_and_close)
18
+ @server.send(:detach_and_close)
19
+ end
20
+
21
+ ENDPOINTS.each do |label, endpoint|
22
+ should "works with #{label} endpoints" do
23
+ @server.bind(endpoint).should >= 0
24
+ @client.connect(endpoint).should >= 0
25
+
26
+ @server_handler.expects(:on_readable).with(@server, ['hello server'])
27
+ @client.send_msg("hello server")
28
+
29
+ EM::add_timer(0.1) do
30
+ @client_handler.expects(:on_readable).with(@client, ['yo !'])
31
+ @server.send_msg("yo !")
32
+ end
33
+
34
+ wait(0.3)
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
@@ -0,0 +1,45 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'Pattern: Router/Dealer' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ describe 'XREQ/XREP' do
9
+ before do
10
+ @client_handler = stub()
11
+
12
+ @server_handler = Class.new do
13
+ def on_readable(sock, parts)
14
+ id, *parts = parts
15
+ sock.send_msg(id, "re: #{parts[0]}")
16
+ end
17
+ end.new
18
+
19
+ @client = @ctx.socket(XS::XREQ, @client_handler)
20
+ @server = @ctx.socket(XS::XREP, @server_handler)
21
+ end
22
+
23
+ after do
24
+ @client.send(:detach_and_close)
25
+ @server.send(:detach_and_close)
26
+ end
27
+
28
+ ENDPOINTS.each do |label, endpoint|
29
+ should "works with #{label} endpoints" do
30
+ @server.bind(endpoint).should >= 0
31
+ @client.connect(endpoint).should >= 0
32
+
33
+ @client_handler.expects(:on_readable).with(@client, ['re: first question'])
34
+ @client.send_msg("first question")
35
+
36
+ @client_handler.expects(:on_readable).with(@client, ['re: second question'])
37
+ @client.send_msg("second question")
38
+
39
+ wait(0.2)
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,47 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe 'Pattern: Surveyor/Respondent' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ describe 'SURVEYOR/RESPONDENT' do
9
+ before do
10
+ @client_handler = stub()
11
+
12
+ @server_handler_class = Class.new do
13
+ def initialize(id)
14
+ @id = id
15
+ end
16
+
17
+ def on_readable(sock, parts)
18
+ p parts
19
+ sock.send_msg("#{parts[0]} : #{@id}")
20
+ end
21
+ end
22
+
23
+ @server1 = @ctx.socket(XS::RESPONDENT, @server_handler)
24
+ @server2 = @ctx.socket(XS::RESPONDENT, @server_handler)
25
+ @client = @ctx.socket(XS::SURVEYOR, @client_handler)
26
+ end
27
+
28
+ # => CRASH
29
+ # ENDPOINTS.each do |label, endpoint|
30
+ # should "works with #{label} endpoints" do
31
+ # @server1.connect(endpoint).should == 0
32
+ # @server2.connect(endpoint).should == 0
33
+ # @client.bind(endpoint).should == 0
34
+ #
35
+ # @client_handler.expects(:on_readable).with(@server, ['msg : 1'])
36
+ # @client_handler.expects(:on_readable).with(@server, ['msg : 2'])
37
+ # @client.send_msg("msg")
38
+ #
39
+ #
40
+ # wait(0.2)
41
+ # end
42
+ # end
43
+
44
+ end
45
+
46
+ end
47
+
@@ -0,0 +1,9 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'Socket' do
4
+ before do
5
+ @ctx = EM::XS::Context.new
6
+ end
7
+
8
+ end
9
+
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-xs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Ammous
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi-rxs
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: eventmachine
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.0.beta4
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0.beta4
46
+ description: EventMachine wrapper for crossroads
47
+ email:
48
+ - schmurfy@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - Guardfile
56
+ - LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - em-xs.gemspec
60
+ - examples/req_rep.rb
61
+ - lib/em-xs.rb
62
+ - lib/em-xs/context.rb
63
+ - lib/em-xs/helpers.rb
64
+ - lib/em-xs/patterns/pub_sub_pattern.rb
65
+ - lib/em-xs/patterns/push_pull_pattern.rb
66
+ - lib/em-xs/patterns/req_rep_pattern.rb
67
+ - lib/em-xs/patterns/router_dealer_pattern.rb
68
+ - lib/em-xs/patterns/surveyor_pattern.rb
69
+ - lib/em-xs/socket.rb
70
+ - lib/em-xs/version.rb
71
+ - specs/spec_helper.rb
72
+ - specs/unit/context_spec.rb
73
+ - specs/unit/helpers_spec.rb
74
+ - specs/unit/patterns/pub_sub_pattern_spec.rb
75
+ - specs/unit/patterns/push_pull_pattern_spec.rb
76
+ - specs/unit/patterns/req_rep_pattern_spec.rb
77
+ - specs/unit/patterns/router_dealer_pattern_spec.rb
78
+ - specs/unit/patterns/surveyor_pattern_spec.rb
79
+ - specs/unit/socket_spec.rb
80
+ homepage: https://github.com/schmurfy/em-xs
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ segments:
93
+ - 0
94
+ hash: 2028971236709690266
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: 2028971236709690266
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.22
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: EventMachine wrapper for crossroads
110
+ test_files: []