raq 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --profile
@@ -0,0 +1,3 @@
1
+ SimpleCov.start do
2
+ add_filter "/features/"
3
+ end
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ rvm:
4
+ - 1.9.3
5
+ notifications:
6
+ hipchat: f99d2e5a191d805ae29ae7b5811eea@AZ dev
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
@@ -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
@@ -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.
@@ -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
+
@@ -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)
@@ -0,0 +1,8 @@
1
+ Before("@amqp") do |scenario|
2
+ start_amqp
3
+ end
4
+
5
+ at_exit do
6
+ include DependentServiceHelper
7
+ stop_amqp
8
+ end
@@ -0,0 +1,4 @@
1
+ module Raq
2
+ require 'raq/runner'
3
+ require 'raq/server'
4
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,8 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'raq'
3
+
4
+ describe "Raq" do
5
+ it "fails" do
6
+ expect(Raq).to be_a(Module)
7
+ end
8
+ end
@@ -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
File without changes
@@ -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
+
@@ -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
@@ -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: []