super-poller 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require "rubygems"
2
+ require "rake/gempackagetask"
3
+
4
+ task :default => :test
5
+
6
+ require "rake/testtask"
7
+ Rake::TestTask.new do |t|
8
+ t.libs << "test"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.verbose = true
11
+ end
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = "super-poller"
15
+ s.version = "0.1.0"
16
+ s.summary = "Tools for dealing with queues."
17
+ s.description = "Small modular library for dealing with queues and pollers."
18
+ s.author = "Tom Lea"
19
+ s.email = "contrib@tomlea.co.uk"
20
+ s.homepage = "http://tomlea.co.uk"
21
+ s.has_rdoc = true
22
+
23
+ s.files = %w(Rakefile super_poller.gemspec) + Dir.glob("{bin,test,lib}/**/*")
24
+ s.executables = FileList["bin/*"].map { |f| File.basename(f) }
25
+
26
+ s.require_paths = ["lib"]
27
+ s.rubyforge_project = "super-poller"
28
+ end
29
+
30
+ Rake::GemPackageTask.new(spec) do |pkg|
31
+ pkg.gem_spec = spec
32
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
33
+ File.open(file, "w") {|f| f << spec.to_ruby }
34
+ end
data/bin/queue ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ script = ARGV.shift
3
+
4
+ if File.exist?("#{File.dirname(__FILE__)}/scripts/queue_#{script}")
5
+ load "#{File.dirname(__FILE__)}/scripts/queue_#{script}"
6
+ else
7
+ STDERR.puts "Available Commands:"
8
+ Dir.glob("#{File.dirname(__FILE__)}/scripts/queue_*") do |f|
9
+ STDERR.puts " " + File.basename(f).gsub(/^queue_/, "")
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require "super_poller"
2
+ queue = SuperPoller::QueueUrl.parse(ARGV.shift).to_queue
3
+
4
+ memo = {:memo => Time.now.to_f}
5
+ queue.push(memo)
6
+
7
+ SuperPoller::NoneBlockingPoller.new(queue, lambda{|msg|
8
+ skip = false
9
+ begin
10
+ exit if msg == memo
11
+ p msg
12
+ print "Kill? > "
13
+ skip = gets =~ /^[Yy]/
14
+ ensure
15
+ queue.push(msg) unless skip or msg == memo
16
+ end
17
+ }).start!
@@ -0,0 +1,4 @@
1
+ require "super_poller"
2
+ in_queue, out_queue = [nil,nil].map{|url| SuperPoller::QueueUrl.parse(ARGV.shift).to_queue }
3
+
4
+ SuperPoller::QueueItterator.new(in_queue).each(&out_queue.method(:push))
@@ -0,0 +1,3 @@
1
+ require "super_poller"
2
+ queue = SuperPoller::QueueUrl.parse(ARGV.shift).to_queue
3
+ SuperPoller::NoneBlockingPoller.new(queue, lambda{|msg| }).start!
@@ -0,0 +1,51 @@
1
+ require "super_poller"
2
+ require 'optparse'
3
+
4
+ options = {}
5
+ opts = OptionParser.new do |opts|
6
+ opts.banner = "Usage queue grep QUEUE_URL [options]"
7
+ opts.on("-f STRING", "--ruby-filter STRING", "Set ruby filter") do |v|
8
+ options[:ruby_filter] = v
9
+ end
10
+
11
+ opts.on("-a STRING", "--ruby-action STRING", "Set ruby action") do |v|
12
+ options[:ruby_action] = v
13
+ end
14
+
15
+ opts.on("-d", "--delete", "Delete all matches") do |v|
16
+ options[:ruby_action] = ":delete"
17
+ end
18
+
19
+ opts.on("-c QUEUE", "--copy QUEUE", "Copy all matches") do |v|
20
+ options[:ruby_action] = "SuperPoller::QueueUrl.parse(#{v.to_s.inspect}).to_queue.push(msg)"
21
+ end
22
+
23
+ opts.on("-m QUEUE", "--move QUEUE", "Move all matches") do |v|
24
+ options[:ruby_action] = "SuperPoller::QueueUrl.parse(#{v.to_s.inspect}).to_queue.push(msg); :delete"
25
+ end
26
+
27
+ opts.on("-t TYPE", "--message-type TYPE", "Select messages of a given type") do |v|
28
+ options[:ruby_filter] = "#{v.to_s.inspect} == msg[:name].to_s rescue false"
29
+ end
30
+
31
+ opts.on_tail("-h", "--help", "Show this message") do
32
+ puts opts
33
+ exit 1
34
+ end
35
+ end
36
+
37
+ opts.parse!
38
+
39
+ unless queue_url = ARGV.shift
40
+ puts opts
41
+ exit 1
42
+ end
43
+
44
+ queue = SuperPoller::QueueUrl.parse(queue_url).to_queue
45
+
46
+ eval "def matcher_matches?(msg); #{ options[:ruby_filter] || "true" }; end"
47
+ eval "def action(msg); #{ options[:ruby_action] || "p msg" }; end"
48
+
49
+ SuperPoller::QueueItterator.new(queue).each do |msg|
50
+ action(msg) if matcher_matches? msg
51
+ end
@@ -0,0 +1,13 @@
1
+ require "super_poller"
2
+ queue_one = SuperPoller::QueueUrl.parse(ARGV.shift).to_queue
3
+ queue_two = SuperPoller::QueueUrl.parse(ARGV.shift).to_queue
4
+
5
+ SuperPoller::QueueItterator.new(queue_one).each do |msg|
6
+ p msg
7
+ print "Move to #{queue_two}? > "
8
+
9
+ if gets =~ /^[Yy]/
10
+ queue_two.push(msg)
11
+ :delete
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ require "super_poller"
2
+ in_queue, out_queue = [nil,nil].map{|url| SuperPoller::QueueUrl.parse(ARGV.shift).to_queue }
3
+
4
+ SuperPoller::NoneBlockingPoller.new(in_queue, out_queue.method(:push)).start!
@@ -0,0 +1,3 @@
1
+ require "super_poller"
2
+ queue = SuperPoller::QueueUrl.parse(ARGV.shift).to_queue
3
+ SuperPoller::QueueItterator.new(queue).each(&method(:p))
@@ -0,0 +1,33 @@
1
+ class SuperPoller::AggregatingErrorLogger
2
+ def initialize(stats_file, queue)
3
+ @stats_file, @queue = stats_file, queue
4
+ end
5
+
6
+ def call(error, failed_message)
7
+ update_error_queue(error, failed_message)
8
+ update_error_stats(error, failed_message[:name] || :unknown)
9
+ end
10
+
11
+ protected
12
+ def update_error_queue(error, failed_message)
13
+ error_class_name = error.class.name.to_sym
14
+ error_description = {:class => error_class_name, :message => error.message}
15
+ @queue.push(failed_message.merge(:error => error_description))
16
+ end
17
+
18
+ def update_error_stats(error, message_name)
19
+ stats = load_stats
20
+ error_class_name = error.class.name.to_sym
21
+ stats_for_name = (stats[message_name] ||= {})
22
+ stats_for_name[error_class_name] ||= 0
23
+ stats_for_name[error_class_name] += 1
24
+
25
+ File.open(@stats_file, "w") do |file|
26
+ file << YAML.dump(stats)
27
+ end
28
+ end
29
+
30
+ def load_stats
31
+ File.exists?(@stats_file) && YAML.load(File.read(@stats_file)) || {}
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ class SuperPoller::BufferedHandler < SuperPoller::Handler
2
+ class << self
3
+ def buffer_size(size)
4
+ @max_buffer_size = size
5
+ end
6
+
7
+ def max_buffer_size
8
+ @max_buffer_size || []
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ @buffer = []
14
+ end
15
+
16
+ def call(msg)
17
+ @buffer.push msg
18
+ if @buffer.size >= self.class.max_buffer_size
19
+ handle_batch @buffer
20
+ @buffer = []
21
+ end
22
+ end
23
+
24
+ def handle_batch(batch)
25
+ raise NotImplementedError, "You must define a batch handler."
26
+ end
27
+
28
+ end
@@ -0,0 +1,12 @@
1
+ class SuperPoller::ErrorReporter
2
+ def initialize(message_handler, error_handler = nil, &block)
3
+ @message_handler = message_handler
4
+ @error_handler = error_handler || block
5
+ end
6
+
7
+ def call(*args)
8
+ @message_handler.call(*args)
9
+ rescue Object => e
10
+ @error_handler.call(e, *args)
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ class SuperPoller::Handler
2
+ autoload :TestCase, "super_poller/test_case"
3
+
4
+ class << self
5
+ def handles(*new_message_names)
6
+ @message_names = (message_names + new_message_names).uniq
7
+ end
8
+
9
+ def message_names
10
+ @message_names || []
11
+ end
12
+ end
13
+
14
+ def can_handle?(message)
15
+ self.class.message_names.include? message[:name].to_sym
16
+ end
17
+
18
+ def call(message)
19
+ raise NotImplementedError, "You must define a call handler."
20
+ end
21
+
22
+ end
@@ -0,0 +1,13 @@
1
+ class SuperPoller::NoneBlockingPoller < SuperPoller::Poller
2
+ EmptyQueue = Class.new(Exception)
3
+
4
+ def start!
5
+ super
6
+ rescue EmptyQueue
7
+ end
8
+
9
+ protected
10
+ def get_message
11
+ @queue.fetch or raise EmptyQueue
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ class SuperPoller::Poller
2
+ def initialize(queue, message_handler)
3
+ @message_handler, @queue = message_handler, queue
4
+ end
5
+
6
+ def poll
7
+ @message_handler.call(get_message)
8
+ end
9
+
10
+ def start!
11
+ poll while true
12
+ end
13
+
14
+ protected
15
+ def get_message
16
+ @queue.pop
17
+ rescue Interrupt
18
+ raise
19
+ rescue Object => e
20
+ STDERR.puts "Error while fetching from the queue: #{e.class}: #{e.message}"
21
+ sleep 10
22
+ retry
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ class SuperPoller::QueueItterator
2
+ def initialize(queue)
3
+ @queue = queue
4
+ end
5
+
6
+ def each(&block)
7
+ @memo = {:memo => Time.now.to_f}
8
+ @queue.push @memo
9
+ while @memo
10
+ begin
11
+ break unless msg = @queue.fetch
12
+ if msg == @memo
13
+ msg = @memo = nil
14
+ else
15
+ msg = nil if :delete == block.call(msg)
16
+ end
17
+ ensure
18
+ @queue.push msg if msg
19
+ end
20
+ end
21
+ ensure
22
+ destroy_the_memo
23
+ end
24
+
25
+ def destroy_the_memo
26
+ while @memo and msg = @queue.fetch and msg != @memo
27
+ @queue.push(msg)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require "uri"
2
+
3
+ class SuperPoller::QueueUrl < URI::Generic
4
+ def self.parse(url)
5
+ url = "starling://localhost:22122/#{url}" if url =~ /^[a-zA-Z0-9_-]+$/
6
+ new(*URI.parse(url).send(:component_ary))
7
+ end
8
+
9
+ def to_queue
10
+ raise URI::InvalidURIError unless respond_to? "to_#{scheme}_queue"
11
+ send("to_#{scheme}_queue")
12
+ end
13
+
14
+ protected
15
+ def to_starling_queue
16
+ SuperPoller::StarlingQueue.new(path, "#{host}:#{port}")
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ class SuperPoller::Router
2
+ RoutingError = Class.new(Exception)
3
+
4
+ def initialize()
5
+ @handlers = []
6
+ end
7
+
8
+ def add_handler(handler)
9
+ @handlers.push handler
10
+ @handlers.uniq!
11
+ self
12
+ end
13
+
14
+ alias << add_handler
15
+
16
+ def call(message)
17
+ handler = best_handler_for_message(message)
18
+ handler.call(message[:body])
19
+ end
20
+
21
+ protected
22
+
23
+ def best_handler_for_message(messsage)
24
+ @handlers.each do |handler|
25
+ return handler if handler.can_handle? messsage
26
+ end
27
+ raise RoutingError, "No handler found"
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ require "starling"
2
+
3
+ class SuperPoller::StarlingQueue
4
+ def initialize(queue_name, *args)
5
+ @queue_name = queue_name.to_s
6
+ @queue = Starling.new(*args)
7
+ end
8
+
9
+ def pop
10
+ @queue.get(@queue_name)
11
+ end
12
+
13
+ def push(v)
14
+ @queue.set(@queue_name, v)
15
+ end
16
+
17
+ def fetch
18
+ @queue.fetch(@queue_name)
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ class SuperPoller::Handler::TestCase < (defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase)
2
+
3
+ def handler
4
+ @handler ||= self.class.name.gsub(/Test$/, "").constantize.new
5
+ end
6
+
7
+ def self.should_handle(name)
8
+ should "handle a #{name.inspect} message" do
9
+ assert handler.can_handle?(:name => name)
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,12 @@
1
+ module SuperPoller
2
+ autoload :Handler, "super_poller/handler"
3
+ autoload :BufferedHandler, "super_poller/buffered_handler"
4
+ autoload :Router, "super_poller/router"
5
+ autoload :ErrorReporter, "super_poller/error_reporter"
6
+ autoload :StarlingQueue, "super_poller/starling_queue"
7
+ autoload :Poller, "super_poller/poller"
8
+ autoload :AggregatingErrorLogger, "super_poller/aggregating_error_logger"
9
+ autoload :NoneBlockingPoller, "super_poller/none_blocking_poller"
10
+ autoload :QueueUrl, "super_poller/queue_url"
11
+ autoload :QueueItterator, "super_poller/queue_itterator"
12
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{super_poller}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Your name"]
9
+ s.date = %q{2009-09-28}
10
+ s.email = %q{you@example.com}
11
+ s.files = ["test/aggregating_error_logger_test.rb", "test/buffered_handler_test.rb", "test/error_reporter_test.rb", "test/handler_test.rb", "test/poller_test.rb", "test/router_test.rb", "test/starling_queue_test.rb", "test/test_helper.rb", "lib/super_poller", "lib/super_poller/aggregating_error_logger.rb", "lib/super_poller/buffered_handler.rb", "lib/super_poller/error_reporter.rb", "lib/super_poller/handler.rb", "lib/super_poller/poller.rb", "lib/super_poller/router.rb", "lib/super_poller/starling_queue.rb", "lib/super_poller/test_case.rb", "lib/super_poller.rb"]
12
+ s.homepage = %q{http://example.com}
13
+ s.require_paths = ["lib"]
14
+ s.rubygems_version = %q{1.3.5}
15
+ s.summary = %q{What this thing does}
16
+
17
+ if s.respond_to? :specification_version then
18
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
19
+ s.specification_version = 3
20
+
21
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
22
+ else
23
+ end
24
+ else
25
+ end
26
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+
4
+ class AggregatingErrorLoggerTest < Test::Unit::TestCase
5
+ include SuperPoller
6
+ attr_reader :queued_message
7
+
8
+ def log_contents
9
+ YAML.load(File.open(@path))
10
+ end
11
+
12
+ context "With an error logger" do
13
+ setup do
14
+ @log_file = Tempfile.new("AggregatingErrorLoggerTest")
15
+ @path = @log_file.path
16
+ @queue = Queue.new
17
+ @logger = AggregatingErrorLogger.new(@path, @queue)
18
+ end
19
+
20
+ context "with a exception and message passed in, it" do
21
+ setup do
22
+ @exception = Exception.new("The house is on fire")
23
+ @message = {:name => :foo, :body => "hello"}
24
+ @logger.call(@exception, @message)
25
+ @queued_message = @queue.pop(nonblocking = true)
26
+ end
27
+
28
+ should "add errors to the error queue" do
29
+ assert_equal :foo, queued_message[:name]
30
+ assert_equal "hello", queued_message[:body]
31
+ end
32
+
33
+ should "add error info to the queued message" do
34
+ assert_equal( {:class => :Exception, :message => "The house is on fire"}, queued_message[:error])
35
+ end
36
+
37
+ should "log errors to the log file" do
38
+ assert_equal( {:foo => {:Exception => 1} }, log_contents)
39
+ end
40
+
41
+ should "aggregate duplicate errors" do
42
+ @logger.call(@exception, @message)
43
+ assert_equal( {:foo => {:Exception => 2} }, log_contents)
44
+ end
45
+
46
+ should "separate different exceptions" do
47
+ @logger.call(ArgumentError.new(""), @message)
48
+ assert_equal( {:foo => {:Exception => 1, :ArgumentError => 1} }, log_contents)
49
+ end
50
+
51
+ should "separate errors for different message names" do
52
+ @logger.call(@exception, {:name => :bar, :body => "hello"})
53
+ assert_equal( {:foo => {:Exception => 1}, :bar => {:Exception => 1} }, log_contents)
54
+ end
55
+
56
+ should "reset the counts to 0 if we delete the log file" do
57
+ assert_equal( {:foo => {:Exception => 1} }, log_contents)
58
+ File.delete(@path)
59
+ @logger.call(@exception, {:name => :bar, :body => "hello"})
60
+ assert_equal( {:bar => {:Exception => 1} }, log_contents)
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class BufferedHandlerTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+ def self.should_submit_batches_for_messages(args = {})
6
+ context "that receives #{args[:messages]} messages" do
7
+ setup{ args[:messages].times{@handler.call(:your_mum)} }
8
+
9
+ before_should "handle #{args[:batches]} batches" do
10
+ @handler.expects(:handle_batch).with([:your_mum] * args[:batch_size]).times(args[:batches])
11
+ end
12
+ end
13
+ end
14
+
15
+ context "With a buffered handler with a buffer size of 10" do
16
+ setup{ @handler = Class.new(BufferedHandler){ buffer_size 10 }.new }
17
+
18
+ should_submit_batches_for_messages :batch_size => 10, :messages => 0, :batches => 0
19
+
20
+ should_submit_batches_for_messages :batch_size => 10, :messages => 9, :batches => 0
21
+ should_submit_batches_for_messages :batch_size => 10, :messages => 10, :batches => 1
22
+ should_submit_batches_for_messages :batch_size => 10, :messages => 11, :batches => 1
23
+
24
+ should_submit_batches_for_messages :batch_size => 10, :messages => 19, :batches => 1
25
+ should_submit_batches_for_messages :batch_size => 10, :messages => 20, :batches => 2
26
+ should_submit_batches_for_messages :batch_size => 10, :messages => 21, :batches => 2
27
+ end
28
+
29
+ context "With a buffered handler with a buffer size of 8" do
30
+ setup{ @handler = Class.new(BufferedHandler){ buffer_size 8 }.new }
31
+
32
+ should_submit_batches_for_messages :batch_size => 8, :messages => 7, :batches => 0
33
+ should_submit_batches_for_messages :batch_size => 8, :messages => 8, :batches => 1
34
+
35
+ should_submit_batches_for_messages :batch_size => 8, :messages => 16, :batches => 2
36
+ end
37
+
38
+ context "With a buffered handler with a buffer size of 0" do
39
+ setup{ @handler = Class.new(BufferedHandler){ buffer_size 0 }.new }
40
+
41
+ should_submit_batches_for_messages :batch_size => 1, :messages => 0, :batches => 0
42
+ should_submit_batches_for_messages :batch_size => 1, :messages => 1, :batches => 1
43
+ end
44
+
45
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class ErrorReporterTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+
6
+ should "catch errors and report them to the provided error handler" do
7
+ has_caught = false
8
+ raiser = proc{|msg| raise "Error doing a #{msg}" }
9
+ handler = proc{|e, message| has_caught = (e.message == "Error doing a Hello" and message == "Hello") }
10
+
11
+ ErrorReporter.new(raiser, handler).call("Hello")
12
+
13
+ assert has_caught
14
+ end
15
+
16
+ should "catch errors and report them to the provided error handling block" do
17
+ has_caught = false
18
+ raiser = proc{|msg| raise "Error doing a #{msg}" }
19
+
20
+ ErrorReporter.new(raiser){|e, message| has_caught = (e.message == "Error doing a Hello" and message == "Hello") }.call("Hello")
21
+
22
+ assert has_caught
23
+ end
24
+
25
+ should "not catch errors if they don't happen" do
26
+ ErrorReporter.new(stub("handler", :call)){|*args| flunk "Should not have tried to raise!" }.call("Hello")
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class HandlerTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+
6
+ context "With handlers for :foo and :bar messages" do
7
+ setup do
8
+ @foo_handler = Class.new(Handler){ handles :foo }.new
9
+ @bar_handler = Class.new(Handler){ handles :bar }.new
10
+ end
11
+
12
+ should "only foo_handler should handle a message named :foo" do
13
+ assert @foo_handler.can_handle?({:name => :foo})
14
+ assert !@bar_handler.can_handle?({:name => :foo})
15
+ end
16
+
17
+ should "only bar_handler should handle a message named :bar" do
18
+ assert !@foo_handler.can_handle?({:name => :bar})
19
+ assert @bar_handler.can_handle?({:name => :bar})
20
+ end
21
+
22
+ should "accept strings or symbols as names" do
23
+ assert @foo_handler.can_handle?({:name => "foo"})
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class PollerTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+
6
+ def setup
7
+ @queue = Queue.new
8
+ @message_handler = stub("message_handler")
9
+ @poller = Poller.new(@queue, @message_handler)
10
+ end
11
+
12
+ should "pull things from the queue to the message_handler" do
13
+ @queue.push "Oh Hi!"
14
+ @message_handler.expects(:call).with("Oh Hi!").once
15
+ @poller.poll
16
+ end
17
+
18
+ should "pull more than one thing from the queue to the message_handler" do
19
+ @queue.push "First"
20
+ @queue.push "Second"
21
+
22
+ @message_handler.expects(:call).with("First").once
23
+ @message_handler.expects(:call).with("Second").once
24
+
25
+ 2.times{ @poller.poll }
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class RouterTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+
6
+ context "With a router that has two handlers" do
7
+
8
+ setup do
9
+ @router = Router.new
10
+ @foo_handler = Class.new(Handler){ handles :foo }.new
11
+ @bar_handler = Class.new(Handler){ handles :bar }.new
12
+ @router << @foo_handler << @bar_handler
13
+ end
14
+
15
+ should "route foo messages to the correct handler" do
16
+ @foo_handler.expects(:call).with("test body").once
17
+ @bar_handler.expects(:call).never
18
+ @router.call({:name => :foo, :body => "test body"})
19
+ end
20
+
21
+ should "route bar messages to the correct handler" do
22
+ @foo_handler.expects(:call).never
23
+ @bar_handler.expects(:call).with("test body").once
24
+ @router.call({:name => :bar, :body => "test body"})
25
+ end
26
+
27
+ should "raise RoutingError if handler not found" do
28
+ assert_raise(Router::RoutingError){ @router.call(:name => :your_mum) }
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class StarlingQueueTest < Test::Unit::TestCase
4
+ include SuperPoller
5
+
6
+ STARLING_QUEUE_PATH = '/tmp/reevoo_superpoller_starling'
7
+ STARLING_PID_PATH = '/tmp/reevoo_superpoller_starling.pid'
8
+ STARLING_PORT = '13891'
9
+
10
+ context "with a running starling" do
11
+ setup do
12
+ FileUtils.rm_r STARLING_QUEUE_PATH if File.exists? STARLING_QUEUE_PATH
13
+ FileUtils.rm_r STARLING_PID_PATH if File.exists? STARLING_PID_PATH
14
+ system("starling -p #{STARLING_PORT} -P #{STARLING_PID_PATH} -q #{STARLING_QUEUE_PATH} -d 2>&1")
15
+ sleep 1
16
+ end
17
+
18
+ teardown do
19
+ system("kill -9 `cat #{STARLING_PID_PATH}`")
20
+ end
21
+
22
+ should "write to and read from a queue in the correct order" do
23
+ queue = StarlingQueue.new("TEST_QUEUE", ["localhost:#{STARLING_PORT}"])
24
+ queue.push("first")
25
+ queue.push("second")
26
+ assert_equal "first", queue.pop
27
+ assert_equal "second", queue.pop
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,7 @@
1
+ require "test/unit"
2
+ require "shoulda"
3
+ require "mocha"
4
+ require "tempfile"
5
+ require "starling"
6
+
7
+ require File.join(File.dirname(__FILE__), *%w[.. lib super_poller])
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: super-poller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tom Lea
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-09 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Small modular library for dealing with queues and pollers.
17
+ email: contrib@tomlea.co.uk
18
+ executables:
19
+ - queue
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - super_poller.gemspec
27
+ - bin/queue
28
+ - bin/scripts/queue_choose
29
+ - bin/scripts/queue_copy
30
+ - bin/scripts/queue_flush
31
+ - bin/scripts/queue_grep
32
+ - bin/scripts/queue_interactive_move
33
+ - bin/scripts/queue_move
34
+ - bin/scripts/queue_scan
35
+ - test/aggregating_error_logger_test.rb
36
+ - test/buffered_handler_test.rb
37
+ - test/error_reporter_test.rb
38
+ - test/handler_test.rb
39
+ - test/poller_test.rb
40
+ - test/router_test.rb
41
+ - test/starling_queue_test.rb
42
+ - test/test_helper.rb
43
+ - lib/super_poller/aggregating_error_logger.rb
44
+ - lib/super_poller/buffered_handler.rb
45
+ - lib/super_poller/error_reporter.rb
46
+ - lib/super_poller/handler.rb
47
+ - lib/super_poller/none_blocking_poller.rb
48
+ - lib/super_poller/poller.rb
49
+ - lib/super_poller/queue_itterator.rb
50
+ - lib/super_poller/queue_url.rb
51
+ - lib/super_poller/router.rb
52
+ - lib/super_poller/starling_queue.rb
53
+ - lib/super_poller/test_case.rb
54
+ - lib/super_poller.rb
55
+ has_rdoc: true
56
+ homepage: http://tomlea.co.uk
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project: super-poller
79
+ rubygems_version: 1.3.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: Tools for dealing with queues.
83
+ test_files: []
84
+