pub 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,21 +1,9 @@
1
- Jotting down the idea:
1
+ # Pub
2
2
 
3
- # Configure
3
+ Pub is a Redis-backed pub with a non-blocking bar counter, or, putting aside
4
+ the metaphor for a moment, a processing queue where consumers, instead of
5
+ simply queuing jobs and getting on with their lives, queue and wait for a
6
+ response without blocking the Ruby process.
4
7
 
5
- Pub.redis = "localhost:6379"
6
- Pub.expires_in = 900
7
-
8
- # Subscriber: A request
9
-
10
- Queue["foo"].subscribe("1234") do |resp|
11
- puts resp
12
- end
13
-
14
- # Publisher: A background worker
15
-
16
- queue = Queue["foo"]
17
- # Process up to 10 requests at a time
18
- reqs = queue.pop(10)
19
- process(reqs) do |req, resp|
20
- queue.publish(req, resp)
21
- end
8
+ This is a barebone work-in-progress that I am about to give a test ride to in
9
+ an app I'm building.
data/Rakefile CHANGED
@@ -1 +1,14 @@
1
- require 'bundler/gem_tasks'
1
+ require "bundler/gem_tasks"
2
+ require "cucumber/rake/task"
3
+ require "rspec/core/rake_task"
4
+
5
+ Cucumber::Rake::Task.new(:features) do |t|
6
+ t.cucumber_opts = "features --format pretty"
7
+ end
8
+
9
+ desc "Run all specs in spec directory"
10
+ RSpec::Core::RakeTask.new(:spec) do |t|
11
+ t.pattern = "spec/**/*_spec.rb"
12
+ end
13
+
14
+ task :default => [:spec, :features]
@@ -0,0 +1,9 @@
1
+ # Pub
2
+
3
+ Pub is a Redis-backed pub with a non-blocking bar counter, or, putting aside
4
+ the metaphor for a moment, a processing queue where consumers, instead of
5
+ simply queuing jobs and getting on with their lives, queue and wait for a
6
+ response without blocking the Ruby process.
7
+
8
+ This is a barebone work-in-progress that I am about to give a test ride to in
9
+ an app I'm building.
@@ -0,0 +1,63 @@
1
+ Feature: Order beers
2
+ As a pub patron
3
+ I want bartenders to take orders asynchronously
4
+ So I do not block the counter
5
+
6
+ Below we assume a bartender can fill an order in one second.
7
+
8
+ Scenario: Order a beer
9
+ Given 1 bartender
10
+ When "John" orders:
11
+ | beer |
12
+ | Brooklyn Lager |
13
+ Then "John" should receive his beer in 1 second
14
+
15
+ Scenario: Order multiple beers
16
+ Given 1 bartender
17
+ When "John" orders:
18
+ | beer |
19
+ | Brooklyn Lager |
20
+ | Efes Pilsen |
21
+ Then "John" should receive his beers in 2 seconds
22
+
23
+ Scenario: Two bartenders
24
+ Given 2 bartenders
25
+ When "John" orders:
26
+ | beer |
27
+ | Brooklyn Lager |
28
+ | Efes Pilsen |
29
+ Then "John" should receive his beers in 1 second
30
+
31
+ Scenario: Timeout
32
+ Given 1 bartender
33
+ And "John" has no patience to wait more than 2 seconds at the counter
34
+ When "John" orders:
35
+ | beer |
36
+ | Brooklyn Lager |
37
+ | Efes Pilsen |
38
+ | Stella |
39
+ Then "John" should receive the following beers in 2 seconds:
40
+ | beer |
41
+ | A pint of Brooklyn Lager |
42
+ | A pint of Efes Pilsen |
43
+
44
+ Scenario: Two patrons order the same beer
45
+ Given 1 bartender
46
+ And "John" orders:
47
+ | beer |
48
+ | Brooklyn Lager |
49
+ And "Jane" orders:
50
+ | beer |
51
+ | Brooklyn Lager |
52
+ Then "John" should receive his beer in 1 second
53
+ And "Jane" should receive her beer in 1 second
54
+
55
+ Scenario: Crowd
56
+ Given 10 bartenders
57
+ When 10 patrons order 1 beer each
58
+ Then they should receive their beers within 1 second
59
+
60
+ Scenario: Overcrowding
61
+ Given 2 bartenders
62
+ When 4 patrons order 2 beers each
63
+ Then they should receive their beers within 4 seconds
@@ -0,0 +1,3 @@
1
+ Transform /^-?\d+$/ do |number|
2
+ number.to_i
3
+ end
@@ -0,0 +1,112 @@
1
+ module PubHelpers
2
+ def data_store(name)
3
+ patron_names << name unless patron_names.include?(name)
4
+
5
+ instance_variable_get("@hash_of_#{name}") ||
6
+ instance_variable_set("@hash_of_#{name}", Hash.new)
7
+ end
8
+
9
+ def find_or_create_patron(name)
10
+ instance_variable_get("@#{name}") ||
11
+ instance_variable_set("@#{name}", pub.new_patron)
12
+ end
13
+
14
+ def map_beers(table)
15
+ table.hashes.map { |hash| hash["beer"] }
16
+ end
17
+
18
+ def now
19
+ Time.now
20
+ end
21
+
22
+ def orders
23
+ @orders ||= Hash.new
24
+ end
25
+
26
+ def patron_names
27
+ @patron_names ||= []
28
+ end
29
+
30
+ def pub
31
+ @pub ||= Pub.new("Ye Olde Rubies")
32
+ end
33
+
34
+ def sleep_until_order_complete(name)
35
+ fiber = data_store(name)[:fiber]
36
+ EM::Synchrony.sleep(0.1) while fiber.alive?
37
+ end
38
+ end
39
+
40
+ World(PubHelpers)
41
+
42
+ Given /^(\d+) bartenders?$/ do |count|
43
+ count.times do |counter|
44
+ bartender = pub.new_bartender
45
+ EM.add_periodic_timer(1) do
46
+ Fiber.new do
47
+ orders = bartender.take_orders(1)
48
+ order = orders.first
49
+ bartender.serve(order) { "A pint of #{order}" } if order
50
+ end.resume
51
+ end
52
+ end
53
+ end
54
+
55
+ Given /^"([^"]+)" has no patience to wait more than (\d+) seconds at the counter$/ do |name, seconds|
56
+ patron = find_or_create_patron(name)
57
+ patron.instance_variable_set(:@timeout, seconds)
58
+ end
59
+
60
+ When /^"([^"]+)" orders:$/ do |name, table|
61
+ beers = map_beers(table)
62
+ patron = find_or_create_patron(name)
63
+
64
+ data_store(name)[:started_at] = Time.now
65
+
66
+ fiber = Fiber.new do
67
+ tray = patron.order(*beers)
68
+ data_store(name)[:tray] = tray
69
+ end
70
+ data_store(name)[:fiber] = fiber
71
+
72
+ EM.next_tick { fiber.resume }
73
+ end
74
+
75
+ When /^(\d+) patrons order (\d+) beers? each$/ do |patrons_count, beers_count|
76
+ patrons_count.times do |patron_counter|
77
+ raw = beers_count.times.inject("| beer |\n") do |raw, beer_counter|
78
+ raw << "| beer_#{patron_counter}_#{beer_counter} |\n"
79
+ end
80
+ name = "patron_#{patron_counter}"
81
+ When %{"#{name}" orders:}, table(raw)
82
+ end
83
+ end
84
+
85
+ Then /^"([^"]+)" should receive (?:his|her) beers? in (\d+) seconds?$/ do |name, seconds|
86
+ sleep_until_order_complete(name)
87
+
88
+ started_at = data_store(name)[:started_at]
89
+ (now - started_at).should be_within(0.2).of(seconds)
90
+ end
91
+
92
+ Then /^"([^"]+)" should receive the following beers? in (\d+) seconds?:$/ do |name, seconds, table|
93
+ sleep_until_order_complete(name)
94
+
95
+ started_at = data_store(name)[:started_at]
96
+ (now - started_at).should be_within(0.2).of(seconds)
97
+
98
+ beers = map_beers(table)
99
+ tray = data_store(name)[:tray]
100
+ tray.should =~ beers
101
+ end
102
+
103
+ Then /^they should receive their beers within (\d+) seconds?$/ do |seconds|
104
+ started_at = now
105
+ patron_names.each do |name|
106
+ patron_started_at = data_store(name)[:started_at]
107
+ started_at = patron_started_at if patron_started_at < started_at
108
+ sleep_until_order_complete(name)
109
+ end
110
+
111
+ (now - started_at).should be_within(0.2).of(seconds)
112
+ end
@@ -0,0 +1,15 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "pub"
5
+
6
+ Around do |scenario, block|
7
+ EM.synchrony do
8
+ block.call
9
+ EM.stop
10
+ end
11
+ end
12
+
13
+ Before do
14
+ Pub.counter.flushall
15
+ end
data/lib/pub.rb CHANGED
@@ -1,14 +1,64 @@
1
- begin
2
- require "yajl"
3
- rescue LoadError
4
- require "json"
5
- end
6
-
7
- require "redis/connection/synchrony"
8
1
  require "redis"
2
+ require "redis/connection/synchrony"
3
+
4
+ require "pub/helpers"
5
+ require "pub/bartender"
6
+ require "pub/patron"
7
+
8
+ # A Redis-backed pub with a non-blocking bar counter.
9
+ #
10
+ # Or, putting aside the metaphor for a moment:
11
+ #
12
+ # A processing queue where consumers, instead of simply queuing jobs and
13
+ # getting on with their lives, queue and wait for a response without
14
+ # blocking the Ruby process.
15
+ #
16
+ # Each pub instance is a distinct queue.
17
+ class Pub
18
+ # The name of the pub.
19
+ attr_reader :name
20
+
21
+ class << self
22
+ # A device that dispenses beer.
23
+ attr_accessor :beer_tap
24
+
25
+ # The bar counter.
26
+ def counter
27
+ Redis.new(url: beer_tap)
28
+ end
29
+ end
30
+
31
+ # Enters a pub.
32
+ #
33
+ # Takes the name of the pub and an optional block.
34
+ #
35
+ # Pub.new('Ye Olde Rubies') do |pub|
36
+ #
37
+ # patron = pub.new_patron
38
+ #
39
+ # patron.order('Guinness') do |beer|
40
+ # JSON.parse(beer).drink
41
+ # end
42
+ #
43
+ # end
44
+ #
45
+ def initialize(name)
46
+ @name = name
47
+ EM.synchrony { yield self } if block_given?
48
+ end
49
+
50
+ # Closes the pub for the night.
51
+ def close
52
+ EM.stop
53
+ end
9
54
 
10
- require "pub/version"
55
+ # A new bartender.
56
+ def new_bartender
57
+ Bartender.new(name)
58
+ end
11
59
 
12
- module Pub
13
- # Your code goes here...
60
+ # A new patron.
61
+ def new_patron(timeout = 5)
62
+ Patron.new(name, timeout)
63
+ end
14
64
  end
@@ -0,0 +1,44 @@
1
+ class Pub
2
+ # A bartender.
3
+ #
4
+ # In other words, a producer in our processing queue.
5
+ class Bartender
6
+ include Helpers
7
+
8
+ # Creates a new bartender.
9
+ #
10
+ # Takes the name of the pub.
11
+ def initialize(pub_name)
12
+ @pub_name = pub_name
13
+ end
14
+
15
+ # Serves a beer to a thirsty patron.
16
+ #
17
+ # Takes a block which should return a glass of beer.
18
+ #
19
+ # See below for example usage.
20
+ def serve(beer, &block)
21
+ counter.lrem(@pub_name, 0, beer)
22
+ counter.publish(order_for(beer), block.call)
23
+ end
24
+
25
+ # Takes one or more orders from the queue.
26
+ #
27
+ # orders = bartender.take_orders(3)
28
+ # orders.each do |order|
29
+ # bartender.serve(order) do
30
+ # "A pint of #{order}"
31
+ # end
32
+ # end
33
+ def take_orders(count = 1)
34
+ orders = Array.new
35
+
36
+ count.times do
37
+ order = counter.lpop(@pub_name) || break
38
+ orders << order
39
+ end
40
+
41
+ orders
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ class Pub
2
+ # Methods used by both patron and bartender.
3
+ module Helpers
4
+ # A specific order.
5
+ #
6
+ # This is a pub/sub channel through with patron and bartender communicate.
7
+ def order_for(beer)
8
+ [@pub_name, beer].join(':')
9
+ end
10
+
11
+ # The bar counter.
12
+ def counter
13
+ @counter ||= Pub.counter
14
+ end
15
+
16
+ private :order_for, :counter
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ class Pub
2
+ # A patron.
3
+ #
4
+ # In other words, a consumer in our processing queue.
5
+ class Patron
6
+ include Helpers
7
+
8
+ # Creates a new pub patron.
9
+ #
10
+ # Takes the name of the pub and a timeout value in seconds.
11
+ #
12
+ # The timeout designates how long a patron is willing to wait at the
13
+ # counter to receive his or her beer.
14
+ def initialize(pub_name, timeout)
15
+ @pub_name, @timeout = pub_name, timeout
16
+ end
17
+
18
+ # Orders one or more beer at the bar counter.
19
+ #
20
+ # If given a block, yields beers as they become available. Otherwise,
21
+ # returns all on a tray.
22
+ #
23
+ # If not all beers are served within specified timeout, it will return only
24
+ # the beers that are ready.
25
+ def order(*beers)
26
+ raise ArgumentError, 'Empty order' if beers.empty?
27
+
28
+ orders, tray = [], []
29
+
30
+ beers.flatten!
31
+
32
+ beers.each do |beer|
33
+ counter.rpush(@pub_name, beer)
34
+ orders << order_for(beer)
35
+ end
36
+
37
+ timer = EM.add_timer(@timeout) do
38
+ foo = counter.unsubscribe
39
+ end
40
+
41
+ counter.subscribe(*orders) do |on|
42
+ on.message do |order, beer|
43
+ counter.unsubscribe(order)
44
+ if block_given?
45
+ yield beer
46
+ else
47
+ tray << beer
48
+ end
49
+ end
50
+ end
51
+
52
+ EM.cancel_timer(timer)
53
+ block_given? ? nil : tray
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,3 @@
1
- module Pub
2
- VERSION = "0.0.1"
1
+ class Pub
2
+ VERSION = "0.1.0"
3
3
  end
@@ -5,30 +5,28 @@ require "pub/version"
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "pub"
7
7
  s.version = Pub::VERSION
8
- s.authors = ["Hakan Ensari"]
9
- s.email = ["hakan.ensari@papercavalier.com"]
10
- s.homepage = "http://rubygems.com/papercavalier/pub"
11
- s.summary = %q{A Redis-backed pub/sub messaging system that processes queues}
12
- s.description = %q{Pub is a Redis-backed pub/sub messaging system that process queues.}
13
-
14
- s.rubyforge_project = "pub"
8
+ s.authors = ["Paper Cavalier"]
9
+ s.email = "code@papercavalier.com"
10
+ s.homepage = "http://github.com/papercavalier/pub"
11
+ s.summary = "A Redis-backed pub or processing queue with a non-blocking bar counter"
15
12
 
16
13
  s.files = `git ls-files`.split("\n")
17
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
19
15
  s.require_paths = ["lib"]
20
16
 
21
- {
22
- "eventmachine" => "~> 1.0.0.beta.3",
17
+ s.required_ruby_version = ">= 1.9"
18
+
19
+ {
23
20
  "em-synchrony" => "~> 0.3.0.beta.1",
24
21
  "hiredis" => "~> 0.3.2",
25
- "redis" => "~> 2.2.0"
22
+ "redis" => "~> 2.2.1"
26
23
  }.each do |lib, version|
27
24
  s.add_runtime_dependency lib, version
28
25
  end
29
26
 
30
27
  {
31
- "rspec" => "~> 2.6.0"
28
+ "cucumber" => "~> 1.0",
29
+ "rspec" => "~> 2.6",
32
30
  }.each do |lib, version|
33
31
  s.add_development_dependency lib, version
34
32
  end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+
3
+ class Pub
4
+ describe Bartender do
5
+ let(:pub) { Pub.new("Ye Olde Rubies") }
6
+ let(:bartender) { pub.new_bartender }
7
+ let(:counter) { double("Counter").as_null_object }
8
+
9
+ before do
10
+ bartender.stub!(:counter).and_return(counter)
11
+ end
12
+
13
+ describe "#take_order" do
14
+ before do
15
+ counter.stub!(:lpop).and_return(Time.now)
16
+ end
17
+
18
+ context "when not passed a number" do
19
+ it "pops one order from the queue" do
20
+ orders = bartender.take_orders
21
+ orders.count.should eql 1
22
+ end
23
+ end
24
+
25
+ context "when passed a number" do
26
+ it "returns that many orders from the queue" do
27
+ orders = bartender.take_orders(3)
28
+ orders.count.should eql 3
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "#serve" do
34
+ let(:beer) { "Guinness" }
35
+ after { bartender.serve(beer) { beer } }
36
+
37
+ it "removes duplicate instances of the beer from the queue" do
38
+ counter.should_receive(:lrem).with(pub.name, 0, beer)
39
+ end
40
+
41
+ it "publishes the order" do
42
+ order = bartender.send(:order_for, beer)
43
+ counter.should_receive(:publish).with(order, beer)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+
3
+ class Pub
4
+ describe Patron do
5
+ let(:pub) { Pub.new("Ye Olde Rubies") }
6
+ let(:patron) { pub.new_patron }
7
+ let(:counter) { double("Counter").as_null_object }
8
+
9
+ before do
10
+ patron.stub!(:counter).and_return(counter)
11
+ end
12
+
13
+ describe "#order" do
14
+ context "when ordering a beer" do
15
+ let(:beer) { "Guinness" }
16
+ after { patron.order(beer) }
17
+
18
+ it "queues the beer at the counter" do
19
+ counter.should_receive(:rpush).with(pub.name, beer)
20
+ end
21
+
22
+ it "subscribes to the order" do
23
+ order = patron.send(:order_for, beer)
24
+ counter.should_receive(:subscribe).with(order)
25
+ end
26
+ end
27
+
28
+ context "when ordering two beers" do
29
+ let(:beers) { ["Guinness", "Stella"] }
30
+ after { patron.order(*beers) }
31
+
32
+ it "queues the beers at the counter" do
33
+ beers.each do |beer|
34
+ counter.should_receive(:rpush).with(pub.name, beer)
35
+ end
36
+ end
37
+
38
+ it "subscribes to the orders" do
39
+ orders = beers.map do |beer|
40
+ patron.send(:order_for, beer)
41
+ end
42
+ counter.should_receive(:subscribe).with(*orders)
43
+ end
44
+ end
45
+
46
+ context "when ordering an array of beers" do
47
+ let(:beers) { ["Guinness", "Stella"] }
48
+ after { patron.order(beers) }
49
+
50
+ it "splats the array and queues the beers at the counter" do
51
+ beers.each do |beer|
52
+ counter.should_receive(:rpush).with(pub.name, beer)
53
+ end
54
+ end
55
+ end
56
+
57
+ context "when patron does not know what he or she wants" do
58
+ it "raises an error" do
59
+ expect do
60
+ patron.order
61
+ end.to raise_error ArgumentError
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe Pub do
4
+ let(:pub) { Pub.new("Ye Olde Rubies") }
5
+
6
+ describe ".counter" do
7
+ it "returns a Redis connection" do
8
+ Pub.counter.should be_a Redis
9
+ end
10
+ end
11
+
12
+ describe "#new_bartender" do
13
+ it "returns a new bartender" do
14
+ pub.new_bartender.should be_a Pub::Bartender
15
+ end
16
+ end
17
+
18
+ describe "#new_patron" do
19
+ it "returns a new patron" do
20
+ pub.new_patron.should be_a Pub::Patron
21
+ end
22
+
23
+ it "optionally sets the timeout of the patron" do
24
+ patron = pub.new_patron(10)
25
+ patron.instance_variable_get(:@timeout).should eql 10
26
+ end
27
+ end
28
+ end
@@ -1,7 +1,15 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'rspec'
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "rspec"
4
4
 
5
- require File.expand_path('../../lib/pub', __FILE__)
5
+ require File.expand_path("../../lib/pub", __FILE__)
6
6
 
7
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
7
+ RSpec.configure do |c|
8
+ c.around(:each) do |example|
9
+ EM.synchrony do
10
+ Pub.counter.flushall
11
+ example.run
12
+ EM.stop
13
+ end
14
+ end
15
+ end
metadata CHANGED
@@ -4,21 +4,21 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 0
8
7
  - 1
9
- version: 0.0.1
8
+ - 0
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
- - Hakan Ensari
12
+ - Paper Cavalier
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-06-08 00:00:00 +01:00
17
+ date: 2011-06-30 00:00:00 +01:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: eventmachine
21
+ name: em-synchrony
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
24
  none: false
@@ -26,16 +26,16 @@ dependencies:
26
26
  - - ~>
27
27
  - !ruby/object:Gem::Version
28
28
  segments:
29
- - 1
30
29
  - 0
30
+ - 3
31
31
  - 0
32
32
  - beta
33
- - 3
34
- version: 1.0.0.beta.3
33
+ - 1
34
+ version: 0.3.0.beta.1
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
- name: em-synchrony
38
+ name: hiredis
39
39
  prerelease: false
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
41
  none: false
@@ -45,14 +45,12 @@ dependencies:
45
45
  segments:
46
46
  - 0
47
47
  - 3
48
- - 0
49
- - beta
50
- - 1
51
- version: 0.3.0.beta.1
48
+ - 2
49
+ version: 0.3.2
52
50
  type: :runtime
53
51
  version_requirements: *id002
54
52
  - !ruby/object:Gem::Dependency
55
- name: hiredis
53
+ name: redis
56
54
  prerelease: false
57
55
  requirement: &id003 !ruby/object:Gem::Requirement
58
56
  none: false
@@ -60,14 +58,14 @@ dependencies:
60
58
  - - ~>
61
59
  - !ruby/object:Gem::Version
62
60
  segments:
63
- - 0
64
- - 3
65
61
  - 2
66
- version: 0.3.2
62
+ - 2
63
+ - 1
64
+ version: 2.2.1
67
65
  type: :runtime
68
66
  version_requirements: *id003
69
67
  - !ruby/object:Gem::Dependency
70
- name: redis
68
+ name: cucumber
71
69
  prerelease: false
72
70
  requirement: &id004 !ruby/object:Gem::Requirement
73
71
  none: false
@@ -75,11 +73,10 @@ dependencies:
75
73
  - - ~>
76
74
  - !ruby/object:Gem::Version
77
75
  segments:
78
- - 2
79
- - 2
76
+ - 1
80
77
  - 0
81
- version: 2.2.0
82
- type: :runtime
78
+ version: "1.0"
79
+ type: :development
83
80
  version_requirements: *id004
84
81
  - !ruby/object:Gem::Dependency
85
82
  name: rspec
@@ -92,13 +89,11 @@ dependencies:
92
89
  segments:
93
90
  - 2
94
91
  - 6
95
- - 0
96
- version: 2.6.0
92
+ version: "2.6"
97
93
  type: :development
98
94
  version_requirements: *id005
99
- description: Pub is a Redis-backed pub/sub messaging system that process queues.
100
- email:
101
- - hakan.ensari@papercavalier.com
95
+ description:
96
+ email: code@papercavalier.com
102
97
  executables: []
103
98
 
104
99
  extensions: []
@@ -111,12 +106,23 @@ files:
111
106
  - Gemfile
112
107
  - README.md
113
108
  - Rakefile
109
+ - features/README.md
110
+ - features/order.feature
111
+ - features/step_definitions/numeric_transforms.rb
112
+ - features/step_definitions/pub_steps.rb
113
+ - features/support/env.rb
114
114
  - lib/pub.rb
115
+ - lib/pub/bartender.rb
116
+ - lib/pub/helpers.rb
117
+ - lib/pub/patron.rb
115
118
  - lib/pub/version.rb
116
119
  - pub.gemspec
120
+ - spec/pub/bartender_spec.rb
121
+ - spec/pub/patron_spec.rb
122
+ - spec/pub_spec.rb
117
123
  - spec/spec_helper.rb
118
124
  has_rdoc: true
119
- homepage: http://rubygems.com/papercavalier/pub
125
+ homepage: http://github.com/papercavalier/pub
120
126
  licenses: []
121
127
 
122
128
  post_install_message:
@@ -130,8 +136,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
136
  - - ">="
131
137
  - !ruby/object:Gem::Version
132
138
  segments:
133
- - 0
134
- version: "0"
139
+ - 1
140
+ - 9
141
+ version: "1.9"
135
142
  required_rubygems_version: !ruby/object:Gem::Requirement
136
143
  none: false
137
144
  requirements:
@@ -142,10 +149,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
149
  version: "0"
143
150
  requirements: []
144
151
 
145
- rubyforge_project: pub
152
+ rubyforge_project:
146
153
  rubygems_version: 1.3.7
147
154
  signing_key:
148
155
  specification_version: 3
149
- summary: A Redis-backed pub/sub messaging system that processes queues
156
+ summary: A Redis-backed pub or processing queue with a non-blocking bar counter
150
157
  test_files:
158
+ - features/README.md
159
+ - features/order.feature
160
+ - features/step_definitions/numeric_transforms.rb
161
+ - features/step_definitions/pub_steps.rb
162
+ - features/support/env.rb
163
+ - spec/pub/bartender_spec.rb
164
+ - spec/pub/patron_spec.rb
165
+ - spec/pub_spec.rb
151
166
  - spec/spec_helper.rb