pivotal-moqueue 0.1.0.200907241602

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ module Moqueue
2
+
3
+ module ObjectMethods
4
+ def mock_queue_and_exchange(name=nil)
5
+ queue = mock_queue(name)
6
+ exchange = mock_exchange
7
+ exchange.attached_queues << queue
8
+ [queue, exchange]
9
+ end
10
+
11
+ def mock_queue(name=nil)
12
+ MockQueue.new(name || "anonymous-#{rand(2**32).to_s(16)}")
13
+ end
14
+
15
+ def mock_exchange(opts={})
16
+ MockExchange.new(opts)
17
+ end
18
+
19
+ def overload_amqp
20
+ require MOQUEUE_ROOT + "moqueue/overloads"
21
+ end
22
+
23
+ def reset_broker
24
+ MockBroker.instance.reset!
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ Object.send(:include, Moqueue::ObjectMethods)
@@ -0,0 +1,46 @@
1
+ require "eventmachine"
2
+
3
+ class MQ
4
+
5
+ class << self
6
+ def queue(name)
7
+ Moqueue::MockQueue.new(name)
8
+ end
9
+
10
+ def fanout(name, opts={})
11
+ Moqueue::MockExchange.new(opts.merge(:fanout=>name))
12
+ end
13
+
14
+ end
15
+
16
+ def initialize(*args)
17
+ end
18
+
19
+ def queue(name, opts = {})
20
+ Moqueue::MockQueue.new(name)
21
+ end
22
+
23
+ def topic(topic_name)
24
+ Moqueue::MockExchange.new(:topic=>topic_name)
25
+ end
26
+
27
+ end
28
+
29
+ module AMQP
30
+
31
+ class << self
32
+ attr_reader :closing
33
+ alias :closing? :closing
34
+ end
35
+
36
+ def self.start(opts={},&block)
37
+ EM.run(&block)
38
+ end
39
+
40
+ def self.stop
41
+ @closing = true
42
+ yield if block_given?
43
+ @closing = false
44
+ end
45
+
46
+ end
data/moqueue.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{moqueue}
5
+ s.version = "0.1.0.200907241602"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Daniel DeLeo"]
9
+ s.date = %q{2009-07-09}
10
+ s.description = %q{Mocktacular Companion to AMQP Library. Happy TATFTing!}
11
+ s.email = %q{dan@kallistec.com}
12
+ s.extra_rdoc_files = ["README.rdoc"]
13
+ s.files = ["lib", "moqueue.gemspec", "Rakefile", "README.rdoc", "spec", "VERSION.yml", "lib/moqueue", "lib/moqueue/fibers18.rb", "lib/moqueue/matchers.rb", "lib/moqueue/mock_broker.rb", "lib/moqueue/mock_exchange.rb", "lib/moqueue/mock_headers.rb", "lib/moqueue/mock_queue.rb", "lib/moqueue/object_methods.rb", "lib/moqueue/overloads.rb", "lib/moqueue.rb", "spec/examples", "spec/examples/ack_spec.rb", "spec/examples/basic_usage_spec.rb", "spec/examples/example_helper.rb", "spec/examples/logger_spec.rb", "spec/examples/ping_pong_spec.rb", "spec/examples/stocks_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/unit", "spec/unit/matchers_spec.rb", "spec/unit/mock_broker_spec.rb", "spec/unit/mock_exchange_spec.rb", "spec/unit/mock_headers_spec.rb", "spec/unit/mock_queue_spec.rb", "spec/unit/moqueue_spec.rb", "spec/unit/object_methods_spec.rb", "spec/unit/overloads_spec.rb"]
14
+ s.homepage = %q{http://github.com/danielsdeleo/moqueue}
15
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
16
+ s.require_paths = ["lib"]
17
+ s.rubygems_version = %q{1.3.3}
18
+ s.summary = %q{Mocktacular Companion to AMQP Library. Happy TATFTing!}
19
+
20
+ if s.respond_to? :specification_version then
21
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
22
+ s.specification_version = 3
23
+
24
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
25
+ else
26
+ end
27
+ else
28
+ end
29
+ end
@@ -0,0 +1,60 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/example_helper'
3
+
4
+ # NOTE: moqueue currently does not mimic AMQP's behavior of:
5
+ # 1) requiring graceful shutdown for acks to be delivered
6
+ # 2) returning messages to the queue if not acked
7
+ # 3) not processing messages when AMQP isn't "running"
8
+ #
9
+ # This causes the result of this test to differ from the actual result when run
10
+ # with a real broker. The true behavior should be that the 3rd message
11
+ # published should be unacknowledged and returned to the queue. In this test,
12
+ # all messages get acknowleged
13
+ describe Moqueue, "when running the ack example" do
14
+ include ExampleHelper
15
+
16
+ def run_ack_example
17
+ AMQP.start(:host => 'localhost') do
18
+ MQ.queue('awesome').publish('Totally rad 1')
19
+ MQ.queue('awesome').publish('Totally rad 2')
20
+ MQ.queue('awesome').publish('Totally rad 3')
21
+
22
+ i = 0
23
+
24
+ # Stopping after the second item was acked will keep the 3rd item in the queue
25
+ MQ.queue('awesome').subscribe(:ack => true) do |h,m|
26
+ if (i+=1) == 3
27
+ #puts 'Shutting down...'
28
+ AMQP.stop{ EM.stop }
29
+ end
30
+
31
+ if AMQP.closing?
32
+ #puts "#{m} (ignored, redelivered later)"
33
+ else
34
+ #puts "received message: " + m
35
+ h.ack
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ before(:all) do
43
+ overload_amqp
44
+ end
45
+
46
+ before(:each) do
47
+ reset_broker
48
+ reset!
49
+ end
50
+
51
+ it "should get the correct result without errors" do
52
+ Timeout::timeout(2) do
53
+ run_ack_example
54
+ end
55
+ q = MQ.queue('awesome')
56
+ q.received_ack_for_message?('Totally rad 1').should be_true
57
+ q.received_ack_for_message?('Totally rad 2').should be_true
58
+ q.received_ack_for_message?('Totally rad 3').should be_true
59
+ end
60
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "AMQP", "when mocked out by Moqueue" do
4
+
5
+ before(:each) do
6
+ reset_broker
7
+ end
8
+
9
+ it "should have direct exchanges" do
10
+ queue = mock_queue("direct-exchanges")
11
+ queue.publish("you are correct, sir!")
12
+ queue.subscribe { |message| "do something with message" }
13
+ queue.received_message?("you are correct, sir!").should be_true
14
+ end
15
+
16
+ it "should have direct exchanges with acks" do
17
+ queue = mock_queue("direct-with-acks")
18
+ queue.publish("yessir!")
19
+ queue.subscribe(:ack => true) { |headers, message| headers.ack }
20
+ queue.received_ack_for_message?("yessir!").should be_true
21
+ end
22
+
23
+ it "should have topic exchanges" do
24
+ topic = mock_exchange(:topic => "TATFT")
25
+ queue = mock_queue("rspec-fiend")
26
+ queue.bind(topic, :key => "bdd.*").subscribe { |msg| "do something" }
27
+ topic.publish("TATFT FTW", :key=> "bdd.4life")
28
+ queue.received_message?("TATFT FTW").should be_true
29
+ end
30
+
31
+ it "should have topic exchanges with acks" do
32
+ topic = mock_exchange(:topic => "animals")
33
+ queue = mock_queue("cat-lover")
34
+ queue.bind(topic, :key => "cats.#").subscribe(:ack => true) do |header, msg|
35
+ header.ack
36
+ "do something with message"
37
+ end
38
+ topic.publish("OMG kittehs!", :key => "cats.lolz.kittehs")
39
+ topic.received_ack_for_message?("OMG kittehs!").should be_true
40
+ end
41
+
42
+ it "should have fanout exchanges with acks" do
43
+ film = mock_exchange(:fanout => "Godfather")
44
+ one_actor = mock_queue("Jack Woltz")
45
+ other_actor = mock_queue("Captain McCluskey")
46
+ one_actor.bind(film).subscribe(:ack =>true) { |h,msg| h.ack && "horse head" }
47
+ other_actor.bind(film).subscribe(:ack => true) { |h,msg| h.ack && "dirty cops" }
48
+ offer = "you can't refuse"
49
+ film.publish(offer)
50
+ one_actor.received_message?(offer).should be_true
51
+ other_actor.received_message?(offer).should be_true
52
+ film.should have(2).acked_messages
53
+ end
54
+
55
+ end
56
+
57
+ describe Moqueue, "with syntax sugar" do
58
+
59
+ before(:each) do
60
+ reset_broker
61
+ end
62
+
63
+ it "counts received messages" do
64
+ queue = mock_queue
65
+ queue.subscribe { |msg| msg.should_not be_nil }
66
+ 5.times {queue.publish("no moar beers kthxbye")}
67
+ queue.should have(5).received_messages
68
+ end
69
+
70
+ it "counts acked messages" do
71
+ queue = mock_queue
72
+ queue.subscribe(:ack=>true) { |headers,msg| headers.ack }
73
+ 5.times { queue.publish("time becomes a loop") }
74
+ queue.should have(5).acked_messages
75
+ end
76
+
77
+ it "makes the callback (#subscribe) block testable" do
78
+ emphasis = mock_queue
79
+ emphasis.subscribe { |msg| @emphasized = "**" + msg + "**" }
80
+ emphasis.run_callback("show emphasis").should == "**show emphasis**"
81
+ end
82
+
83
+ end
84
+
85
+ describe Moqueue, "when using custom rspec matchers" do
86
+
87
+ it "should accept syntax like queue.should have_received('a message')" do
88
+ queue = mock_queue("sugary")
89
+ queue.subscribe { |msg| "eat the message" }
90
+ queue.publish("a message")
91
+ queue.should have_received("a message")
92
+ end
93
+
94
+ it "should accept syntax like queue_or_exchange.should have_ack_for('a message')" do
95
+ queue = mock_queue("more sugar")
96
+ queue.subscribe(:ack => true) { |headers, msg| headers.ack }
97
+ queue.publish("another message")
98
+ queue.should have_ack_for("another message")
99
+ end
100
+
101
+ end
@@ -0,0 +1,16 @@
1
+ module ExampleHelper
2
+ def capture_output(*args)
3
+ @captured_output << args
4
+ end
5
+
6
+ def counter
7
+ @counter += 1
8
+ EM.stop {AMQP.stop} if @counter >= 2
9
+ @counter
10
+ end
11
+
12
+ def reset!
13
+ @counter, @captured_output = 0, []
14
+ end
15
+
16
+ end
@@ -0,0 +1,159 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Moqueue, "when running the logger example" do
4
+
5
+ class Logger
6
+ def initialize *args, &block
7
+ opts = args.pop if args.last.is_a? Hash
8
+ opts ||= {}
9
+
10
+ printer(block) if block
11
+
12
+ @prop = opts
13
+ @tags = ([:timestamp] + args).uniq
14
+ end
15
+
16
+ attr_reader :prop
17
+ alias :base :prop
18
+
19
+ def log severity, *args
20
+ opts = args.pop if args.last.is_a? Hash and args.size != 1
21
+ opts ||= {}
22
+ opts = @prop.clone.update(opts)
23
+
24
+ data = args.shift
25
+
26
+ data = {:type => :exception,
27
+ :name => data.class.to_s.intern,
28
+ :backtrace => data.backtrace,
29
+ :message => data.message} if data.is_a? Exception
30
+
31
+ (@tags + args).each do |tag|
32
+ tag = tag.to_sym
33
+ case tag
34
+ when :timestamp
35
+ opts.update :timestamp => Time.now
36
+ when :hostname
37
+ @hostname ||= { :hostname => `hostname`.strip }
38
+ opts.update @hostname
39
+ when :process
40
+ @process_id ||= { :process_id => Process.pid,
41
+ :process_name => $0,
42
+ :process_parent_id => Process.ppid,
43
+ :thread_id => Thread.current.object_id }
44
+ opts.update :process => @process_id
45
+ else
46
+ (opts[:tags] ||= []) << tag
47
+ end
48
+ end
49
+
50
+ opts.update(:severity => severity,
51
+ :msg => data)
52
+
53
+ print(opts)
54
+ unless Logger.disabled?
55
+ MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
56
+ end
57
+
58
+ opts
59
+ end
60
+ alias :method_missing :log
61
+
62
+ def print data = nil, &block
63
+ if block
64
+ @printer = block
65
+ elsif data.is_a? Proc
66
+ @printer = data
67
+ elsif data
68
+ (pr = @printer || self.class.printer) and pr.call(data)
69
+ else
70
+ @printer
71
+ end
72
+ end
73
+ alias :printer :print
74
+
75
+ def self.printer &block
76
+ @printer = block if block
77
+ @printer
78
+ end
79
+
80
+ def self.disabled?
81
+ !!@disabled
82
+ end
83
+
84
+ def self.enable
85
+ @disabled = false
86
+ end
87
+
88
+ def self.disable
89
+ @disabled = true
90
+ end
91
+ end
92
+
93
+
94
+ before(:all) do
95
+ overload_amqp
96
+ end
97
+
98
+
99
+ def run_client
100
+ AMQP.start do
101
+ log = Logger.new
102
+ log.debug 'its working!'
103
+
104
+ log = Logger.new do |msg|
105
+ #require 'pp'
106
+ #pp msg
107
+ #puts
108
+ end
109
+
110
+ log.info '123'
111
+ log.debug [1,2,3]
112
+ log.debug :one => 1, :two => 2
113
+ log.error Exception.new('123')
114
+
115
+ log.info '123', :process_id => Process.pid
116
+ log.info '123', :process
117
+ log.debug 'login', :session => 'abc', :user => 123
118
+
119
+ log = Logger.new(:webserver, :timestamp, :hostname, &log.printer)
120
+ log.info 'Request for /', :GET, :session => 'abc'
121
+
122
+ #AMQP.stop{ EM.stop }
123
+ end
124
+ end
125
+
126
+ def run_server
127
+ AMQP.start(:host => 'localhost') do
128
+
129
+ @server_queue = MQ.queue('logger')
130
+ @server_queue.bind(MQ.fanout('logging', :durable => true)).subscribe do |msg|
131
+ msg = Marshal.load(msg)
132
+ end
133
+ end
134
+ end
135
+
136
+ it "should get the expected results" do
137
+ EM.run do
138
+ threads = []
139
+ threads << Thread.new do
140
+ run_server
141
+ end
142
+ threads << Thread.new do
143
+ run_client
144
+ end
145
+
146
+ EM.add_timer(0.1) do
147
+ @server_queue.should have(9).received_messages
148
+ webserver_log = Marshal.load(@server_queue.received_messages.last)
149
+ webserver_log[:tags].should == [:webserver, :GET]
150
+ webserver_log[:msg].should == "Request for /"
151
+
152
+ EM.stop
153
+ threads.each { |t| t.join }
154
+ end
155
+
156
+ end
157
+ end
158
+
159
+ end