brontes3d-amqp 0.6.4.3 → 0.6.7.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/README.md +143 -0
- data/Rakefile +7 -1
- data/amqp.gemspec +9 -6
- data/bacon/client.rb +23 -1
- data/lib/amqp.rb +20 -14
- data/lib/amqp/client.rb +6 -0
- data/lib/amqp/frame.rb +1 -1
- data/lib/amqp/protocol.rb +3 -3
- data/lib/amqp/server.rb +1 -1
- data/lib/amqp/version.rb +10 -0
- data/lib/mq.rb +26 -3
- data/lib/mq/exchange.rb +19 -7
- data/lib/mq/queue.rb +28 -6
- data/lib/mq/rpc.rb +9 -9
- metadata +39 -17
- data/README +0 -128
- data/lib/ext/em.rb +0 -51
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
What AMQP gem is
|
2
|
+
===================
|
3
|
+
|
4
|
+
Simple asynchronous AMQP driver for Ruby/EventMachine
|
5
|
+
This library works with Ruby 1.8, Ruby 1.9, JRuby and Rubinius, and is licensed under [the Ruby License](http://www.ruby-lang.org/en/LICENSE.txt).
|
6
|
+
|
7
|
+
This library was tested primarily with RabbitMQ, although it should be compatible with any
|
8
|
+
server implementing the [AMQP 0-8 spec](http://www.amqp.org/confluence/download/attachments/720900/amqp0-8.pdf).
|
9
|
+
|
10
|
+
Getting started
|
11
|
+
===============
|
12
|
+
|
13
|
+
To use examples with RabbitMQ, first [install the broker](http://www.rabbitmq.com/install.html). If you have Mercurial
|
14
|
+
and Erlang/OTP installed, here is how to do it in 4 lines:
|
15
|
+
|
16
|
+
hg clone http://hg.rabbitmq.com/rabbitmq-codegen
|
17
|
+
hg clone http://hg.rabbitmq.com/rabbitmq-server
|
18
|
+
cd rabbitmq-server
|
19
|
+
make run
|
20
|
+
|
21
|
+
Then have a look at the various bundled examples:
|
22
|
+
|
23
|
+
ruby examples/mq/pingpong.rb # 1-1 communication using amq.direct
|
24
|
+
ruby examples/mq/clock.rb # 1-N communication using amq.fanout
|
25
|
+
ruby examples/mq/stocks.rb # 1-subscriber communication using amq.topic
|
26
|
+
|
27
|
+
ruby examples/mq/multiclock.rb # header based routing (new rabbitmq feature)
|
28
|
+
ruby examples/mq/ack.rb # using ack
|
29
|
+
ruby examples/mq/pop.rb # pop off messages one at a time
|
30
|
+
|
31
|
+
ruby examples/mq/hashtable.rb # simple async rpc layer
|
32
|
+
ruby examples/mq/primes.rb 4 # parallelized prime number generation
|
33
|
+
ruby examples/mq/logger.rb # simple logging api
|
34
|
+
|
35
|
+
For high level API documentation see MQ class.
|
36
|
+
For more details into the lower level AMQP client API, run the simple client example:
|
37
|
+
|
38
|
+
ruby examples/amqp/simple.rb # low-level AMQP api
|
39
|
+
ruby examples/mq/internal.rb # low-level Queue/Exchange api
|
40
|
+
|
41
|
+
Or refer to protocol/doc.txt, which enumerates packets sent between a server and client
|
42
|
+
during a typical session, in both binary and decoded formats.
|
43
|
+
|
44
|
+
How to use AMQP gem with Ruby on Rails, Merb, Sinatra and other web frameworks
|
45
|
+
==============================================================================
|
46
|
+
|
47
|
+
To use AMQP gem from web applications, you would need to have EventMachine reactor running.
|
48
|
+
If you use [Thin](http://code.macournoyer.com/thin/), you are set: Thin uses EventMachine under
|
49
|
+
the hook.
|
50
|
+
|
51
|
+
With other web servers, you need to start EventMachine reactor in it's own thread like this:
|
52
|
+
|
53
|
+
Thread.new { EM.run }
|
54
|
+
|
55
|
+
because otherwise EventMachine will block current thread. Then connect to AMQP broker:
|
56
|
+
|
57
|
+
AMQP.connect(:host => "localhost", :user => "guest", :pass => "guest", :vhost => "/")
|
58
|
+
|
59
|
+
In a Ruby on Rails app, probably the best place for this code is initializer
|
60
|
+
(like config/initializers/amqp.rb). For Merb apps, it is config/init.rb. For
|
61
|
+
Sinatra and pure Rack applications, place it next to other configuration
|
62
|
+
code.
|
63
|
+
|
64
|
+
Same separate thread technique can be used to make EventMachine play nicely with other
|
65
|
+
libraries that would block current thread (like [File::Tail](http://rubygems.org/gems/file-tail)).
|
66
|
+
|
67
|
+
AMQP gem mailing list
|
68
|
+
==============================
|
69
|
+
|
70
|
+
* [AMQP gem mailing list](http://groups.google.com/group/ruby-amqp)
|
71
|
+
* [AMQP gem at GitHub](http://github.com/tmm1/amqp)
|
72
|
+
* [AMQP gem at Gemcutter](http://rubygems.org/gems/amqp)
|
73
|
+
|
74
|
+
Running specifications suite
|
75
|
+
============================
|
76
|
+
|
77
|
+
To run the test suite make sure you have [bacon](http://gemcutter.org/gems/bacon) gem installed and run:
|
78
|
+
|
79
|
+
rake spec
|
80
|
+
|
81
|
+
The lib/amqp/spec.rb file is generated automatically based on the [AMQP specification](http://www.amqp.org/confluence/display/AMQP/AMQP+Specification). To generate it:
|
82
|
+
|
83
|
+
rake codegen
|
84
|
+
|
85
|
+
Credits and more information
|
86
|
+
============================
|
87
|
+
|
88
|
+
(c) 2008—2010 [Aman Gupta](http://github.com/tmm1) (tmm1)
|
89
|
+
|
90
|
+
This project was inspired by [py-amqplib](http://barryp.org/software/py-amqplib/), [rabbitmq](http://rabbitmq.com), [qpid](http://qpid.apache.org/) and [rubbyt](http://github.com/rubbyt/rubbyt).
|
91
|
+
Special thanks to Dmitriy Samovskiy, Ben Hood and Tony Garnock-Jones.
|
92
|
+
|
93
|
+
AMQP brokers
|
94
|
+
------------
|
95
|
+
|
96
|
+
* [RabbitMQ](http://rabbitmq.com) (Rabbit Technologies, Erlang/OTP, MPL)
|
97
|
+
* [ZeroMQ](http://www.zeromq.org) (iMatix/FastMQ/Intel, C++, GPL3)
|
98
|
+
* [OpenAMQ](http://openamq.org) (iMatix, C, GPL2)
|
99
|
+
* [ActiveMQ](http://activemq.apache.org) (Apache Foundation, Java, Apache2)
|
100
|
+
|
101
|
+
AMQP resources
|
102
|
+
--------------
|
103
|
+
|
104
|
+
* Steve Vinoski [explains AMQP](http://steve.vinoski.net/pdf/IEEE-Advanced_Message_Queuing_Protocol.pdf) in his column, Towards Integration
|
105
|
+
|
106
|
+
* John O'Hara on [the history of AMQP](http://www.acmqueue.org/modules.php?name=Content&pa=showpage&pid=485)
|
107
|
+
|
108
|
+
* Dmitriy's [presentation on RabbitMQ/AMQP](http://somic-org.homelinux.org/blog/2008/07/31/slides-for-my-amqprabbitmq-talk/)
|
109
|
+
|
110
|
+
* ZeroMQ's [analysis of the messaging technology market](http://www.zeromq.org/whitepapers:market-analysis)
|
111
|
+
|
112
|
+
* Pieter Hintjens's [background to AMQP](http://www.openamq.org/doc:amqp-background)
|
113
|
+
|
114
|
+
* Barry Pederson's [py-amqplib](http://barryp.org/software/py-amqplib/)
|
115
|
+
|
116
|
+
* Ben Hood on [writing an AMQP client](http://hopper.squarespace.com/blog/2008/6/21/build-your-own-amqp-client.html)
|
117
|
+
|
118
|
+
* Dmitriy Samovskiy introduces [Ruby + QPid + RabbitMQ](http://somic-org.homelinux.org/blog/2008/06/24/ruby-amqp-rabbitmq-example/)
|
119
|
+
|
120
|
+
* Ben Hood's [as3-amqp](http://github.com/0x6e6562/as3-amqp) ([two](http://hopper.squarespace.com/blog/2008/7/4/server-side-as3.html), [three](http://hopper.squarespace.com/blog/2008/3/24/as3-amqp-client-first-cut.html))
|
121
|
+
|
122
|
+
* RabbitMQ's [AMQP protocol code generator](http://hg.rabbitmq.com/rabbitmq-codegen/)
|
123
|
+
|
124
|
+
* Erlang Exchange [presentation on the implementation of RabbitMQ](http://skillsmatter.com/podcast/erlang/presenting-rabbitmq-an-erlang-based-implementatio-nof-amqp) (and on the [LShift blog](http://www.lshift.net/blog/2008/07/01/slides-from-our-erlang-exchange-talk))
|
125
|
+
|
126
|
+
* Jonathan Conway's series on RabbitMQ and using it with Ruby and Merb: [One](http://jaikoo.com/2007/9/4/didn-t-you-get-the-memo), [Two](http://jaikoo.com/2008/2/29/friday-round-up-2008-02-29), [Three](http://jaikoo.com/2008/3/14/oh-hai-rabbitmq), [Four](http://jaikoo.com/2008/3/20/daemonize-rabbitmq)
|
127
|
+
|
128
|
+
* Open Enterprise's series on messaging middleware and AMQP: [Part 1](http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-1.html), [Part 2](http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-2.html), [Part 3](http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-3.html)
|
129
|
+
|
130
|
+
Messaging and distributed systems resources
|
131
|
+
-------------------------------------------
|
132
|
+
|
133
|
+
* [A Critique of the Remote Procedure Call Paradigm](http://www.cs.vu.nl/~ast/publications/euteco-1988.pdf)
|
134
|
+
|
135
|
+
* [A Note on Distributed Computing](http://research.sun.com/techrep/1994/smli_tr-94-29.pdf)
|
136
|
+
|
137
|
+
* [Convenience Over Correctness](http://steve.vinoski.net/pdf/IEEE-Convenience_Over_Correctness.pdf)
|
138
|
+
|
139
|
+
* [Metaprotocol Taxonomy and Communications Patterns](http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp)
|
140
|
+
|
141
|
+
* Joe Armstrong on [Erlang messaging vs RPC](http://armstrongonsoftware.blogspot.com/2008/05/road-we-didnt-go-down.html)
|
142
|
+
|
143
|
+
* [SEDA: scalable internet services using message queues](http://www.eecs.harvard.edu/~mdw/papers/seda-sosp01.pdf)
|
data/Rakefile
CHANGED
@@ -1,16 +1,22 @@
|
|
1
|
+
desc "Generate AMQP specification classes"
|
1
2
|
task :codegen do
|
2
3
|
sh 'ruby protocol/codegen.rb > lib/amqp/spec.rb'
|
3
4
|
sh 'ruby lib/amqp/spec.rb'
|
4
5
|
end
|
5
6
|
|
7
|
+
desc "Run spec suite (uses bacon gem)"
|
6
8
|
task :spec do
|
7
9
|
sh 'bacon lib/amqp.rb'
|
8
10
|
end
|
9
11
|
|
12
|
+
desc "Build the gem"
|
10
13
|
task :gem do
|
11
14
|
sh 'gem build *.gemspec'
|
12
15
|
end
|
13
16
|
|
17
|
+
desc "Synonym for gem"
|
14
18
|
task :pkg => :gem
|
19
|
+
desc "Synonym for gem"
|
15
20
|
task :package => :gem
|
16
|
-
|
21
|
+
|
22
|
+
task :default => :spec
|
data/amqp.gemspec
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
+
require File.expand_path('../lib/amqp/version', __FILE__)
|
2
|
+
|
1
3
|
spec = Gem::Specification.new do |s|
|
2
4
|
s.name = 'amqp'
|
3
|
-
s.version =
|
4
|
-
s.date = '2009-
|
5
|
+
s.version = AMQP::VERSION
|
6
|
+
s.date = '2009-12-29'
|
5
7
|
s.summary = 'AMQP client implementation in Ruby/EventMachine'
|
6
8
|
s.email = "amqp@tmm1.net"
|
7
9
|
s.homepage = "http://amqp.rubyforge.org/"
|
8
|
-
s.
|
10
|
+
s.rubyforge_project = 'amqp'
|
11
|
+
s.description = "An implementation of the AMQP protocol in Ruby/EventMachine for writing clients to the RabbitMQ message broker"
|
9
12
|
s.has_rdoc = true
|
10
13
|
s.rdoc_options = '--include=examples'
|
11
14
|
|
12
15
|
# ruby -rpp -e' pp `git ls-files`.split("\n").grep(/^(doc|README)/) '
|
13
16
|
s.extra_rdoc_files = [
|
14
|
-
"README",
|
17
|
+
"README.md",
|
15
18
|
"doc/EXAMPLE_01_PINGPONG",
|
16
19
|
"doc/EXAMPLE_02_CLOCK",
|
17
20
|
"doc/EXAMPLE_03_STOCKS",
|
@@ -26,7 +29,7 @@ spec = Gem::Specification.new do |s|
|
|
26
29
|
|
27
30
|
# ruby -rpp -e' pp `git ls-files`.split("\n") '
|
28
31
|
s.files = [
|
29
|
-
"README",
|
32
|
+
"README.md",
|
30
33
|
"Rakefile",
|
31
34
|
"amqp.gemspec",
|
32
35
|
"amqp.todo",
|
@@ -50,6 +53,7 @@ spec = Gem::Specification.new do |s|
|
|
50
53
|
"examples/mq/primes.rb",
|
51
54
|
"examples/mq/stocks.rb",
|
52
55
|
"lib/amqp.rb",
|
56
|
+
"lib/amqp/version.rb",
|
53
57
|
"lib/amqp/buffer.rb",
|
54
58
|
"lib/amqp/client.rb",
|
55
59
|
"lib/amqp/frame.rb",
|
@@ -57,7 +61,6 @@ spec = Gem::Specification.new do |s|
|
|
57
61
|
"lib/amqp/server.rb",
|
58
62
|
"lib/amqp/spec.rb",
|
59
63
|
"lib/ext/blankslate.rb",
|
60
|
-
"lib/ext/em.rb",
|
61
64
|
"lib/ext/emfork.rb",
|
62
65
|
"lib/mq.rb",
|
63
66
|
"lib/mq/exchange.rb",
|
data/bacon/client.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# this is the bacon spec for AMQP client
|
2
2
|
# you can find other specs inline in frame.rb, buffer.rb and protocol.rb
|
3
|
+
# to run just 1 test use:
|
4
|
+
# bacon lib/amqp.rb --name "should ..."
|
3
5
|
# this one couldn't be written in line because:
|
4
6
|
# due to the load order 'AMQP' isn't completely defined yet when Client is loaded
|
5
7
|
require 'mocha'
|
@@ -14,6 +16,7 @@ describe Client do
|
|
14
16
|
AMQP.instance_eval{ @closing = false }
|
15
17
|
Client.class_eval{ @retry_count = 0 }
|
16
18
|
Client.class_eval{ @server_to_select = 0 }
|
19
|
+
EM.instance_eval{ @next_tick_queue = nil }
|
17
20
|
end
|
18
21
|
|
19
22
|
should 'reconnect on disconnect after connection_completed (use reconnect_timer)' do
|
@@ -71,7 +74,7 @@ describe Client do
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
74
|
-
should "use fallback servers on reconnect" do
|
77
|
+
should "use fallback servers on reconnect without connection_completed" do
|
75
78
|
@times_connected = 0
|
76
79
|
@connect_args = []
|
77
80
|
|
@@ -291,5 +294,24 @@ describe Client do
|
|
291
294
|
@amqp_conn.should == @client
|
292
295
|
end
|
293
296
|
|
297
|
+
should "send Heartbeat frames based on keepalive interval" do
|
298
|
+
@stuff_sent = []
|
299
|
+
EventMachine.stubs(:connect_server).returns(99).with do |arg1, arg2|
|
300
|
+
EM.next_tick do
|
301
|
+
@client = EM.class_eval{ @conns }[99]
|
302
|
+
@amqp_conn = AMQP.class_eval{ @conn }
|
303
|
+
@client.stubs(:send).returns(true).with do |what_to_send|
|
304
|
+
@stuff_sent << what_to_send.class
|
305
|
+
true
|
306
|
+
end
|
307
|
+
@client.connection_completed
|
308
|
+
end
|
309
|
+
true
|
310
|
+
end
|
311
|
+
EM.next_tick{ EM.add_timer(0.5){ EM.stop_event_loop } }
|
312
|
+
AMQP.start(:host => 'nonexistanthost', :keepalive => 0.2)
|
313
|
+
@stuff_sent.should == [AMQP::Frame::Heartbeat, AMQP::Frame::Heartbeat]
|
314
|
+
end
|
315
|
+
|
294
316
|
|
295
317
|
end
|
data/lib/amqp.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require 'ext/em'
|
8
|
-
require 'ext/blankslate'
|
9
|
-
|
10
|
-
%w[ buffer spec protocol frame client ].each do |file|
|
11
|
-
require "amqp/#{file}"
|
1
|
+
unless defined?(EM)
|
2
|
+
begin
|
3
|
+
require 'eventmachine'
|
4
|
+
rescue LoadError
|
5
|
+
require 'rubygems'
|
6
|
+
require 'eventmachine'
|
12
7
|
end
|
8
|
+
end
|
9
|
+
|
10
|
+
library_root = File.expand_path("..", __FILE__)
|
11
|
+
$LOAD_PATH << library_root unless $LOAD_PATH.include?(library_root)
|
12
|
+
require 'ext/emfork'
|
13
|
+
require 'ext/blankslate'
|
13
14
|
|
15
|
+
%w[ version buffer spec protocol frame client ].each do |file|
|
16
|
+
require "amqp/#{file}"
|
17
|
+
end
|
18
|
+
|
19
|
+
module AMQP
|
14
20
|
class << self
|
15
21
|
@logging = false
|
16
22
|
attr_accessor :logging
|
@@ -81,7 +87,7 @@ module AMQP
|
|
81
87
|
# block. See the code examples in MQ for details.
|
82
88
|
#
|
83
89
|
def self.start *args, &blk
|
84
|
-
EM.run{
|
90
|
+
EM.run {
|
85
91
|
@conn ||= connect *args
|
86
92
|
@conn.callback(&blk) if blk
|
87
93
|
@conn
|
@@ -91,11 +97,11 @@ module AMQP
|
|
91
97
|
class << self
|
92
98
|
alias :run :start
|
93
99
|
end
|
94
|
-
|
100
|
+
|
95
101
|
def self.stop
|
96
102
|
if @conn and not @closing
|
97
103
|
@closing = true
|
98
|
-
@conn.close{
|
104
|
+
@conn.close {
|
99
105
|
yield if block_given?
|
100
106
|
@conn = nil
|
101
107
|
@closing = false
|
data/lib/amqp/client.rb
CHANGED
@@ -95,6 +95,12 @@ module AMQP
|
|
95
95
|
@buf = Buffer.new
|
96
96
|
send_data HEADER
|
97
97
|
send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
|
98
|
+
|
99
|
+
if @settings[:keepalive]
|
100
|
+
EM.add_periodic_timer(@settings[:keepalive]) do
|
101
|
+
send Frame::Heartbeat.new
|
102
|
+
end
|
103
|
+
end
|
98
104
|
end
|
99
105
|
|
100
106
|
def connected?
|
data/lib/amqp/frame.rb
CHANGED
data/lib/amqp/protocol.rb
CHANGED
@@ -8,9 +8,9 @@ module AMQP
|
|
8
8
|
def initialize *args
|
9
9
|
opts = args.pop if args.last.is_a? Hash
|
10
10
|
opts ||= {}
|
11
|
-
|
11
|
+
|
12
12
|
@debug = 1 # XXX hack, p(obj) == '' if no instance vars are set
|
13
|
-
|
13
|
+
|
14
14
|
if args.size == 1 and args.first.is_a? Buffer
|
15
15
|
buf = args.shift
|
16
16
|
else
|
@@ -209,4 +209,4 @@ if $0 =~ /bacon/ or $0 == __FILE__
|
|
209
209
|
Protocol::Header.new(orig.to_binary).should == orig
|
210
210
|
end
|
211
211
|
end
|
212
|
-
end
|
212
|
+
end
|
data/lib/amqp/server.rb
CHANGED
data/lib/amqp/version.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Generally this kind of guard is a sign of a bad require, and that is
|
2
|
+
# arguably true here as well. amqp.gemspec requires this file, but later
|
3
|
+
# we want people to be able to require 'amqp/version' without getting a warning.
|
4
|
+
# On the other hand, we aren't in the load path when the gemspec is evaluated,
|
5
|
+
# so we can't use a relative require there.
|
6
|
+
unless defined?(::AMQP::VERSION)
|
7
|
+
module AMQP
|
8
|
+
VERSION = '0.6.7.1'
|
9
|
+
end
|
10
|
+
end
|
data/lib/mq.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
#:main: README
|
2
2
|
#
|
3
3
|
|
4
|
-
$:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
|
5
4
|
require 'amqp'
|
6
5
|
|
7
6
|
class MQ
|
@@ -199,7 +198,7 @@ class MQ
|
|
199
198
|
end
|
200
199
|
|
201
200
|
when Protocol::Queue::DeclareOk
|
202
|
-
queues[ method.queue ].
|
201
|
+
queues[ method.queue ].receive_status method
|
203
202
|
|
204
203
|
when Protocol::Basic::Deliver, Protocol::Basic::GetOk
|
205
204
|
@method = method
|
@@ -230,6 +229,13 @@ class MQ
|
|
230
229
|
c.channels.delete @channel
|
231
230
|
c.close if c.channels.empty?
|
232
231
|
}
|
232
|
+
|
233
|
+
when Protocol::Basic::ConsumeOk
|
234
|
+
if @consumer = consumers[ method.consumer_tag ]
|
235
|
+
@consumer.confirm_subscribe
|
236
|
+
else
|
237
|
+
MQ.error "Basic.ConsumeOk for invalid consumer tag: #{method.consumer_tag}"
|
238
|
+
end
|
233
239
|
end
|
234
240
|
end
|
235
241
|
end
|
@@ -733,7 +739,22 @@ class MQ
|
|
733
739
|
end
|
734
740
|
|
735
741
|
def prefetch(size)
|
742
|
+
@prefetch_size = size
|
736
743
|
send Protocol::Basic::Qos.new(:prefetch_size => 0, :prefetch_count => size, :global => false)
|
744
|
+
self
|
745
|
+
end
|
746
|
+
|
747
|
+
# Asks the broker to redeliver all unacknowledged messages on this
|
748
|
+
# channel.
|
749
|
+
#
|
750
|
+
# * requeue (default false)
|
751
|
+
# If this parameter is false, the message will be redelivered to the original recipient.
|
752
|
+
# If this flag is true, the server will attempt to requeue the message, potentially then
|
753
|
+
# delivering it to an alternative subscriber.
|
754
|
+
#
|
755
|
+
def recover requeue = false
|
756
|
+
send Protocol::Basic::Recover.new(:requeue => requeue)
|
757
|
+
self
|
737
758
|
end
|
738
759
|
|
739
760
|
# Returns a hash of all the exchange proxy objects.
|
@@ -786,6 +807,8 @@ class MQ
|
|
786
807
|
qus = @queues
|
787
808
|
@queues = {}
|
788
809
|
qus.each{ |_,q| q.reset } if qus
|
810
|
+
|
811
|
+
prefetch(@prefetch_size) if @prefetch_size
|
789
812
|
end
|
790
813
|
|
791
814
|
private
|
@@ -820,4 +843,4 @@ class MQ
|
|
820
843
|
def MQ.id
|
821
844
|
Thread.current[:mq_id] ||= "#{`hostname`.strip}-#{Process.pid}-#{Thread.current.object_id}"
|
822
845
|
end
|
823
|
-
end
|
846
|
+
end
|
data/lib/mq/exchange.rb
CHANGED
@@ -183,6 +183,16 @@ class MQ
|
|
183
183
|
# not wait for a reply method. If the server could not complete the
|
184
184
|
# method it will raise a channel or connection exception.
|
185
185
|
#
|
186
|
+
# * :no_declare => true | false (default false)
|
187
|
+
# If set, the exchange will not be declared to the
|
188
|
+
# AMQP broker at instantiation-time. This allows the AMQP
|
189
|
+
# client to send messages to exchanges that were
|
190
|
+
# already declared by someone else, e.g. if the client
|
191
|
+
# does not have sufficient privilege to declare (create)
|
192
|
+
# an exchange. Use with caution, as binding to an exchange
|
193
|
+
# with the no-declare option causes your system to become
|
194
|
+
# sensitive to the ordering of clients' actions!
|
195
|
+
#
|
186
196
|
# == Exceptions
|
187
197
|
# Doing any of these activities are illegal and will raise MQ:Error.
|
188
198
|
# * redeclare an already-declared exchange to a different type
|
@@ -194,11 +204,13 @@ class MQ
|
|
194
204
|
@mq.exchanges[@name = name] ||= self
|
195
205
|
@key = opts[:key]
|
196
206
|
|
197
|
-
|
198
|
-
@mq.
|
199
|
-
|
200
|
-
|
201
|
-
|
207
|
+
unless name == "amq.#{type}" or name == '' or opts[:no_declare]
|
208
|
+
@mq.callback{
|
209
|
+
@mq.send Protocol::Exchange::Declare.new({ :exchange => name,
|
210
|
+
:type => type,
|
211
|
+
:nowait => true }.merge(opts))
|
212
|
+
}
|
213
|
+
end
|
202
214
|
end
|
203
215
|
attr_reader :name, :type, :key
|
204
216
|
|
@@ -247,13 +259,13 @@ class MQ
|
|
247
259
|
out = []
|
248
260
|
|
249
261
|
out << Protocol::Basic::Publish.new({ :exchange => name,
|
250
|
-
:routing_key => opts
|
262
|
+
:routing_key => opts[:key] || @key }.merge(opts))
|
251
263
|
|
252
264
|
data = data.to_s
|
253
265
|
|
254
266
|
out << Protocol::Header.new(Protocol::Basic,
|
255
267
|
data.length, { :content_type => 'application/octet-stream',
|
256
|
-
:delivery_mode => (opts
|
268
|
+
:delivery_mode => (opts[:persistent] ? 2 : 1),
|
257
269
|
:priority => 0 }.merge(opts))
|
258
270
|
|
259
271
|
out << Frame::Body.new(data)
|
data/lib/mq/queue.rb
CHANGED
@@ -113,7 +113,7 @@ class MQ
|
|
113
113
|
@mq.callback{
|
114
114
|
@mq.send Protocol::Queue::Bind.new({ :queue => name,
|
115
115
|
:exchange => exchange,
|
116
|
-
:routing_key => opts
|
116
|
+
:routing_key => opts[:key],
|
117
117
|
:nowait => true }.merge(opts))
|
118
118
|
}
|
119
119
|
self
|
@@ -140,7 +140,7 @@ class MQ
|
|
140
140
|
@mq.callback{
|
141
141
|
@mq.send Protocol::Queue::Unbind.new({ :queue => name,
|
142
142
|
:exchange => exchange,
|
143
|
-
:routing_key => opts
|
143
|
+
:routing_key => opts[:key],
|
144
144
|
:nowait => true }.merge(opts))
|
145
145
|
}
|
146
146
|
self
|
@@ -175,6 +175,16 @@ class MQ
|
|
175
175
|
nil
|
176
176
|
end
|
177
177
|
|
178
|
+
# Purge all messages from the queue.
|
179
|
+
#
|
180
|
+
def purge opts = {}
|
181
|
+
@mq.callback{
|
182
|
+
@mq.send Protocol::Queue::Purge.new({ :queue => name,
|
183
|
+
:nowait => true }.merge(opts))
|
184
|
+
}
|
185
|
+
nil
|
186
|
+
end
|
187
|
+
|
178
188
|
# This method provides a direct access to the messages in a queue
|
179
189
|
# using a synchronous dialogue that is designed for specific types of
|
180
190
|
# application where synchronous functionality is more important than
|
@@ -240,7 +250,7 @@ class MQ
|
|
240
250
|
q.push(self)
|
241
251
|
@mq.send Protocol::Basic::Get.new({ :queue => name,
|
242
252
|
:consumer_tag => name,
|
243
|
-
:no_ack => !opts
|
253
|
+
:no_ack => !opts[:ack],
|
244
254
|
:nowait => true }.merge(opts))
|
245
255
|
}
|
246
256
|
}
|
@@ -296,6 +306,12 @@ class MQ
|
|
296
306
|
# not wait for a reply method. If the server could not complete the
|
297
307
|
# method it will raise a channel or connection exception.
|
298
308
|
#
|
309
|
+
# * :confirm => proc (default nil)
|
310
|
+
# If set, this proc will be called when the server confirms subscription
|
311
|
+
# to the queue with a ConsumeOk message. Setting this option will
|
312
|
+
# automatically set :nowait => false. This is required for the server
|
313
|
+
# to send a confirmation.
|
314
|
+
#
|
299
315
|
def subscribe opts = {}, &blk
|
300
316
|
@consumer_tag = "#{name}-#{Kernel.rand(999_999_999_999)}"
|
301
317
|
@mq.consumers[@consumer_tag] = self
|
@@ -304,11 +320,12 @@ class MQ
|
|
304
320
|
|
305
321
|
@on_msg = blk
|
306
322
|
@on_msg_opts = opts
|
323
|
+
opts[:nowait] = false if (@on_confirm_subscribe = opts[:confirm])
|
307
324
|
|
308
325
|
@mq.callback{
|
309
326
|
@mq.send Protocol::Basic::Consume.new({ :queue => name,
|
310
327
|
:consumer_tag => @consumer_tag,
|
311
|
-
:no_ack => !opts
|
328
|
+
:no_ack => !opts[:ack],
|
312
329
|
:nowait => true }.merge(opts))
|
313
330
|
}
|
314
331
|
self
|
@@ -336,7 +353,6 @@ class MQ
|
|
336
353
|
# method it will raise a channel or connection exception.
|
337
354
|
#
|
338
355
|
def unsubscribe opts = {}, &blk
|
339
|
-
@on_msg = nil
|
340
356
|
@on_cancel = blk
|
341
357
|
@mq.callback{
|
342
358
|
@mq.send Protocol::Basic::Cancel.new({ :consumer_tag => @consumer_tag }.merge(opts))
|
@@ -391,7 +407,7 @@ class MQ
|
|
391
407
|
self
|
392
408
|
end
|
393
409
|
|
394
|
-
def
|
410
|
+
def receive_status declare_ok
|
395
411
|
if @on_status
|
396
412
|
m, c = declare_ok.message_count, declare_ok.consumer_count
|
397
413
|
@on_status.call *(@on_status.arity == 1 ? [m] : [m, c])
|
@@ -399,10 +415,16 @@ class MQ
|
|
399
415
|
end
|
400
416
|
end
|
401
417
|
|
418
|
+
def confirm_subscribe
|
419
|
+
@on_confirm_subscribe.call if @on_confirm_subscribe
|
420
|
+
@on_confirm_subscribe = nil
|
421
|
+
end
|
422
|
+
|
402
423
|
def cancelled
|
403
424
|
@on_cancel.call if @on_cancel
|
404
425
|
@on_cancel = @on_msg = nil
|
405
426
|
@mq.consumers.delete @consumer_tag
|
427
|
+
@mq.queues.delete(@name) if @opts[:auto_delete]
|
406
428
|
@consumer_tag = nil
|
407
429
|
end
|
408
430
|
|
data/lib/mq/rpc.rb
CHANGED
@@ -30,14 +30,14 @@ class MQ
|
|
30
30
|
#
|
31
31
|
# Marshalling and unmarshalling the objects is handled internally. This
|
32
32
|
# marshalling is subject to the same restrictions as defined in the
|
33
|
-
# Marshal[http://ruby-doc.org/core/classes/Marshal.html] standard
|
33
|
+
# Marshal[http://ruby-doc.org/core/classes/Marshal.html] standard
|
34
34
|
# library. See that documentation for further reference.
|
35
35
|
#
|
36
|
-
# When the optional object is not passed, the returned rpc reference is
|
37
|
-
# used to send messages and arguments to the queue. See #method_missing
|
38
|
-
# which does all of the heavy lifting with the proxy. Some client
|
39
|
-
# elsewhere must call this method *with* the optional block so that
|
40
|
-
# there is a valid destination. Failure to do so will just enqueue
|
36
|
+
# When the optional object is not passed, the returned rpc reference is
|
37
|
+
# used to send messages and arguments to the queue. See #method_missing
|
38
|
+
# which does all of the heavy lifting with the proxy. Some client
|
39
|
+
# elsewhere must call this method *with* the optional block so that
|
40
|
+
# there is a valid destination. Failure to do so will just enqueue
|
41
41
|
# marshalled messages that are never consumed.
|
42
42
|
#
|
43
43
|
def initialize mq, queue, obj = nil
|
@@ -53,7 +53,7 @@ class MQ
|
|
53
53
|
else
|
54
54
|
obj
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
@mq.queue(queue).subscribe(:ack=>true){ |info, request|
|
58
58
|
method, *args = ::Marshal.load(request)
|
59
59
|
ret = @obj.__send__(method, *args)
|
@@ -67,7 +67,7 @@ class MQ
|
|
67
67
|
else
|
68
68
|
@callbacks ||= {}
|
69
69
|
# XXX implement and use queue(nil)
|
70
|
-
@queue = @mq.queue(@name = "random identifier #{::Kernel.rand(999_999_999_999)}").subscribe{|info, msg|
|
70
|
+
@queue = @mq.queue(@name = "random identifier #{::Kernel.rand(999_999_999_999)}", :auto_delete => true).subscribe{|info, msg|
|
71
71
|
if blk = @callbacks.delete(info.message_id)
|
72
72
|
blk.call ::Marshal.load(msg)
|
73
73
|
end
|
@@ -97,4 +97,4 @@ class MQ
|
|
97
97
|
@remote.publish(::Marshal.dump([meth, *args]), :reply_to => blk ? @name : nil, :message_id => message_id)
|
98
98
|
end
|
99
99
|
end
|
100
|
-
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,35 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brontes3d-amqp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 97
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 7
|
10
|
+
- 1
|
11
|
+
version: 0.6.7.1
|
5
12
|
platform: ruby
|
6
13
|
authors:
|
7
14
|
- Aman Gupta
|
15
|
+
- Jacob Burkhart
|
8
16
|
autorequire:
|
9
17
|
bindir: bin
|
10
18
|
cert_chain: []
|
11
19
|
|
12
|
-
date:
|
20
|
+
date: 2010-06-04 00:00:00 -04:00
|
13
21
|
default_executable:
|
14
22
|
dependencies:
|
15
23
|
- !ruby/object:Gem::Dependency
|
16
24
|
name: eventmachine
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
20
28
|
requirements:
|
21
29
|
- - ">="
|
22
30
|
- !ruby/object:Gem::Version
|
31
|
+
hash: 39
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
- 12
|
35
|
+
- 4
|
23
36
|
version: 0.12.4
|
24
|
-
|
25
|
-
|
26
|
-
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
description: An implementation of the AMQP protocol in Ruby/EventMachine for writing clients to the RabbitMQ message broker
|
40
|
+
email: jacob@brontes3d.com
|
27
41
|
executables: []
|
28
42
|
|
29
43
|
extensions: []
|
30
44
|
|
31
45
|
extra_rdoc_files:
|
32
|
-
- README
|
46
|
+
- README.md
|
33
47
|
- doc/EXAMPLE_01_PINGPONG
|
34
48
|
- doc/EXAMPLE_02_CLOCK
|
35
49
|
- doc/EXAMPLE_03_STOCKS
|
@@ -38,7 +52,7 @@ extra_rdoc_files:
|
|
38
52
|
- doc/EXAMPLE_05_POP
|
39
53
|
- doc/EXAMPLE_06_HASHTABLE
|
40
54
|
files:
|
41
|
-
- README
|
55
|
+
- README.md
|
42
56
|
- Rakefile
|
43
57
|
- amqp.gemspec
|
44
58
|
- amqp.todo
|
@@ -62,6 +76,7 @@ files:
|
|
62
76
|
- examples/mq/primes.rb
|
63
77
|
- examples/mq/stocks.rb
|
64
78
|
- lib/amqp.rb
|
79
|
+
- lib/amqp/version.rb
|
65
80
|
- lib/amqp/buffer.rb
|
66
81
|
- lib/amqp/client.rb
|
67
82
|
- lib/amqp/frame.rb
|
@@ -69,7 +84,6 @@ files:
|
|
69
84
|
- lib/amqp/server.rb
|
70
85
|
- lib/amqp/spec.rb
|
71
86
|
- lib/ext/blankslate.rb
|
72
|
-
- lib/ext/em.rb
|
73
87
|
- lib/ext/emfork.rb
|
74
88
|
- lib/mq.rb
|
75
89
|
- lib/mq/exchange.rb
|
@@ -93,30 +107,38 @@ files:
|
|
93
107
|
- research/primes-threaded.rb
|
94
108
|
- bacon/client.rb
|
95
109
|
has_rdoc: true
|
96
|
-
homepage: http://
|
110
|
+
homepage: http://github.com/brontes3d/amqp
|
111
|
+
licenses: []
|
112
|
+
|
97
113
|
post_install_message:
|
98
114
|
rdoc_options:
|
99
115
|
- --include=examples
|
100
116
|
require_paths:
|
101
117
|
- lib
|
102
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
103
120
|
requirements:
|
104
121
|
- - ">="
|
105
122
|
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
106
126
|
version: "0"
|
107
|
-
version:
|
108
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
109
129
|
requirements:
|
110
130
|
- - ">="
|
111
131
|
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
133
|
+
segments:
|
134
|
+
- 0
|
112
135
|
version: "0"
|
113
|
-
version:
|
114
136
|
requirements: []
|
115
137
|
|
116
|
-
rubyforge_project:
|
117
|
-
rubygems_version: 1.
|
138
|
+
rubyforge_project: amqp
|
139
|
+
rubygems_version: 1.3.7
|
118
140
|
signing_key:
|
119
|
-
specification_version:
|
141
|
+
specification_version: 3
|
120
142
|
summary: AMQP client implementation in Ruby/EventMachine
|
121
143
|
test_files: []
|
122
144
|
|
data/README
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
Simple AMQP driver for Ruby/EventMachine
|
2
|
-
(c) 2008 Aman Gupta (tmm1)
|
3
|
-
|
4
|
-
http://github.com/tmm1/amqp
|
5
|
-
http://rubyforge.org/projects/amqp
|
6
|
-
http://hopper.squarespace.com/blog/2008/7/22/simple-amqp-library-for-ruby.html
|
7
|
-
http://groups.google.com/group/ruby-amqp
|
8
|
-
http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2008-July/001417.html
|
9
|
-
|
10
|
-
This library works with Ruby 1.8, Ruby 1.9 and JRuby.
|
11
|
-
|
12
|
-
This library was tested primarily with RabbitMQ, although it should be compatible with any
|
13
|
-
server implementing the AMQP 0-8 spec.
|
14
|
-
|
15
|
-
To use with RabbitMQ, first run the server:
|
16
|
-
|
17
|
-
hg clone http://hg.rabbitmq.com/rabbitmq-codegen
|
18
|
-
hg clone http://hg.rabbitmq.com/rabbitmq-server
|
19
|
-
cd rabbitmq-server
|
20
|
-
make run
|
21
|
-
|
22
|
-
To get started, refer to the various bundled examples:
|
23
|
-
|
24
|
-
ruby examples/mq/pingpong.rb # 1-1 communication using amq.direct
|
25
|
-
ruby examples/mq/clock.rb # 1-N communication using amq.fanout
|
26
|
-
ruby examples/mq/stocks.rb # 1-subscriber communication using amq.topic
|
27
|
-
|
28
|
-
ruby examples/mq/multiclock.rb # header based routing (new rabbitmq feature)
|
29
|
-
ruby examples/mq/ack.rb # using ack
|
30
|
-
ruby examples/mq/pop.rb # pop off messages one at a time
|
31
|
-
|
32
|
-
ruby examples/mq/hashtable.rb # simple async rpc layer
|
33
|
-
ruby examples/mq/primes.rb 4 # parallelized prime number generation
|
34
|
-
ruby examples/mq/logger.rb # simple logging api
|
35
|
-
|
36
|
-
For more details into the lower level AMQP client API, run the simple client example:
|
37
|
-
|
38
|
-
ruby examples/amqp/simple.rb # low-level AMQP api
|
39
|
-
ruby examples/mq/internal.rb # low-level Queue/Exchange api
|
40
|
-
|
41
|
-
Or refer to protocol/doc.txt, which enumerates packets sent between a server and client
|
42
|
-
during a typical session, in both binary and decoded formats.
|
43
|
-
|
44
|
-
To run the test suite:
|
45
|
-
|
46
|
-
rake spec
|
47
|
-
|
48
|
-
The lib/amqp/spec.rb file is generated automatically based on the AMQP specification. To generate it:
|
49
|
-
|
50
|
-
rake codegen
|
51
|
-
|
52
|
-
This project was inspired by py-amqplib, rabbitmq, qpid and rubbyt.
|
53
|
-
Special thanks to Dmitriy Samovskiy, Ben Hood and Tony Garnock-Jones.
|
54
|
-
|
55
|
-
AMQP resources:
|
56
|
-
|
57
|
-
Servers:
|
58
|
-
RabbitMQ (Rabbit Technologies, Erlang/OTP, MPL) - http://rabbitmq.com
|
59
|
-
ZeroMQ (iMatix/FastMQ/Intel, C++, GPL3) - http://www.zeromq.org
|
60
|
-
OpenAMQ (iMatix, C, GPL2) - http://openamq.org
|
61
|
-
ActiveMQ (Apache Foundation, Java, apache2) - http://activemq.apache.org
|
62
|
-
|
63
|
-
Steve Vinoski explains AMQP in his column, Towards Integration
|
64
|
-
http://steve.vinoski.net/pdf/IEEE-Advanced_Message_Queuing_Protocol.pdf
|
65
|
-
|
66
|
-
John O'Hara on the history of AMQP
|
67
|
-
http://www.acmqueue.org/modules.php?name=Content&pa=showpage&pid=485
|
68
|
-
|
69
|
-
Dmitriy's presentation on RabbitMQ/AMQP
|
70
|
-
http://somic-org.homelinux.org/blog/2008/07/31/slides-for-my-amqprabbitmq-talk/
|
71
|
-
|
72
|
-
ZeroMQ's analysis of the messaging technology market
|
73
|
-
http://www.zeromq.org/whitepapers:market-analysis
|
74
|
-
|
75
|
-
Pieter Hintjens's background to AMQP
|
76
|
-
http://www.openamq.org/doc:amqp-background
|
77
|
-
|
78
|
-
Barry Pederson's py-amqplib
|
79
|
-
http://barryp.org/software/py-amqplib/
|
80
|
-
|
81
|
-
Ben Hood on writing an AMQP client
|
82
|
-
http://hopper.squarespace.com/blog/2008/6/21/build-your-own-amqp-client.html
|
83
|
-
|
84
|
-
Dmitriy Samovskiy introduces Ruby + QPid + RabbitMQ
|
85
|
-
http://somic-org.homelinux.org/blog/2008/06/24/ruby-amqp-rabbitmq-example/
|
86
|
-
|
87
|
-
Ben Hood's as3-amqp
|
88
|
-
http://github.com/0x6e6562/as3-amqp
|
89
|
-
http://hopper.squarespace.com/blog/2008/7/4/server-side-as3.html
|
90
|
-
http://hopper.squarespace.com/blog/2008/3/24/as3-amqp-client-first-cut.html
|
91
|
-
|
92
|
-
RabbitMQ's protocol code generator
|
93
|
-
http://hg.rabbitmq.com/rabbitmq-codegen/
|
94
|
-
|
95
|
-
Erlang Exchange presentation on the implementation of RabbitMQ
|
96
|
-
http://skillsmatter.com/podcast/erlang/presenting-rabbitmq-an-erlang-based-implementatio-nof-amqp
|
97
|
-
http://www.lshift.net/blog/2008/07/01/slides-from-our-erlang-exchange-talk
|
98
|
-
|
99
|
-
Jonathan Conway's series on RabbitMQ and using it with Ruby/Merb
|
100
|
-
http://jaikoo.com/2008/3/20/daemonize-rabbitmq
|
101
|
-
http://jaikoo.com/2008/3/14/oh-hai-rabbitmq
|
102
|
-
http://jaikoo.com/2008/2/29/friday-round-up-2008-02-29
|
103
|
-
http://jaikoo.com/2007/9/4/didn-t-you-get-the-memo
|
104
|
-
|
105
|
-
Open Enterprise's series on messaging middleware and AMQP
|
106
|
-
http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-1.html
|
107
|
-
http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-2.html
|
108
|
-
http://www1.interopsystems.com/analysis/can-amqp-break-ibms-mom-monopoly-part-3.html
|
109
|
-
|
110
|
-
Messaging and distributed systems resources:
|
111
|
-
|
112
|
-
A Critique of the Remote Procedure Call Paradigm
|
113
|
-
http://www.cs.vu.nl/~ast/publications/euteco-1988.pdf
|
114
|
-
|
115
|
-
A Note on Distributed Computing
|
116
|
-
http://research.sun.com/techrep/1994/smli_tr-94-29.pdf
|
117
|
-
|
118
|
-
Convenience Over Correctness
|
119
|
-
http://steve.vinoski.net/pdf/IEEE-Convenience_Over_Correctness.pdf
|
120
|
-
|
121
|
-
Metaprotocol Taxonomy and Communications Patterns
|
122
|
-
http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp
|
123
|
-
|
124
|
-
Joe Armstrong on Erlang messaging vs RPC
|
125
|
-
http://armstrongonsoftware.blogspot.com/2008/05/road-we-didnt-go-down.html
|
126
|
-
|
127
|
-
SEDA: scalable internet services using message queues
|
128
|
-
http://www.eecs.harvard.edu/~mdw/papers/seda-sosp01.pdf
|
data/lib/ext/em.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'eventmachine'
|
3
|
-
rescue LoadError
|
4
|
-
require 'rubygems'
|
5
|
-
require 'eventmachine'
|
6
|
-
end
|
7
|
-
|
8
|
-
#:stopdoc:
|
9
|
-
|
10
|
-
if EM::VERSION < '0.12.2'
|
11
|
-
|
12
|
-
def EventMachine::run blk=nil, tail=nil, &block
|
13
|
-
@tails ||= []
|
14
|
-
tail and @tails.unshift(tail)
|
15
|
-
|
16
|
-
if reactor_running?
|
17
|
-
(b = blk || block) and b.call # next_tick(b)
|
18
|
-
else
|
19
|
-
@conns = {}
|
20
|
-
@acceptors = {}
|
21
|
-
@timers = {}
|
22
|
-
begin
|
23
|
-
@reactor_running = true
|
24
|
-
initialize_event_machine
|
25
|
-
(b = blk || block) and add_timer(0, b)
|
26
|
-
run_machine
|
27
|
-
ensure
|
28
|
-
release_machine
|
29
|
-
@reactor_running = false
|
30
|
-
end
|
31
|
-
|
32
|
-
until @tails.empty?
|
33
|
-
@tails.pop.call
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def EventMachine::fork_reactor &block
|
39
|
-
Kernel.fork do
|
40
|
-
if self.reactor_running?
|
41
|
-
self.stop_event_loop
|
42
|
-
self.release_machine
|
43
|
-
self.instance_variable_set( '@reactor_running', false )
|
44
|
-
end
|
45
|
-
self.run block
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
require 'ext/emfork'
|