em-xs 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.
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: []