raq 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.simplecov +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +23 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +20 -0
- data/README.md +20 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/features/raq.feature +102 -0
- data/features/step_definitions/raq_steps.rb +127 -0
- data/features/support/env.rb +145 -0
- data/features/support/hooks.rb +8 -0
- data/lib/raq.rb +4 -0
- data/lib/raq/runner.rb +37 -0
- data/lib/raq/server.rb +44 -0
- data/lib/raq/server/builder.rb +27 -0
- data/raq.gemspec +90 -0
- data/spec/raq/runner_spec.rb +83 -0
- data/spec/raq/server_spec.rb +35 -0
- data/spec/raq_spec.rb +8 -0
- data/spec/spec_helper.rb +12 -0
- data/tmp/.gitkeep +0 -0
- data/tmp/consumer.rb +58 -0
- data/tmp/producer.rb +24 -0
- data/tmp/rack_hack.rb +54 -0
- metadata +221 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --profile
|
data/.simplecov
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "eventmachine", "~> 1.0"
|
4
|
+
gem "amqp", "~> 1.0"
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "bundler", "~> 1.3.0"
|
8
|
+
gem "guard", "~> 1.8.0"
|
9
|
+
gem "guard-cucumber", "~> 1.4.0"
|
10
|
+
gem "guard-rspec", "~> 3.0.2"
|
11
|
+
gem "pry", "~> 0.9.12"
|
12
|
+
gem "rdoc", "~> 3.12"
|
13
|
+
gem "travis-lint", "~> 1.7.0"
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem "aruba", "~> 0.5.3"
|
18
|
+
gem "cucumber", "~> 1.3.0"
|
19
|
+
gem "jeweler", "~> 1.8.4"
|
20
|
+
gem "rake", "~> 10.0.4"
|
21
|
+
gem "rspec", "~> 2.13.0"
|
22
|
+
gem "simplecov", "~> 0.7.0", platform: :ruby_19
|
23
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard :rspec do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/spec_helper.rb') { "spec" }
|
5
|
+
end
|
6
|
+
|
7
|
+
guard 'cucumber' do
|
8
|
+
watch(%r{^features/.+\.feature$})
|
9
|
+
watch(%r{^features/support/.+$}) { 'features' }
|
10
|
+
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
|
11
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Caleb Buxton
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Raq [![Build Status](https://travis-ci.org/cpb/raq.png?branch=master)](https://travis-ci.org/cpb/raq) [![Dependency Status](https://gemnasium.com/cpb/raq.png)](https://gemnasium.com/cpb/raq) [![Code Climate](https://codeclimate.com/github/cpb/raq.png)](https://codeclimate.com/github/cpb/raq)
|
2
|
+
=====
|
3
|
+
|
4
|
+
Description goes here.
|
5
|
+
|
6
|
+
== Contributing to raq
|
7
|
+
|
8
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
9
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
10
|
+
* Fork the project.
|
11
|
+
* Start a feature/bugfix branch.
|
12
|
+
* Commit and push until you are happy with your contribution.
|
13
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
14
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2013 Caleb Buxton. See LICENSE.txt for
|
19
|
+
further details.
|
20
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "raq"
|
18
|
+
gem.homepage = "http://github.com/cpb/raq"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Middleware for your AMQP Message Consumer}
|
21
|
+
gem.description = %Q{The elegance of Rack with none of the unreliability of HTTP}
|
22
|
+
gem.email = "me@cpb.ca"
|
23
|
+
gem.authors = ["Caleb Buxton"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'cucumber/rake/task'
|
29
|
+
Cucumber::Rake::Task.new(:features)
|
30
|
+
|
31
|
+
require 'rspec/core'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rdoc/task'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "raq #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,102 @@
|
|
1
|
+
Feature: Raq provides a friendly and familiar way of consuming messages off a durable queue.
|
2
|
+
In order to be happy with consuming amqp messages
|
3
|
+
A user wants to deal with the smallest amount of connection information in code as possible
|
4
|
+
|
5
|
+
@amqp
|
6
|
+
Scenario: Happily receiving a message on a single queue
|
7
|
+
Given I produce a message "Hello" on the queue "single.queue"
|
8
|
+
And a consumer with:
|
9
|
+
"""
|
10
|
+
runner = Raq::Runner.new(ARGV)
|
11
|
+
server = Raq::Server.new(
|
12
|
+
connection: runner.connection_options,
|
13
|
+
queues: runner.options[:queue]) do
|
14
|
+
|
15
|
+
run do |meta, payload|
|
16
|
+
puts "Got #{payload}"
|
17
|
+
meta.ack
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
server.run
|
23
|
+
"""
|
24
|
+
When I run the consumer on the queue "single.queue"
|
25
|
+
Then the output should contain "Got Hello"
|
26
|
+
|
27
|
+
@amqp
|
28
|
+
Scenario: An agent crashes, another receives the message
|
29
|
+
Given I produce a unique message on the queue "single.queue"
|
30
|
+
And a consumer "crasher" with:
|
31
|
+
"""
|
32
|
+
runner = Raq::Runner.new(ARGV)
|
33
|
+
server = Raq::Server.new(
|
34
|
+
connection: runner.connection_options,
|
35
|
+
queues: runner.options[:queue]) do
|
36
|
+
|
37
|
+
run do |meta, payload|
|
38
|
+
raise "Ahh! I'm going to die alone!"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
server.run
|
43
|
+
"""
|
44
|
+
And a consumer with:
|
45
|
+
"""
|
46
|
+
runner = Raq::Runner.new(ARGV)
|
47
|
+
server = Raq::Server.new(
|
48
|
+
connection: runner.connection_options,
|
49
|
+
queues: runner.options[:queue]) do
|
50
|
+
|
51
|
+
run do |meta, payload|
|
52
|
+
puts "Got #{payload}"
|
53
|
+
meta.ack
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
server.run
|
59
|
+
"""
|
60
|
+
And I run the consumer "crasher" on the queue "single.queue", failing
|
61
|
+
When I run the consumer on the queue "single.queue"
|
62
|
+
Then the output should contain the unique message
|
63
|
+
|
64
|
+
@amqp
|
65
|
+
Scenario: Happily receiving a message on a using middleware
|
66
|
+
Given I produce a unique message on a new queue
|
67
|
+
And a file named "always_ack.rb" with:
|
68
|
+
"""
|
69
|
+
class AlwaysAck < Struct.new(:app)
|
70
|
+
def call(meta, payload)
|
71
|
+
puts "Alwasy be ackin'"
|
72
|
+
meta.ack
|
73
|
+
app.call(meta,payload)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
"""
|
77
|
+
And a consumer with:
|
78
|
+
"""
|
79
|
+
begin
|
80
|
+
require 'always_ack'
|
81
|
+
rescue LoadError => e
|
82
|
+
puts $LOAD_PATH
|
83
|
+
end
|
84
|
+
|
85
|
+
runner = Raq::Runner.new(ARGV)
|
86
|
+
server = Raq::Server.new(
|
87
|
+
connection: runner.connection_options,
|
88
|
+
queues: runner.options[:queue]) do
|
89
|
+
|
90
|
+
use AlwaysAck
|
91
|
+
|
92
|
+
run do |meta, payload|
|
93
|
+
puts "Got #{payload}"
|
94
|
+
server.connection.close { EM.stop }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
server.run
|
99
|
+
"""
|
100
|
+
And I run the consumer on the queue
|
101
|
+
When I run the consumer on the queue, again
|
102
|
+
Then it should never return
|
@@ -0,0 +1,127 @@
|
|
1
|
+
QUEUE = Transform /^(?:the queue(?: "(.*?)")?|(a new queue))$/ do |named_queue,make_a_new_queue|
|
2
|
+
if named_queue
|
3
|
+
new_queue(named_queue)
|
4
|
+
elsif make_a_new_queue
|
5
|
+
new_queue
|
6
|
+
else
|
7
|
+
last_queue
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
SOME_MESSAGE = Transform /^(?:a message "(.*?)"|(a unique message))$/ do |specific_message,unique_message|
|
12
|
+
if unique_message
|
13
|
+
current_message(Digest::MD5.new << Time.now.to_s << rand(10000).to_s)
|
14
|
+
else
|
15
|
+
current_message(specific_message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
EXPECT_SOME_MESSAGE = Transform /^the unique message$/ do |string|
|
20
|
+
last_message
|
21
|
+
end
|
22
|
+
|
23
|
+
A_CONSUMER = Transform /^a consumer(?: "(.*?)")?$/ do |possible_name|
|
24
|
+
if possible_name
|
25
|
+
new_consumer_path(possible_name)
|
26
|
+
else
|
27
|
+
new_consumer_path
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
THE_CONSUMER = Transform /^the consumer(?: "(.*?)")?$/ do |possible_name|
|
32
|
+
if possible_name
|
33
|
+
last_consumer_path(possible_name)
|
34
|
+
else
|
35
|
+
last_consumer_path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Given(/^a raq agent file named "(.*?)" with:$/) do |name, content|
|
40
|
+
steps %{
|
41
|
+
Given a file named "#{name}" with:
|
42
|
+
"""
|
43
|
+
#{simplecov_for(name)}
|
44
|
+
require 'raq'
|
45
|
+
|
46
|
+
#{content}
|
47
|
+
"""
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
Given(/^I produce (#{SOME_MESSAGE}) on (#{QUEUE})/) do |message, queue|
|
52
|
+
steps %{
|
53
|
+
Given a raq agent file named "producer.rb" with:
|
54
|
+
"""
|
55
|
+
runner = Raq::Runner.new(ARGV)
|
56
|
+
|
57
|
+
EventMachine.run do
|
58
|
+
connection = AMQP.connect(runner.connection_options)
|
59
|
+
|
60
|
+
puts "Connected to AMQP broker. Running \#{AMQP::VERSION} version of the gem..."
|
61
|
+
|
62
|
+
channel = AMQP::Channel.new(connection)
|
63
|
+
queue = channel.queue(runner.options[:queue], durable: true, auto_delete: false)
|
64
|
+
exchange = channel.direct("")
|
65
|
+
|
66
|
+
puts "publshing \#{runner.command} to \#{queue.name}"
|
67
|
+
exchange.publish runner.command, routing_key: queue.name, persistent: true, type: runner.options[:type]
|
68
|
+
|
69
|
+
EventMachine.add_timer(1) { connection.close { EventMachine.stop } }
|
70
|
+
end
|
71
|
+
"""
|
72
|
+
When I run the raq agent "producer.rb #{message} --queue #{queue}"
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
Given(/^(#{A_CONSUMER}) with:$/) do |consumer_path, consumer_implementation|
|
77
|
+
steps %{
|
78
|
+
Given a raq agent file named "#{consumer_path}" with:
|
79
|
+
"""
|
80
|
+
#{consumer_implementation}
|
81
|
+
"""
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
When(/^I run the raq agent "(.*?)"(, failing)?(, again)?$/) do |agent_run_string, failing, again|
|
86
|
+
running = lambda do
|
87
|
+
steps %{
|
88
|
+
When I #{'successfully ' unless failing || again}run `ruby -I#{full_current_dir} -I#{load_path} #{agent_run_string}`
|
89
|
+
}
|
90
|
+
|
91
|
+
if failing
|
92
|
+
steps %{
|
93
|
+
Then the exit status should not be 0
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if again
|
99
|
+
expect(&running).to raise_error(ChildProcess::TimeoutError)
|
100
|
+
else
|
101
|
+
running.call
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
When(/^I run (#{THE_CONSUMER}) on (#{QUEUE})(, failing)?(, again)?$/) do |consumer, queue, failing, again|
|
106
|
+
steps %{
|
107
|
+
When I run the raq agent "#{consumer} --queue #{queue}"#{failing}#{again}
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
Then(/^the output should contain (#{EXPECT_SOME_MESSAGE})$/) do |message|
|
112
|
+
steps %{
|
113
|
+
Then the output should contain "#{message}"
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
Then(/^it should never return$/) do
|
118
|
+
expect do
|
119
|
+
Timeout::timeout(exit_timeout) do
|
120
|
+
loop do
|
121
|
+
assert_not_exit_status(0)
|
122
|
+
assert_not_exit_status(1)
|
123
|
+
sleep(0.1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end.to raise_error(Timeout::Error)
|
127
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
begin
|
3
|
+
Bundler.setup(:default, :development)
|
4
|
+
rescue Bundler::BundlerError => e
|
5
|
+
$stderr.puts e.message
|
6
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
7
|
+
exit e.status_code
|
8
|
+
end
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
11
|
+
|
12
|
+
require 'rspec/expectations'
|
13
|
+
require 'aruba/cucumber'
|
14
|
+
|
15
|
+
require 'simplecov'
|
16
|
+
SimpleCov.command_name("features")
|
17
|
+
|
18
|
+
require 'raq'
|
19
|
+
|
20
|
+
module PryHelper
|
21
|
+
def pry(byending)
|
22
|
+
begin
|
23
|
+
byending.pry
|
24
|
+
rescue NoMethodError => e
|
25
|
+
require 'pry'
|
26
|
+
byending.pry
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module CoverageHelper
|
32
|
+
def simplecov_for(path)
|
33
|
+
%{
|
34
|
+
require 'simplecov'
|
35
|
+
SimpleCov.command_name(#{path.inspect})
|
36
|
+
SimpleCov.start
|
37
|
+
SimpleCov.root(#{File.join(File.dirname(__FILE__),'..','..').inspect})
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module RaqUnderTestHelper
|
43
|
+
def load_path
|
44
|
+
File.dirname(__FILE__) + '/../../lib'
|
45
|
+
end
|
46
|
+
|
47
|
+
def full_current_dir
|
48
|
+
File.expand_path(File.dirname(__FILE__) + '/../../' + current_dir)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module DependentServiceHelper
|
53
|
+
def start_amqp
|
54
|
+
unless rabbitmq_running?
|
55
|
+
system("rabbitmq-server &", out: "/dev/null", err: "/dev/null")
|
56
|
+
unless system("rabbitmqctl wait /usr/local/var/lib/rabbitmq/mnesia/rabbit@localhost.pid", out: "/dev/null", err: "/dev/null")
|
57
|
+
raise "Unable to start rabbitmq"
|
58
|
+
else
|
59
|
+
true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def rabbitmq_running?
|
65
|
+
system("rabbitmqctl status", out: "/dev/null", err: "/dev/null")
|
66
|
+
end
|
67
|
+
|
68
|
+
def stop_amqp
|
69
|
+
if rabbitmq_running?
|
70
|
+
unless system("rabbitmqctl stop", out: "/dev/null", err: "/dev/null")
|
71
|
+
raise "Unable to stop rabbitmq"
|
72
|
+
else
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module AgentSessionHelper
|
80
|
+
|
81
|
+
def new_queue(name=nil)
|
82
|
+
with_queues do
|
83
|
+
queue_name = name || (Digest::MD5.new << Time.now.to_s << rand(1000).to_s)
|
84
|
+
if @queues.include?(queue_name)
|
85
|
+
queue_name
|
86
|
+
else
|
87
|
+
@queues << queue_name
|
88
|
+
@queues.last
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def last_queue
|
94
|
+
with_queues do
|
95
|
+
@queues.last
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def with_queues
|
100
|
+
@queues ||= Array.new
|
101
|
+
yield
|
102
|
+
end
|
103
|
+
|
104
|
+
def current_message(new_message=nil)
|
105
|
+
with_messages do
|
106
|
+
@messages << new_message if new_message
|
107
|
+
@messages.last
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def last_message
|
112
|
+
with_messages do
|
113
|
+
@messages.last
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def with_messages
|
118
|
+
@messages ||= Array.new
|
119
|
+
yield
|
120
|
+
end
|
121
|
+
|
122
|
+
def new_consumer_path(fragment="consumer")
|
123
|
+
with_consumers do
|
124
|
+
@consumers << "#{fragment}#{@consumers.length}.rb"
|
125
|
+
@consumers.last
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def last_consumer_path(fragment=nil)
|
130
|
+
with_consumers do
|
131
|
+
if fragment
|
132
|
+
@consumers.reverse.find {|c| c.include?(fragment)}
|
133
|
+
else
|
134
|
+
@consumers.last
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def with_consumers
|
140
|
+
@consumers ||= Array.new
|
141
|
+
yield
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
World(PryHelper,RaqUnderTestHelper,DependentServiceHelper,AgentSessionHelper,CoverageHelper)
|
data/lib/raq.rb
ADDED
data/lib/raq/runner.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Raq
|
4
|
+
class Runner
|
5
|
+
attr_reader :options
|
6
|
+
attr_reader :command
|
7
|
+
attr_reader :connection_options
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@argv = argv
|
11
|
+
|
12
|
+
@options = {}
|
13
|
+
|
14
|
+
parse!
|
15
|
+
end
|
16
|
+
|
17
|
+
def parser
|
18
|
+
@parser ||= OptionParser.new do |opts|
|
19
|
+
opts.on("-H", "--host HOST", "AMQP HOST address (default: #{@options[:host]}") { |host| @options[:host] = host }
|
20
|
+
opts.on("-p", "--port PORT", "AMQP PORT") { |port| @options[:port] = port.to_i }
|
21
|
+
opts.on("-q", "--queue QUEUE","AMQP Queue to use") { |queue| @options[:queue] = queue }
|
22
|
+
opts.on("-u", "--user USER","AMQP User to connect as") { |user| @options[:user] = user }
|
23
|
+
|
24
|
+
opts.on("-t", "--type TYPE","AMQP message type") { |type| @options[:type] = type }
|
25
|
+
|
26
|
+
opts.on("-r", "--require LIBRARY","Require the provided library before starting") { |lib| require lib }
|
27
|
+
# ... and on
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse!
|
32
|
+
parser.parse! @argv
|
33
|
+
@command = @argv.shift
|
34
|
+
@connection_options = @options.reject {|k,v| [:queue, :type].include?(k) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/raq/server.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'raq/server/builder'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'amqp'
|
4
|
+
|
5
|
+
module Raq
|
6
|
+
class Server
|
7
|
+
CONNECTION_DEFAULTS = {host: "127.0.0.1"}
|
8
|
+
|
9
|
+
attr_reader :connection_options, :queue_names, :connection, :app
|
10
|
+
|
11
|
+
def initialize(options={}, app=nil, &block)
|
12
|
+
@queue_names = options.fetch(:queues) { raise ArgumentError, "You must provide a list of at least 1 queue to subscribe to." }
|
13
|
+
@connection_options = options.fetch(:connection,CONNECTION_DEFAULTS)
|
14
|
+
@app = app
|
15
|
+
@app = Server::Builder.new(&block).to_app if block
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
starter = proc do
|
20
|
+
connect
|
21
|
+
end
|
22
|
+
|
23
|
+
if EventMachine.reactor_running?
|
24
|
+
starter.call
|
25
|
+
else
|
26
|
+
EventMachine.run(&starter)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def connect
|
31
|
+
@connection = AMQP.connect(self.connection_options)
|
32
|
+
@channel = AMQP::Channel.new(@connection)
|
33
|
+
#@channel.prefetch(1)
|
34
|
+
@queues = Array(self.queue_names).collect do |queue_name|
|
35
|
+
queue = @channel.queue(queue_name, durable: true, auto_delete: false)
|
36
|
+
queue.subscribe(ack: true, &method(:handle_message))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_message(meta, payload)
|
41
|
+
@app.call(meta,payload)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Raq
|
2
|
+
class Server
|
3
|
+
class Builder
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@middleware = []
|
7
|
+
@app = proc {}
|
8
|
+
instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def use(middleware)
|
12
|
+
@middleware << middleware
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(app=nil,&block_as_app)
|
16
|
+
@app = app if app
|
17
|
+
@app = block_as_app if block_as_app
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_app
|
21
|
+
@middleware.inject(@app) do |app,middleware|
|
22
|
+
middleware.new(app)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/raq.gemspec
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "raq"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Caleb Buxton"]
|
12
|
+
s.date = "2013-07-04"
|
13
|
+
s.description = "The elegance of Rack with none of the unreliability of HTTP"
|
14
|
+
s.email = "me@cpb.ca"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".simplecov",
|
23
|
+
".travis.yml",
|
24
|
+
"Gemfile",
|
25
|
+
"Guardfile",
|
26
|
+
"LICENSE.txt",
|
27
|
+
"README.md",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"features/raq.feature",
|
31
|
+
"features/step_definitions/raq_steps.rb",
|
32
|
+
"features/support/env.rb",
|
33
|
+
"features/support/hooks.rb",
|
34
|
+
"lib/raq.rb",
|
35
|
+
"lib/raq/runner.rb",
|
36
|
+
"lib/raq/server.rb",
|
37
|
+
"lib/raq/server/builder.rb",
|
38
|
+
"raq.gemspec",
|
39
|
+
"spec/raq/runner_spec.rb",
|
40
|
+
"spec/raq/server_spec.rb",
|
41
|
+
"spec/raq_spec.rb",
|
42
|
+
"spec/spec_helper.rb",
|
43
|
+
"tmp/.gitkeep",
|
44
|
+
"tmp/consumer.rb",
|
45
|
+
"tmp/producer.rb",
|
46
|
+
"tmp/rack_hack.rb"
|
47
|
+
]
|
48
|
+
s.homepage = "http://github.com/cpb/raq"
|
49
|
+
s.licenses = ["MIT"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = "1.8.25"
|
52
|
+
s.summary = "Middleware for your AMQP Message Consumer"
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<eventmachine>, ["~> 1.0"])
|
59
|
+
s.add_runtime_dependency(%q<amqp>, ["~> 1.0"])
|
60
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.3.0"])
|
61
|
+
s.add_development_dependency(%q<guard>, ["~> 1.8.0"])
|
62
|
+
s.add_development_dependency(%q<guard-cucumber>, ["~> 1.4.0"])
|
63
|
+
s.add_development_dependency(%q<guard-rspec>, ["~> 3.0.2"])
|
64
|
+
s.add_development_dependency(%q<pry>, ["~> 0.9.12"])
|
65
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
66
|
+
s.add_development_dependency(%q<travis-lint>, ["~> 1.7.0"])
|
67
|
+
else
|
68
|
+
s.add_dependency(%q<eventmachine>, ["~> 1.0"])
|
69
|
+
s.add_dependency(%q<amqp>, ["~> 1.0"])
|
70
|
+
s.add_dependency(%q<bundler>, ["~> 1.3.0"])
|
71
|
+
s.add_dependency(%q<guard>, ["~> 1.8.0"])
|
72
|
+
s.add_dependency(%q<guard-cucumber>, ["~> 1.4.0"])
|
73
|
+
s.add_dependency(%q<guard-rspec>, ["~> 3.0.2"])
|
74
|
+
s.add_dependency(%q<pry>, ["~> 0.9.12"])
|
75
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
76
|
+
s.add_dependency(%q<travis-lint>, ["~> 1.7.0"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<eventmachine>, ["~> 1.0"])
|
80
|
+
s.add_dependency(%q<amqp>, ["~> 1.0"])
|
81
|
+
s.add_dependency(%q<bundler>, ["~> 1.3.0"])
|
82
|
+
s.add_dependency(%q<guard>, ["~> 1.8.0"])
|
83
|
+
s.add_dependency(%q<guard-cucumber>, ["~> 1.4.0"])
|
84
|
+
s.add_dependency(%q<guard-rspec>, ["~> 3.0.2"])
|
85
|
+
s.add_dependency(%q<pry>, ["~> 0.9.12"])
|
86
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
87
|
+
s.add_dependency(%q<travis-lint>, ["~> 1.7.0"])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'raq/runner'
|
3
|
+
|
4
|
+
describe Raq::Runner do
|
5
|
+
def argsv(command,arguments={})
|
6
|
+
arguments.inject([command]) do |m, (key,value)|
|
7
|
+
m << "--#{key}"
|
8
|
+
m << value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "options" do
|
13
|
+
it "should parse host" do
|
14
|
+
runner = described_class.new(%w(start --host 127.0.0.1))
|
15
|
+
|
16
|
+
expect(runner.options[:host]).to eql("127.0.0.1")
|
17
|
+
|
18
|
+
runner = described_class.new(%w(start -H host))
|
19
|
+
|
20
|
+
expect(runner.options[:host]).to eql("host")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should parse port" do
|
24
|
+
runner = described_class.new(%w(start --port 8080))
|
25
|
+
|
26
|
+
expect(runner.options[:port]).to eql(8080)
|
27
|
+
|
28
|
+
runner = described_class.new(%w(start -p 888))
|
29
|
+
|
30
|
+
expect(runner.options[:port]).to eql(888)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should parse user" do
|
34
|
+
runner = described_class.new(%w(start --user guest))
|
35
|
+
|
36
|
+
expect(runner.options[:user]).to eql("guest")
|
37
|
+
|
38
|
+
runner = described_class.new(%w(start -u gus))
|
39
|
+
|
40
|
+
expect(runner.options[:user]).to eql("gus")
|
41
|
+
end
|
42
|
+
|
43
|
+
# pass=>"[filtered]"
|
44
|
+
# auth_mechanism=>"PLAIN"
|
45
|
+
# vhost=>"/"
|
46
|
+
# timeout=>nil
|
47
|
+
# logging=>false
|
48
|
+
# ssl=>false
|
49
|
+
# broker=>nil
|
50
|
+
# frame_max=>131072
|
51
|
+
# heartbeat=>0}"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should parse specified command" do
|
55
|
+
runner = described_class.new(argsv("start"))
|
56
|
+
|
57
|
+
expect(runner.command).to eql("start")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should parse AMQP Connection options" do
|
61
|
+
runner = described_class.new(%w("start --host host.name --queue is.not.a.connection.option"))
|
62
|
+
|
63
|
+
expect(runner.connection_options).to_not include(:queue)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should abort on unknown command"
|
67
|
+
it "should exiit on empty command"
|
68
|
+
|
69
|
+
it "should require file" do
|
70
|
+
expect do
|
71
|
+
Raq::Runner.new(%w(start -r unexisting))
|
72
|
+
end.to raise_error(LoadError)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should remember requires"
|
76
|
+
it "should remember debug options"
|
77
|
+
it "should default debug, silent and trace to false"
|
78
|
+
end
|
79
|
+
|
80
|
+
describe Raq::Runner, "with config file" do
|
81
|
+
it "should load options from file"
|
82
|
+
it "should change directory after loading config"
|
83
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'raq/server'
|
3
|
+
|
4
|
+
describe Raq::Server, 'app builder' do
|
5
|
+
class ExampleMiddleware < Struct.new(:app)
|
6
|
+
def call(meta,payload)
|
7
|
+
self.app.call(meta,payload)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should build app from constructor" do
|
12
|
+
app = proc {}
|
13
|
+
server = described_class.new({queues: "queue.name"}, app)
|
14
|
+
|
15
|
+
server.app.should == app
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should build app from builder block" do
|
19
|
+
server = described_class.new queues: "queue.name" do
|
20
|
+
run(proc { |meta,payload| :works })
|
21
|
+
end
|
22
|
+
|
23
|
+
server.app.call({}).should == :works
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use middlewares in builder block" do
|
27
|
+
server = described_class.new queues: "queue.name" do
|
28
|
+
use ExampleMiddleware
|
29
|
+
run(proc { |meta,payload| :works })
|
30
|
+
end
|
31
|
+
|
32
|
+
server.app.class.should == ExampleMiddleware
|
33
|
+
server.app.call("meta","payload").should == :works
|
34
|
+
end
|
35
|
+
end
|
data/spec/raq_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'simplecov'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
data/tmp/.gitkeep
ADDED
File without changes
|
data/tmp/consumer.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
$LOAD_PATH << "./lib"
|
5
|
+
|
6
|
+
require 'raq/runner'
|
7
|
+
require 'raq/server'
|
8
|
+
|
9
|
+
runner = Raq::Runner.new(ARGV)
|
10
|
+
|
11
|
+
module Raq
|
12
|
+
class Pryable < Struct.new(:app)
|
13
|
+
def call(meta, payload)
|
14
|
+
if payload == "pry"
|
15
|
+
binding.pry
|
16
|
+
else
|
17
|
+
self.app.call(meta,payload)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class QuickAck < Struct.new(:app)
|
23
|
+
def call(meta, payload)
|
24
|
+
if payload == "ack me"
|
25
|
+
meta.ack
|
26
|
+
puts "Pre-acked #{payload}"
|
27
|
+
else
|
28
|
+
self.app.call(meta, payload)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class FailureNack < Struct.new(:app)
|
34
|
+
def call(meta, payload)
|
35
|
+
begin
|
36
|
+
self.app.call(meta, payload)
|
37
|
+
meta.ack
|
38
|
+
puts "acked #{payload}"
|
39
|
+
rescue => e
|
40
|
+
puts "Got #{e}, not acking"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
server = Raq::Server.new(connection: runner.connection_options, queues: Array(runner.options[:queue])) do
|
47
|
+
use Raq::QuickAck
|
48
|
+
use Raq::FailureNack
|
49
|
+
use Raq::Pryable
|
50
|
+
|
51
|
+
run do |meta,payload|
|
52
|
+
puts "Received a message: #{meta} #{payload}"
|
53
|
+
raise "flaky ruby, you can't even scale" if rand(5) == 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
server.run
|
58
|
+
|
data/tmp/producer.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "amqp"
|
6
|
+
|
7
|
+
require './lib/raq/runner'
|
8
|
+
|
9
|
+
runner = Raq::Runner.new(ARGV)
|
10
|
+
|
11
|
+
EventMachine.run do
|
12
|
+
connection = AMQP.connect(runner.connection_options)
|
13
|
+
|
14
|
+
puts "Connected to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
|
15
|
+
|
16
|
+
channel = AMQP::Channel.new(connection)
|
17
|
+
queue = channel.queue(runner.options[:queue], durable: true, auto_delete: false)
|
18
|
+
exchange = channel.direct("")
|
19
|
+
|
20
|
+
puts "publshing #{runner.command} to #{queue.name}"
|
21
|
+
exchange.publish runner.command, routing_key: queue.name, persistent: true, type: runner.options[:type]
|
22
|
+
|
23
|
+
EventMachine.add_timer(1) { connection.close { EventMachine.stop } }
|
24
|
+
end
|
data/tmp/rack_hack.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "amqp"
|
6
|
+
|
7
|
+
$LOAD_PATH << "./lib"
|
8
|
+
|
9
|
+
require 'raq/runner'
|
10
|
+
require 'raq/server'
|
11
|
+
|
12
|
+
runner = Raq::Runner.new(ARGV)
|
13
|
+
|
14
|
+
require 'rack'
|
15
|
+
require 'sinatra/base'
|
16
|
+
|
17
|
+
class Resty < Sinatra::Base
|
18
|
+
post("/foo.:fragment.:type") do
|
19
|
+
puts "oh yeah"
|
20
|
+
puts params.inspect
|
21
|
+
status 200
|
22
|
+
end
|
23
|
+
|
24
|
+
not_found do
|
25
|
+
binding.pry
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
server = Raq::Server.new(connection: runner.connection_options, queues: Array(runner.options[:queue])) do
|
30
|
+
resty = Resty.new
|
31
|
+
|
32
|
+
run do |meta,payload|
|
33
|
+
status, headers, response = resty.call({"REQUEST_METHOD" => "POST",
|
34
|
+
"SCRIPT_NAME" => "",
|
35
|
+
"PATH_INFO" => meta.type,
|
36
|
+
"QUERY_STRING" => "",
|
37
|
+
"SERVER_NAME" => "127.0.0.1",
|
38
|
+
"SERVER_PORT" => 80,
|
39
|
+
"rack.version" => Rack::VERSION,
|
40
|
+
"rack.url_scheme" => "http",
|
41
|
+
"rack.input" => StringIO.new(payload),
|
42
|
+
"rack.errors" => StringIO.new,
|
43
|
+
"rack.multithread" => false,
|
44
|
+
"rack.multiprocess" => false,
|
45
|
+
"rack.run_once" => false})
|
46
|
+
|
47
|
+
if status == 200
|
48
|
+
meta.ack
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
server.run
|
54
|
+
|
metadata
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: raq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Caleb Buxton
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.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: '1.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: amqp
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.0'
|
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'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.3.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.8.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.8.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: guard-cucumber
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.4.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.4.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: guard-rspec
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 3.0.2
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 3.0.2
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: pry
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.9.12
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.9.12
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rdoc
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '3.12'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '3.12'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: travis-lint
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.7.0
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.7.0
|
158
|
+
description: The elegance of Rack with none of the unreliability of HTTP
|
159
|
+
email: me@cpb.ca
|
160
|
+
executables: []
|
161
|
+
extensions: []
|
162
|
+
extra_rdoc_files:
|
163
|
+
- LICENSE.txt
|
164
|
+
- README.md
|
165
|
+
files:
|
166
|
+
- .document
|
167
|
+
- .rspec
|
168
|
+
- .simplecov
|
169
|
+
- .travis.yml
|
170
|
+
- Gemfile
|
171
|
+
- Guardfile
|
172
|
+
- LICENSE.txt
|
173
|
+
- README.md
|
174
|
+
- Rakefile
|
175
|
+
- VERSION
|
176
|
+
- features/raq.feature
|
177
|
+
- features/step_definitions/raq_steps.rb
|
178
|
+
- features/support/env.rb
|
179
|
+
- features/support/hooks.rb
|
180
|
+
- lib/raq.rb
|
181
|
+
- lib/raq/runner.rb
|
182
|
+
- lib/raq/server.rb
|
183
|
+
- lib/raq/server/builder.rb
|
184
|
+
- raq.gemspec
|
185
|
+
- spec/raq/runner_spec.rb
|
186
|
+
- spec/raq/server_spec.rb
|
187
|
+
- spec/raq_spec.rb
|
188
|
+
- spec/spec_helper.rb
|
189
|
+
- tmp/.gitkeep
|
190
|
+
- tmp/consumer.rb
|
191
|
+
- tmp/producer.rb
|
192
|
+
- tmp/rack_hack.rb
|
193
|
+
homepage: http://github.com/cpb/raq
|
194
|
+
licenses:
|
195
|
+
- MIT
|
196
|
+
post_install_message:
|
197
|
+
rdoc_options: []
|
198
|
+
require_paths:
|
199
|
+
- lib
|
200
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
segments:
|
207
|
+
- 0
|
208
|
+
hash: 705315900894353883
|
209
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
|
+
none: false
|
211
|
+
requirements:
|
212
|
+
- - ! '>='
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
requirements: []
|
216
|
+
rubyforge_project:
|
217
|
+
rubygems_version: 1.8.25
|
218
|
+
signing_key:
|
219
|
+
specification_version: 3
|
220
|
+
summary: Middleware for your AMQP Message Consumer
|
221
|
+
test_files: []
|