amqp-utils 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/Rakefile CHANGED
@@ -1,4 +1,68 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
3
-
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "amqp-utils"
8
+ gem.summary = %Q{Command line utilities for interacting with AMQP compliant queues}
9
+ gem.description = %Q{Command line utilies for interacting with AMQP compliant queues.
10
+ The intention is provide simple management tools that can be used to complete ad hoc
11
+ housework on an AMQP queue. In addition, simple scripts can be layered over the tools
12
+ when needed.}
13
+ gem.email = "dougbarth@gmail.com"
14
+ gem.homepage = "http://github.com/dougbarth/amqp-utils"
15
+ gem.authors = ["Doug Barth"]
16
+ gem.rubyforge_project = "amqp-utils"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+
19
+ gem.add_dependency('tmm1-amqp', '~> 0.6.4')
20
+ gem.add_dependency('trollop', '~> 1.10.2')
21
+ gem.add_dependency('facets', '~> 2.7.0')
22
+ gem.add_dependency('clio', '~> 0.3.0')
23
+ gem.add_dependency('json', '~> 1.1.6')
24
+ end
25
+ Jeweler::RubyforgeTasks.new do |rubyforge|
26
+ rubyforge.doc_task = "rdoc"
27
+ end
28
+ rescue LoadError
29
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
30
+ end
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/*_test.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ begin
40
+ require 'rcov/rcovtask'
41
+ Rcov::RcovTask.new do |test|
42
+ test.libs << 'test'
43
+ test.pattern = 'test/**/*_test.rb'
44
+ test.verbose = true
45
+ end
46
+ rescue LoadError
47
+ task :rcov do
48
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
49
+ end
50
+ end
51
+
52
+ task :test => :check_dependencies
53
+
54
+ task :default => :test
55
+
56
+ require 'rake/rdoctask'
57
+ Rake::RDocTask.new do |rdoc|
58
+ if File.exist?('VERSION')
59
+ version = File.read('VERSION')
60
+ else
61
+ version = ""
62
+ end
63
+
64
+ rdoc.rdoc_dir = 'rdoc'
65
+ rdoc.title = "amqp-utils #{version}"
66
+ rdoc.rdoc_files.include('README*')
67
+ rdoc.rdoc_files.include('lib/**/*.rb')
68
+ end
data/TODO.txt ADDED
@@ -0,0 +1,7 @@
1
+ [x] fix amqp-enqueue message count
2
+ [x] detect commandline ARGV when no queue is specified bring up help
3
+ [x] amqp-stat hangs when no queue specified
4
+ [x] amqp-enqueue inspect error when message not specified
5
+ [o] amqp-dequeue hangs on exit
6
+ [o] setup.rb seems to install command.rb in wrong location
7
+ [o] gem create
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,79 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
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 = %q{amqp-utils}
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Doug Barth"]
12
+ s.date = %q{2009-11-13}
13
+ s.description = %q{Command line utilies for interacting with AMQP compliant queues.
14
+ The intention is provide simple management tools that can be used to complete ad hoc
15
+ housework on an AMQP queue. In addition, simple scripts can be layered over the tools
16
+ when needed.}
17
+ s.email = %q{dougbarth@gmail.com}
18
+ s.executables = ["amqp-deleteq", "amqp-dequeue", "amqp-enqueue", "amqp-peek", "amqp-pop", "amqp-purge", "amqp-statq"]
19
+ s.extra_rdoc_files = [
20
+ "README.txt"
21
+ ]
22
+ s.files = [
23
+ ".gitignore",
24
+ "History.txt",
25
+ "License.txt",
26
+ "README.txt",
27
+ "Rakefile",
28
+ "TODO.txt",
29
+ "VERSION",
30
+ "amqp-utils.gemspec",
31
+ "bin/amqp-deleteq",
32
+ "bin/amqp-dequeue",
33
+ "bin/amqp-enqueue",
34
+ "bin/amqp-peek",
35
+ "bin/amqp-pop",
36
+ "bin/amqp-purge",
37
+ "bin/amqp-statq",
38
+ "lib/amqp_utils.rb",
39
+ "lib/amqp_utils/command.rb",
40
+ "lib/amqp_utils/message_formatter.rb",
41
+ "test/test_amqp_utils.rb",
42
+ "test/test_helper.rb"
43
+ ]
44
+ s.homepage = %q{http://github.com/dougbarth/amqp-utils}
45
+ s.rdoc_options = ["--charset=UTF-8"]
46
+ s.require_paths = ["lib"]
47
+ s.rubyforge_project = %q{amqp-utils}
48
+ s.rubygems_version = %q{1.3.5}
49
+ s.summary = %q{Command line utilities for interacting with AMQP compliant queues}
50
+ s.test_files = [
51
+ "test/test_amqp_utils.rb",
52
+ "test/test_helper.rb"
53
+ ]
54
+
55
+ if s.respond_to? :specification_version then
56
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
57
+ s.specification_version = 3
58
+
59
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
60
+ s.add_runtime_dependency(%q<tmm1-amqp>, ["~> 0.6.4"])
61
+ s.add_runtime_dependency(%q<trollop>, ["~> 1.10.2"])
62
+ s.add_runtime_dependency(%q<facets>, ["~> 2.7.0"])
63
+ s.add_runtime_dependency(%q<clio>, ["~> 0.3.0"])
64
+ s.add_runtime_dependency(%q<json>, ["~> 1.1.6"])
65
+ else
66
+ s.add_dependency(%q<tmm1-amqp>, ["~> 0.6.4"])
67
+ s.add_dependency(%q<trollop>, ["~> 1.10.2"])
68
+ s.add_dependency(%q<facets>, ["~> 2.7.0"])
69
+ s.add_dependency(%q<clio>, ["~> 0.3.0"])
70
+ s.add_dependency(%q<json>, ["~> 1.1.6"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<tmm1-amqp>, ["~> 0.6.4"])
74
+ s.add_dependency(%q<trollop>, ["~> 1.10.2"])
75
+ s.add_dependency(%q<facets>, ["~> 2.7.0"])
76
+ s.add_dependency(%q<clio>, ["~> 0.3.0"])
77
+ s.add_dependency(%q<json>, ["~> 1.1.6"])
78
+ end
79
+ end
data/bin/amqp-deleteq CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
4
 
@@ -7,12 +7,16 @@ class QueueDeleteCommand < AmqpUtils::Command
7
7
  options.banner %Q{
8
8
  |Deletes the supplied queues.
9
9
  |
10
- | #{command_name} <queue>[, <another queue>, ...]
10
+ | #{command_name} <queue> [<another queue> ...]
11
11
  }.margin
12
12
  end
13
13
 
14
+ def validate
15
+ raise "need at least one queue name" unless args[0] && !args[0].empty?
16
+ end
17
+
14
18
  def execute
15
- @queues = ARGV
19
+ @queues = args
16
20
  def @queues.delete_or_stop
17
21
  queue = pop
18
22
  if queue
data/bin/amqp-dequeue CHANGED
@@ -1,28 +1,40 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
+ require File.dirname(__FILE__) + '/../lib/amqp_utils/message_formatter'
4
5
 
5
6
  class DequeueCommand < AmqpUtils::Command
6
7
  def prepare_options(options)
7
8
  options.banner %Q{
8
9
  |Removes messages from the supplied queues and displays them on STDOUT.
9
10
  |
10
- | #{command_name} <queue>[, <another queue>, ...]
11
+ | #{command_name} <queue> [<another queue> ...]
12
+ |
13
+ |Dequeue options:
11
14
  }.margin
15
+ options.opt :format, 'The format that the messages should be displayed as',
16
+ :short => :none, :default => 'pretty'
17
+ options.opt :quiet, 'Suppresses non-message content output',
18
+ :short => 'q', :default => false
19
+ end
20
+
21
+ def validate
22
+ raise "need at least one queue name" if args.empty?
12
23
  end
13
24
 
14
25
  def execute
15
- @queues = ARGV
26
+ @queues = args
27
+ @formatter = AmqpUtils::MessageFormatter.for_type(options[:format])
28
+
16
29
  @queues.each do |queue|
17
- puts "Dequeueing from #{queue}..."
30
+ puts "Dequeueing from #{queue} (^C to stop)..." unless options[:quiet]
18
31
  mq = MQ.new
19
32
 
20
33
  process_message = lambda do |header, message|
21
- puts "(#{queue})"
22
- puts " Header: #{header.properties.inspect}"
23
- puts " Message: #{message.inspect}"
34
+ puts "(#{queue})" unless options[:quiet]
35
+ @formatter.generate(STDOUT, header, message)
24
36
  end
25
- mq.queue(queue).subscribe &process_message
37
+ mq.queue(queue).subscribe(&process_message)
26
38
  end
27
39
  end
28
40
  end
data/bin/amqp-enqueue CHANGED
@@ -1,6 +1,7 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
+ require 'clio/progressbar'
4
5
 
5
6
  class EnqueueCommand < AmqpUtils::Command
6
7
  def prepare_options(options)
@@ -11,28 +12,35 @@ class EnqueueCommand < AmqpUtils::Command
11
12
  |
12
13
  |Enqueue options:
13
14
  }.margin
14
- options.opt :persistent, 'Mark messages as persistent.'
15
+ options.opt :persistent, 'Mark messages as persistent.', :short => :none
15
16
  options.opt :count, 'Number of times the message should be published.', :type => :int, :default => 1
16
17
  end
17
18
 
19
+ def validate
20
+ raise "need a queue to publish to" unless args[0] && !args[0].empty?
21
+ raise "need a message to publish" unless args[1] && !args[1].empty?
22
+ end
23
+
18
24
  def execute
19
- @queue = ARGV[0]
20
- @message = ARGV[1]
25
+ @queue, @message = args
21
26
 
22
27
  publisher = EM.spawn do |queue, message, messages, options|
28
+ @progress ||= Clio::Progressbar.new('Enqueuing', options[:count])
29
+
23
30
  if messages > 0
24
31
  @mq ||= MQ.new
25
32
  @mq.queue(queue, :durable => true, :auto_delete => false).
26
33
  publish(message, :persistent => options.persistent)
27
34
 
28
- print '.'; STDOUT.flush
35
+ @progress.inc
29
36
  publisher.notify(queue, message, messages - 1, options)
30
37
  else
38
+ @progress.finish
31
39
  EM.next_tick { AMQP.stop { EM.stop } }
32
40
  end
33
41
  end
34
42
 
35
- publisher.notify(@queue, @message, options.count, options)
43
+ publisher.notify(@queue, @message, options[:count], options)
36
44
  end
37
45
  end
38
46
 
data/bin/amqp-peek CHANGED
@@ -1,6 +1,7 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
+ require File.dirname(__FILE__) + '/../lib/amqp_utils/message_formatter'
4
5
 
5
6
  class PeekCommand < AmqpUtils::Command
6
7
  def prepare_options(options)
@@ -12,22 +13,26 @@ class PeekCommand < AmqpUtils::Command
12
13
  | NOTE: This operation does have side effects on the server. The message is
13
14
  | placed at the end of the queue after returning from this method. The
14
15
  | message is also marked as being redelivered.
16
+ |
17
+ |Peek options:
15
18
  }.margin
19
+ options.opt :format, 'The format that the messages should be displayed as',
20
+ :short => :none, :default => 'pretty'
21
+ end
22
+
23
+ def validate
24
+ raise "need a queue name" unless args[0] && !args[0].empty?
16
25
  end
17
26
 
18
27
  def execute
19
28
  @queue = args[0]
29
+ @formatter = AmqpUtils::MessageFormatter.for_type(options[:format])
20
30
 
21
31
  mq = MQ.new
22
-
23
32
  mq.queue(@queue).pop(:ack => true) do |header, message|
24
33
  if message
25
34
  puts "(#{@queue})"
26
- puts " Header: "
27
- header.properties.each do |key, value|
28
- puts " #{key.inspect} => #{value.inspect}"
29
- end
30
- puts " Message: #{message.inspect}"
35
+ @formatter.generate(STDOUT, header, message)
31
36
  else
32
37
  puts "(#{@queue}) empty"
33
38
  end
data/bin/amqp-pop CHANGED
@@ -1,6 +1,7 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
+ require File.dirname(__FILE__) + '/../lib/amqp_utils/message_formatter'
4
5
 
5
6
  class PopCommand < AmqpUtils::Command
6
7
  def prepare_options(options)
@@ -8,21 +9,26 @@ class PopCommand < AmqpUtils::Command
8
9
  |Pops a single message from the queue.
9
10
  |
10
11
  | #{command_name} <queue>
12
+ |
13
+ |Pop options:
11
14
  }.margin
15
+ options.opt :format, 'The format that the messages should be displayed as',
16
+ :short => :none, :default => 'pretty'
17
+ end
18
+
19
+ def validate
20
+ raise "need a queue name" unless args[0] && !args[0].empty?
12
21
  end
13
22
 
14
23
  def execute
15
24
  @queue = args[0]
25
+ @formatter = AmqpUtils::MessageFormatter.for_type(options[:format])
16
26
 
17
27
  mq = MQ.new
18
28
  mq.queue(@queue).pop do |header, message|
19
29
  if message
20
30
  puts "(#{@queue})"
21
- puts " Header: "
22
- header.properties.each do |key, value|
23
- puts " #{key.inspect} => #{value.inspect}"
24
- end
25
- puts " Message: #{message.inspect}"
31
+ @formatter.generate(STDOUT, header, message)
26
32
  else
27
33
  puts "(#{@queue}) empty"
28
34
  end
data/bin/amqp-purge ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
+
5
+ # Monkey patching in purge support.
6
+ #
7
+ # TODO: submit this change back to the AMQP project.
8
+ class MQ::Queue
9
+ def purge(options = {})
10
+ @mq.callback {
11
+ @mq.send Protocol::Queue::Purge.new({:queue => name,
12
+ :nowait => true}.merge(options))
13
+ }
14
+ end
15
+ end
16
+
17
+ class PurgeCommand < AmqpUtils::Command
18
+ def prepare_options(options)
19
+ options.banner %Q{
20
+ |Removes all messages from the supplied queues.
21
+ |
22
+ | #{command_name} <queue> [<another queue> ...]
23
+ }.margin
24
+ end
25
+
26
+ def validate
27
+ raise "need at least one queue name" if args.empty?
28
+ end
29
+
30
+ def execute
31
+ mq = MQ.new
32
+ @queues = args
33
+ purge_one = lambda do
34
+ queue = @queues.shift
35
+ puts "Purging #{queue}..."
36
+ mq.queue(queue).purge
37
+ if @queues.empty?
38
+ AMQP.stop { EM.stop }
39
+ else
40
+ EM.next_tick(&purge_one)
41
+ end
42
+ end
43
+ EM.next_tick(&purge_one)
44
+ end
45
+ end
46
+
47
+ PurgeCommand.run
data/bin/amqp-statq CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/amqp_utils/command'
4
4
 
@@ -7,15 +7,19 @@ class QueueStatCommand < AmqpUtils::Command
7
7
  options.banner %Q{
8
8
  |Gets statistics on the queues specified.
9
9
  |
10
- | #{command_name} <queue> [<another_queue> ..]
10
+ | #{command_name} <queue> [<another_queue> ...]
11
11
  }.margin
12
12
  end
13
13
 
14
+ def validate
15
+ raise "need at least one queue name" unless args[0] && !args[0].empty?
16
+ end
17
+
14
18
  def execute
15
- @queues = ARGV
19
+ @queues = args
16
20
 
17
21
  @queues.each do |queue|
18
- MQ.new.queue(queue).status do |size, consumers|
22
+ mq.queue(queue, :passive => true).status do |size, consumers|
19
23
  puts "Queue <#{queue}>: #{size} message(s), #{consumers} consumer(s)"
20
24
 
21
25
  @queues.delete(queue)
@@ -1,10 +1,19 @@
1
1
  require File.dirname(__FILE__) + '/../amqp_utils'
2
2
 
3
3
  class AmqpUtils::Command
4
+ include Clio::Terminal
5
+
4
6
  class << self
5
7
  def run(args = ARGV)
6
8
  command = new(args)
7
9
  command.process_options
10
+
11
+ begin
12
+ command.validate
13
+ rescue RuntimeError => e
14
+ Trollop::die(e.message)
15
+ end
16
+
8
17
  command.go
9
18
  end
10
19
  end
@@ -13,25 +22,43 @@ class AmqpUtils::Command
13
22
  @args = args
14
23
  end
15
24
 
25
+ def version
26
+ IO.read(File.join(File.dirname(__FILE__), '..', '..', 'VERSION'))
27
+ end
28
+
16
29
  attr_reader :args, :options
17
30
 
18
31
  def process_options
19
32
  command = self
20
33
  @options = Trollop::options(@args) do
34
+ version(command.version)
21
35
  command.prepare_options(self) if command.respond_to?(:prepare_options)
22
36
 
23
37
  banner %Q{
24
38
  |
25
39
  |Standard options:
26
40
  }.margin
27
- opt :host, 'The AMQP host to connect to', :default => 'localhost'
28
- opt :port, 'The AMQP port to connect to', :default => 5672
29
- opt :vhost, 'The vhost to connect to', :default => '/'
30
- opt :user, 'The user name to authenticate with', :default => 'guest'
31
- opt :password, 'The password to connect with', :default => 'guest'
32
- opt :timeout, 'The connect timeout in seconds', :default => 5
33
- opt :verbose, 'Print all AMQP commands sent and received.'
41
+ opt :host, 'The AMQP host to connect to', :short => 'H', :default => 'localhost'
42
+ opt :port, 'The AMQP port to connect to', :short => 'P', :default => 5672
43
+ opt :vhost, 'The vhost to connect to', :short => 'V', :default => '/'
44
+ opt :user, 'The user name to authenticate with', :default => 'guest', :short => 'u'
45
+ opt :prompt, 'Prompt the user for a password', :short => 'p'
46
+ opt :password, 'The password to connect with.', :default => 'guest', :short => :none
47
+ conflicts(:prompt, :password)
48
+ opt :timeout, 'The connect timeout in seconds', :default => 5, :short => 't'
49
+ opt :verbose, 'Print all AMQP commands sent and received.', :short => 'v'
34
50
  end
51
+
52
+ @args = @args.dup
53
+ ARGV.clear
54
+ end
55
+
56
+ # Called to validate that the supplied command line options and arguments
57
+ # are valid. If there is a problem with the supplied values, and exception
58
+ # should be raised.
59
+ #
60
+ # Subclasses show override this method and do their validation.
61
+ def validate
35
62
  end
36
63
 
37
64
  def command_name
@@ -39,23 +66,42 @@ class AmqpUtils::Command
39
66
  end
40
67
 
41
68
  def go
42
- EM.run do
43
- %w(host port vhost user timeout).each do |val|
44
- AMQP.settings[val.to_sym] = options[val.to_sym]
69
+ if options.prompt
70
+ options[:password] = password()
71
+ puts
72
+ end
73
+
74
+ %w(host port vhost user timeout).each do |val|
75
+ AMQP.settings[val.to_sym] = options[val.to_sym]
76
+ end
77
+ AMQP.settings[:pass] = options.password
78
+ AMQP.logging = options.verbose
79
+
80
+ trap("INT") do
81
+ if @nice_tried
82
+ EM.stop
83
+ else
84
+ AMQP.stop { EM.stop }
85
+ @nice_tried = true
45
86
  end
46
- AMQP.settings[:pass] = options.password
47
- AMQP.logging = options.verbose
48
-
49
- trap("INT") do
50
- if @nice_tried
51
- EM.stop
52
- else
53
- AMQP.stop { EM.stop }
54
- @nice_tried = true
87
+ end
88
+
89
+ EM.run do
90
+ amqp.connection_status do |status|
91
+ if status == :disconnected
92
+ Trollop::die "disconnected from #{AMQP.settings[:host]}:#{AMQP.settings[:port]}"
55
93
  end
56
94
  end
57
95
 
58
- execute
96
+ mq.callback { execute }
59
97
  end
60
98
  end
99
+
100
+ def amqp
101
+ @amqp ||= AMQP.start
102
+ end
103
+
104
+ def mq
105
+ @mq ||= MQ.new
106
+ end
61
107
  end
@@ -0,0 +1,42 @@
1
+ class AmqpUtils::MessageFormatter
2
+ class << self
3
+ @@formatters ||= {}
4
+
5
+ def for_type(format_type)
6
+ @@formatters[format_type.downcase].new
7
+ end
8
+
9
+ def register_formatter(formatter, format_type)
10
+ @@formatters[format_type.downcase] = formatter
11
+ end
12
+ end
13
+
14
+ class Base
15
+ def self.inherited(klass)
16
+ ::AmqpUtils::MessageFormatter.register_formatter(klass, klass.basename)
17
+ end
18
+ end
19
+
20
+ class Pretty < Base
21
+ def generate(io, header, message)
22
+ io.puts " Header: "
23
+ header.properties.each do |key, value|
24
+ io.puts " #{key.inspect} => #{value.inspect}"
25
+ end
26
+ io.puts " Message: #{message.inspect}"
27
+ end
28
+ end
29
+
30
+ class JSON < Base
31
+ def generate(io, header, message)
32
+ json_obj = {'header' => header.properties, 'message' => message}
33
+ io.puts ::JSON.generate(json_obj)
34
+ end
35
+ end
36
+
37
+ class Message < Base
38
+ def generate(io, header, message)
39
+ io.puts message
40
+ end
41
+ end
42
+ end
data/lib/amqp_utils.rb CHANGED
@@ -10,8 +10,16 @@ require 'rubygems'
10
10
  gem 'trollop'
11
11
  require 'trollop'
12
12
 
13
- gem 'amqp'
13
+ gem 'tmm1-amqp'
14
14
  require 'mq'
15
15
 
16
16
  gem 'facets'
17
17
  require 'facets/string/tab'
18
+ require 'facets/kernel/returning'
19
+ require 'facets/module/basename'
20
+
21
+ gem 'clio'
22
+ require 'clio/consoleutils'
23
+
24
+ gem 'json'
25
+ require 'json'