amqp 0.7.0.pre → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG +8 -2
  4. data/CONTRIBUTORS +22 -0
  5. data/Gemfile +3 -3
  6. data/README.md +20 -11
  7. data/Rakefile +30 -6
  8. data/amqp.gemspec +1 -1
  9. data/bin/cleanify.rb +50 -0
  10. data/examples/amqp/simple.rb +6 -4
  11. data/examples/mq/ack.rb +8 -6
  12. data/examples/mq/automatic_binding_for_default_direct_exchange.rb +65 -0
  13. data/examples/mq/callbacks.rb +9 -1
  14. data/examples/mq/clock.rb +17 -17
  15. data/examples/mq/hashtable.rb +19 -10
  16. data/examples/mq/internal.rb +13 -11
  17. data/examples/mq/logger.rb +38 -36
  18. data/examples/mq/multiclock.rb +16 -7
  19. data/examples/mq/pingpong.rb +16 -7
  20. data/examples/mq/pop.rb +8 -6
  21. data/examples/mq/primes-simple.rb +2 -0
  22. data/examples/mq/primes.rb +7 -5
  23. data/examples/mq/stocks.rb +14 -5
  24. data/lib/amqp.rb +12 -8
  25. data/lib/amqp/buffer.rb +35 -158
  26. data/lib/amqp/client.rb +34 -22
  27. data/lib/amqp/frame.rb +8 -64
  28. data/lib/amqp/protocol.rb +21 -70
  29. data/lib/amqp/server.rb +11 -9
  30. data/lib/amqp/spec.rb +8 -6
  31. data/lib/amqp/version.rb +2 -0
  32. data/lib/ext/blankslate.rb +3 -1
  33. data/lib/ext/em.rb +2 -0
  34. data/lib/ext/emfork.rb +13 -11
  35. data/lib/mq.rb +253 -156
  36. data/lib/mq/collection.rb +6 -88
  37. data/lib/mq/exchange.rb +70 -13
  38. data/lib/mq/header.rb +12 -6
  39. data/lib/mq/logger.rb +9 -7
  40. data/lib/mq/queue.rb +42 -30
  41. data/lib/mq/rpc.rb +6 -4
  42. data/protocol/codegen.rb +20 -18
  43. data/research/api.rb +10 -46
  44. data/research/primes-forked.rb +9 -7
  45. data/research/primes-processes.rb +74 -72
  46. data/research/primes-threaded.rb +9 -7
  47. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +61 -0
  48. data/spec/mq_helper.rb +70 -0
  49. data/spec/spec_helper.rb +84 -29
  50. data/spec/unit/amqp/buffer_spec.rb +178 -0
  51. data/spec/unit/amqp/client_spec.rb +472 -0
  52. data/spec/unit/amqp/frame_spec.rb +60 -0
  53. data/spec/unit/amqp/misc_spec.rb +123 -0
  54. data/spec/unit/amqp/protocol_spec.rb +53 -0
  55. data/spec/unit/mq/channel_close_spec.rb +15 -0
  56. data/spec/unit/mq/collection_spec.rb +129 -0
  57. data/spec/unit/mq/exchange_declaration_spec.rb +524 -0
  58. data/spec/unit/mq/misc_spec.rb +228 -0
  59. data/spec/unit/mq/mq_basic_spec.rb +39 -0
  60. data/spec/unit/mq/queue_declaration_spec.rb +97 -0
  61. data/spec/unit/mq/queue_spec.rb +71 -0
  62. metadata +33 -21
  63. data/Gemfile.lock +0 -16
  64. data/old/README +0 -30
  65. data/old/Rakefile +0 -12
  66. data/old/amqp-0.8.json +0 -606
  67. data/old/amqp_spec.rb +0 -796
  68. data/old/amqpc.rb +0 -695
  69. data/old/codegen.rb +0 -148
  70. data/spec/channel_close_spec.rb +0 -13
  71. data/spec/sync_async_spec.rb +0 -52
data/.gitignore CHANGED
@@ -2,5 +2,9 @@
2
2
  *.rbc
3
3
  ~*
4
4
  #*
5
+ *~
5
6
  .rvmrc
6
7
  .bundle
8
+ Gemfile.lock
9
+
10
+ spec/amqp.yml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
data/CHANGELOG CHANGED
@@ -1,5 +1,11 @@
1
1
  = Version 0.7
2
2
  * [BUG] Sync API for queues and exchanges, support for server-generated queues & exchange names (via semi-lazy collection).
3
3
  * [BUG] Sync API for MQ#close (Channel.Close) [issue #34].
4
- * [FEATURE] From majek's fork, with some fixes. Example: AMQP.start("amqps://")
5
- * [DEVELOP] some em-spec-based specs, bin/irb, Gemfile.
4
+ * [FEATURE] AMQP URL from majek's fork, with some fixes. Example: AMQP.start("amqps://")
5
+ * [DEVELOP] Added some em-spec-based specs, bin/irb, Gemfile.
6
+ * [FEATURE] Added MQ::Exchange.default for the default exchange.
7
+ * [FEATURE] Raise an exception if we're trying to use Basic.Reject with RabbitMQ.
8
+ * [FEATURE] Fail if an entity is re-declared with different options.
9
+ * [BUG] Don't reconnect if the credentials are wrong.
10
+ * [BUG] Fixed an exception which occurred when Queue#bind was called synchronously with a callback.
11
+ * [DEVELOPMENT] Added a lot of specs (Bacon replaced by rSpec 2).
@@ -0,0 +1,22 @@
1
+ Aman Gupta: 232
2
+ Jakub Šťastný aka Botanicus: 57
3
+ arvicco: 26
4
+ Michael S. Klishin: 17
5
+ Chuck Remes: 7
6
+ Marek Majkowski: 4
7
+ Chris Van Pelt: 3
8
+ Jake Douglas: 3
9
+ Doug Barth: 2
10
+ coderrr: 2
11
+ binary42: 1
12
+ Daniel Neighman: 1
13
+ Brendan Ribera: 1
14
+ dan: 1
15
+ Dane Jensen: 1
16
+ Tony Garnock-Jones: 1
17
+ Phil Smith: 1
18
+ Kasper Bjørn Nielsen: 1
19
+ Simon Horne: 1
20
+ Cliff Moon: 1
21
+ Coda Hale: 1
22
+ John Richmond: 1
data/Gemfile CHANGED
@@ -3,9 +3,9 @@
3
3
  source "http://gemcutter.org"
4
4
 
5
5
  gem "eventmachine"
6
- gem "json"
6
+ gem "json" if RUBY_VERSION < "1.9"
7
7
 
8
8
  group(:test) do
9
- gem "em-spec"
10
- gem "bacon"
9
+ gem "rspec", ">=2.0.0"
10
+ gem "amqp-spec", ">=0.3.7"
11
11
  end
data/README.md CHANGED
@@ -45,8 +45,8 @@ How to use AMQP gem with Ruby on Rails, Merb, Sinatra and other web frameworks
45
45
  ==============================================================================
46
46
 
47
47
  To use AMQP gem from web applications, you would need to have EventMachine reactor running.
48
- If you use [Thin](http://code.macournoyer.com/thin/), you are set: Thin uses EventMachine under
49
- the hook.
48
+ If you use [Thin](http://code.macournoyer.com/thin/), you are all set: Thin uses EventMachine under
49
+ the hood.
50
50
 
51
51
  With other web servers, you need to start EventMachine reactor in it's own thread like this:
52
52
 
@@ -61,31 +61,41 @@ In a Ruby on Rails app, probably the best place for this code is initializer
61
61
  Sinatra and pure Rack applications, place it next to other configuration
62
62
  code.
63
63
 
64
+ If you want to integrate AMQP with Thin or another EventMachine-based software which already runs an event loop, you might want to use following code:
65
+
66
+ EM.next_tick { AMQP.connect(...) }
67
+
68
+ So in case the reactor isn't running yet (which seems to be the case with Ruby on Rails 3.x and Thin combination), it won't fail, but wait when the reactor is started (see [issue #21](https://github.com/tmm1/amqp/issues/21)).
69
+
64
70
  Same separate thread technique can be used to make EventMachine play nicely with other
65
71
  libraries that would block current thread (like [File::Tail](http://rubygems.org/gems/file-tail)).
66
72
 
67
- AMQP gem mailing list
73
+ Links
68
74
  ==============================
69
75
 
76
+ * Jabber chat [amqp-dev@conf.netlab.cz](xmpp://amqp-dev@conf.netlab.cz)
70
77
  * [AMQP gem mailing list](http://groups.google.com/group/ruby-amqp)
71
- * [AMQP gem at GitHub](http://github.com/tmm1/amqp)
78
+ * [AMQP gem at GitHub](http://github.com/amqp-dev/amqp)
72
79
  * [AMQP gem at Gemcutter](http://rubygems.org/gems/amqp)
73
80
 
74
- Running specifications suite
81
+ Contributions
75
82
  ============================
76
83
 
77
- To run the test suite make sure you have [bacon](http://gemcutter.org/gems/bacon) gem installed and run:
84
+ All the dependencies are specified in <code>Gemfile</code>, so if you have [Bundler](http://gembundler.com), you can just run <code>bundle install</code>.
78
85
 
79
- rake spec
80
-
81
- The lib/amqp/spec.rb file is generated automatically based on the [AMQP specification](http://www.amqp.org/confluence/display/AMQP/AMQP+Specification). To generate it:
86
+ The <code>lib/amqp/spec.rb</code> file is generated automatically based on the [AMQP specification](http://www.amqp.org/confluence/display/AMQP/AMQP+Specification). To generate it:
82
87
 
83
88
  rake codegen
84
89
 
90
+ For running specs, use <code>rake spec</code>.
91
+
85
92
  Credits and more information
86
93
  ============================
87
94
 
88
- (c) 2008—2010 [Aman Gupta](http://github.com/tmm1) (tmm1)
95
+ * The Original Code is tmm1/amqp.
96
+ * The Initial Developer of the Original Code is Aman Gupta.
97
+ * Copyright (c) 2008 - 2010 [Aman Gupta](http://github.com/tmm1) (tmm1).
98
+ * Contributions from Jakub Stastny are Copyright (c) 2011 VMware, Inc.
89
99
 
90
100
  Current maintainer: [Jakub Stastny](http://github.com/botanicus) aka [botanicus](http://twitter.com/botanicus).
91
101
 
@@ -96,7 +106,6 @@ AMQP brokers
96
106
  ------------
97
107
 
98
108
  * [RabbitMQ](http://rabbitmq.com) (Rabbit Technologies, Erlang/OTP, MPL)
99
- * [ZeroMQ](http://www.zeromq.org) (iMatix/FastMQ/Intel, C++, GPL3)
100
109
  * [OpenAMQ](http://openamq.org) (iMatix, C, GPL2)
101
110
  * [ActiveMQ](http://activemq.apache.org) (Apache Foundation, Java, Apache2)
102
111
 
data/Rakefile CHANGED
@@ -1,14 +1,20 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ desc "Run spec suite (uses Rspec2)"
4
+ RSpec::Core::RakeTask.new(:spec) { |t|}
5
+
6
+ desc "Run specs with RCov"
7
+ RSpec::Core::RakeTask.new(:rcov) do |t|
8
+ t.rcov = true
9
+ t.rcov_opts = ['--exclude', 'spec']
10
+ end
11
+
1
12
  desc "Generate AMQP specification classes"
2
13
  task :codegen do
3
14
  sh 'ruby protocol/codegen.rb > lib/amqp/spec.rb'
4
15
  sh 'ruby lib/amqp/spec.rb'
5
16
  end
6
17
 
7
- desc "Run spec suite (uses bacon gem)"
8
- task :spec do
9
- sh 'bacon lib/amqp.rb'
10
- end
11
-
12
18
  desc "Build the gem"
13
19
  task :gem do
14
20
  sh 'gem build *.gemspec'
@@ -17,4 +23,22 @@ end
17
23
  desc "Synonym for gem"
18
24
  task :pkg => :gem
19
25
  desc "Synonym for gem"
20
- task :package => :gem
26
+ task :package => :gem
27
+
28
+
29
+ desc "Regenerate contributors file."
30
+ task :contributors do
31
+ authors = %x{git log | grep ^Author:}.split("\n")
32
+ results = authors.reduce(Hash.new) do |results, line|
33
+ name = line.sub(/^Author: (.+) <.+>$/, '\1')
34
+ results[name] ||= 0
35
+ results[name] += 1
36
+ results
37
+ end
38
+ results = results.sort_by { |_, count| count }.reverse
39
+ File.open("CONTRIBUTORS", "w") do |file|
40
+ results.each do |name, count|
41
+ file.puts "#{name}: #{count}"
42
+ end
43
+ end
44
+ end
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.name = "amqp"
9
9
  s.version = AMQP::VERSION
10
10
  s.authors = ["Aman Gupta", "Jakub Stastny aka botanicus"]
11
- s.homepage = "http://github.com/tmm1/amqp"
11
+ s.homepage = "http://github.com/ruby-amqp/amqp"
12
12
  s.summary = "AMQP client implementation in Ruby/EventMachine."
13
13
  s.description = "An implementation of the AMQP protocol in Ruby/EventMachine for writing clients to the RabbitMQ message broker."
14
14
  s.cert_chain = nil
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby -i
2
+ # encoding: utf-8
3
+
4
+ # Usage:
5
+ # find . | egrep '\.rb$' | egrep -v cleanify.rb | xargs ./bin/cleanify.rb
6
+
7
+ # \n at the end of the file
8
+ # def foo a, b, &block
9
+ # no trailing whitespace
10
+ # encoding declaration
11
+
12
+ ENCODING = "utf-8"
13
+
14
+ while line = ARGF.gets
15
+ # whitespace
16
+ # line.chomp!
17
+ line.gsub!(/\t/, " ")
18
+ line.rstrip!
19
+
20
+ # encoding
21
+ if line.length == (ARGF.pos - 1) && ! line.match(/^#.*coding/)
22
+ puts "# encoding: #{ENCODING}\n\n"
23
+ end
24
+
25
+ # def
26
+ regexp = /^(\s*def \w[\w\d]*)\s+(.+)$/
27
+ if line.match(regexp)
28
+ line.gsub!(regexp, '\1(\2)')
29
+ end
30
+
31
+ # foo{} => foo {}
32
+ line.gsub!(/([^%][^#( ])(\{)/, '\1 \2')
33
+
34
+ # a=foo => a = foo
35
+ line.gsub!(/([^ ])(\+=)/, '\1 \2')
36
+ line.gsub!(/(\+=)([^ ])/, '\1 \2')
37
+ line.gsub!(/([^ :])(<<)/, '\1 \2')
38
+ line.gsub!(/(<<)([^ ])/, '\1 \2')
39
+
40
+ # foo=>bar
41
+ line.gsub!(/([^\s])=>/, '\1 =>')
42
+ line.gsub!(/=>([^\s])/, '=> \1')
43
+
44
+ line.gsub!(/\{\|/, '{ |')
45
+
46
+ line.gsub!(/,\s*/, ', ')
47
+ line.rstrip!
48
+
49
+ puts line
50
+ end
@@ -1,8 +1,10 @@
1
+ # encoding: utf-8
2
+
1
3
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
4
  require 'amqp'
3
5
 
4
6
  module SimpleClient
5
- def process_frame frame
7
+ def process_frame(frame)
6
8
  case frame
7
9
  when Frame::Body
8
10
  EM.stop_event_loop
@@ -12,7 +14,7 @@ module SimpleClient
12
14
  when Protocol::Connection::Start
13
15
  send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
14
16
  :product => 'AMQP',
15
- :information => 'http://github.com/tmm1/amqp',
17
+ :information => 'http://github.com/ruby-amqp/amqp',
16
18
  :version => '0.1.0'},
17
19
  'AMQPLAIN',
18
20
  {:LOGIN => 'guest',
@@ -72,8 +74,8 @@ module SimpleClient
72
74
  end
73
75
  end
74
76
 
75
- EM.run{
77
+ EM.run {
76
78
  AMQP.logging = true
77
79
  AMQP.client = SimpleClient
78
80
  AMQP.start
79
- }
81
+ }
@@ -1,10 +1,12 @@
1
+ # encoding: utf-8
2
+
1
3
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
4
  require 'mq'
3
5
 
4
6
  # For ack to work appropriately you must shutdown AMQP gracefully,
5
7
  # otherwise all items in your queue will be returned
6
- Signal.trap('INT') { AMQP.stop{ EM.stop } }
7
- Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
8
+ Signal.trap('INT') { AMQP.stop { EM.stop } }
9
+ Signal.trap('TERM') { AMQP.stop { EM.stop } }
8
10
 
9
11
  AMQP.start(:host => 'localhost') do
10
12
  MQ.queue('awesome').publish('Totally rad 1')
@@ -14,10 +16,10 @@ AMQP.start(:host => 'localhost') do
14
16
  i = 0
15
17
 
16
18
  # Stopping after the second item was acked will keep the 3rd item in the queue
17
- MQ.queue('awesome').subscribe(:ack => true) do |h,m|
18
- if (i+=1) == 3
19
+ MQ.queue('awesome').subscribe(:ack => true) do |h, m|
20
+ if (i += 1) == 3
19
21
  puts 'Shutting down...'
20
- AMQP.stop{ EM.stop }
22
+ AMQP.stop { EM.stop }
21
23
  end
22
24
 
23
25
  if AMQP.closing?
@@ -42,4 +44,4 @@ Totally rad 3
42
44
  Totally rad 1
43
45
  Shutting down...
44
46
  Totally rad 2 (ignored, redelivered later)
45
- Totally rad 3 (ignored, redelivered later)
47
+ Totally rad 3 (ignored, redelivered later)
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
4
+
5
+ require 'mq'
6
+
7
+ if RUBY_VERSION == "1.8.7"
8
+ module ArrayExtensions
9
+ def sample
10
+ self.choice
11
+ end # sample
12
+ end
13
+
14
+ class Array
15
+ include ArrayExtensions
16
+ end
17
+ end
18
+
19
+
20
+
21
+ EM.run do
22
+ connection = AMQP.connect
23
+ mq = MQ.new(connection)
24
+
25
+ show_stopper = Proc.new do
26
+ $stdout.puts "Stopping..."
27
+ # now change this to just EM.stop and it
28
+ # unbinds instantly
29
+ connection.close {
30
+ EM.stop { exit }
31
+ }
32
+ end
33
+
34
+ Signal.trap "INT", &show_stopper
35
+
36
+ $stdout.puts "Bound! Running #{AMQP::VERSION} version of the gem."
37
+
38
+ queue1 = mq.queue("queue1")
39
+ queue2 = mq.queue("queue2")
40
+ queue3 = mq.queue("queue3")
41
+
42
+ queues = [queue1, queue2, queue3]
43
+
44
+ # Rely on default direct exchange binding, see section 2.1.2.4 Automatic Mode in AMQP 0.9.1 spec.
45
+ exchange = MQ::Exchange.default
46
+
47
+ queue1.subscribe do |payload|
48
+ puts "Got #{payload} for #{queue1.name}"
49
+ end
50
+
51
+ queue2.subscribe do |payload|
52
+ puts "New message to queue #{queue2.name}"
53
+ end
54
+
55
+ queue3.subscribe do |payload|
56
+ puts "There is a message for #{queue3.name}"
57
+ end
58
+
59
+ EM.add_periodic_timer(1) do
60
+ q = queues.sample
61
+
62
+ $stdout.puts "Publishing to default exchange with routing key = #{q.name}..."
63
+ exchange.publish "Some payload", :routing_key => q.name
64
+ end
65
+ end
@@ -3,7 +3,15 @@
3
3
  $:.unshift File.expand_path("../../../lib", __FILE__)
4
4
  require "mq"
5
5
 
6
- AMQP.start(:host => "localhost") do
6
+ AMQP.start(:host => "localhost") do |connection|
7
+
8
+ # Send Connection.Close on Ctrl+C
9
+ trap(:INT) do
10
+ unless connection.closing?
11
+ connection.close { exit! }
12
+ end
13
+ end
14
+
7
15
  @counter = 0
8
16
  amq = MQ.new
9
17
 
@@ -1,42 +1,42 @@
1
+ # encoding: utf-8
2
+
1
3
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
4
  require 'mq'
3
5
 
4
- AMQP.start(:host => 'localhost', port: 5673) do
6
+ AMQP.start(:host => 'localhost') do |connection|
7
+
8
+ # Send Connection.Close on Ctrl+C
9
+ trap(:INT) do
10
+ unless connection.closing?
11
+ connection.close { exit! }
12
+ end
13
+ end
5
14
 
6
- def log *args
15
+ def log(*args)
7
16
  p args
8
17
  end
9
18
 
10
19
  # AMQP.logging = true
11
20
 
12
21
  clock = MQ.new.fanout('clock')
13
- clock2 = MQ.new.fanout('clock2')
14
-
15
- EM.add_periodic_timer(1){
22
+ EM.add_periodic_timer(1) {
16
23
  puts
17
24
 
18
25
  log :publishing, time = Time.now
19
26
  clock.publish(Marshal.dump(time))
20
27
  }
21
28
 
22
- EM.add_periodic_timer(1){
23
- puts
24
-
25
- log 2, :publishing, time = Time.now
26
- clock.publish(Marshal.dump(time))
27
- }
28
-
29
29
  amq = MQ.new
30
- q = amq.queue('every second')
31
- q.bind(amq.fanout('clock')).subscribe{ |time|
30
+ amq.queue('every second').bind(amq.fanout('clock')).subscribe { |time|
32
31
  log 'every second', :received, Marshal.load(time)
33
32
  }
34
33
 
35
- amq.queue!('every second').bind(amq.fanout('clock2')).subscribe{ |time|
36
- log 2, 'every second', :received, Marshal.load(time)
34
+ amq = MQ.new
35
+ amq.queue('every 5 seconds').bind(amq.fanout('clock')).subscribe { |time|
36
+ time = Marshal.load(time)
37
+ log 'every 5 seconds', :received, time if time.strftime('%S').to_i % 5 == 0
37
38
  }
38
39
 
39
-
40
40
  end
41
41
 
42
42
  __END__