simple_queues 1.0.0
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.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.textile +60 -0
- data/Rakefile +19 -0
- data/autotest/discover.rb +1 -0
- data/lib/simple_queues/redis.rb +44 -0
- data/lib/simple_queues/version.rb +3 -0
- data/lib/simple_queues.rb +14 -0
- data/simple_queues.gemspec +24 -0
- data/spec/dequeueing_spec.rb +51 -0
- data/spec/enqueueing_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- metadata +131 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2009 François Beausoleil (francois@teksol.info)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
h1. SimpleQueues
|
2
|
+
|
3
|
+
In the Gang of Four books, one of the first few lines is "Program to an interface, not an implementation." When you need a queue, the only operations you need are enqueue and dequeue. It doesn't matter that Redis (a nice and simple queue server when you need it) has all kinds of operations available.
|
4
|
+
|
5
|
+
This library was written and spec'd on Ruby 1.9.2. It's so small it should work anywhere the Redis gem works.
|
6
|
+
|
7
|
+
h2. Exceptions
|
8
|
+
|
9
|
+
All underlying exceptions the Redis gem raises are let through: no extra exceptions are raised. This means you'll see <code>Errno::ECONNREFUSED</code>, <code>Errno::EAGAIN</code> and friends.
|
10
|
+
|
11
|
+
h2. Usage
|
12
|
+
|
13
|
+
<pre><code>require "simple_queues"
|
14
|
+
Queues = SimpleQueues::Redis.new
|
15
|
+
|
16
|
+
Queues.enqueue :pages_to_crawl, {:url => "http://blog.teksol.info"}.to_json
|
17
|
+
Queues.enqueue :pages_to_crawl, {:url => "http://techcrunch.com"}.to_json
|
18
|
+
|
19
|
+
# In another process
|
20
|
+
|
21
|
+
loop do
|
22
|
+
message = Queues.dequeue_blocking :pages_to_crawl
|
23
|
+
process(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Alternatively, using a timeout
|
27
|
+
loop do
|
28
|
+
message = Queues.dequeue_with_timeout :pages_to_crawl, 5 # seconds
|
29
|
+
if message then
|
30
|
+
process(message)
|
31
|
+
else
|
32
|
+
# Timed out
|
33
|
+
end
|
34
|
+
end
|
35
|
+
<code></pre>
|
36
|
+
|
37
|
+
h2. LICENSE
|
38
|
+
|
39
|
+
(The MIT License)
|
40
|
+
|
41
|
+
Copyright (c) 2008-2009 François Beausoleil (francois@teksol.info)
|
42
|
+
|
43
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
44
|
+
a copy of this software and associated documentation files (the
|
45
|
+
'Software'), to deal in the Software without restriction, including
|
46
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
47
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
48
|
+
permit persons to whom the Software is furnished to do so, subject to
|
49
|
+
the following conditions:
|
50
|
+
|
51
|
+
The above copyright notice and this permission notice shall be
|
52
|
+
included in all copies or substantial portions of the Software.
|
53
|
+
|
54
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
55
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
56
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
57
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
58
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
59
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
60
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
rescue LoadError
|
10
|
+
warn "RSpec not available - Rake tasks not available"
|
11
|
+
warn "Install rspec using: gem install rspec"
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
require "yard"
|
16
|
+
YARD::Rake::YardocTask.new(:doc)
|
17
|
+
rescue LoadError
|
18
|
+
warn "YARD not available - gem install yard"
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SimpleQueues
|
2
|
+
# The Redis version of SimpleQueues.
|
3
|
+
#
|
4
|
+
# Messages are enqueued to the right, dequeued from the left - thus the most recent messages are at the end of the list.
|
5
|
+
class Redis
|
6
|
+
def initialize(redis=Redis.new)
|
7
|
+
@redis = redis
|
8
|
+
end
|
9
|
+
|
10
|
+
# Enqueues a new message to the Redis backend.
|
11
|
+
#
|
12
|
+
# @param queue_name [String, Symbol] The queue name, which must not be nil or the empty String.
|
13
|
+
# @param message [#to_s] The message to be enqueued. The message will be turned into a String through #to_s before being enqueued. Must not be nil, but the empty string is accepted, although it seems meaningless to do so.
|
14
|
+
# @return No useful value.
|
15
|
+
# @raise ArgumentError Whenever the queue name or the message are nil, or the queue name is empty.
|
16
|
+
def enqueue(queue_name, message)
|
17
|
+
raise ArgumentError, "Queue name argument was nil - must not be" if queue_name.nil? || queue_name.to_s.empty?
|
18
|
+
raise ArgumentError, "Message argument was nil - must not be" if message.nil?
|
19
|
+
|
20
|
+
@redis.rpush(queue_name.to_s, message.to_s)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Dequeues a message, and waits forever for one to arrive.
|
24
|
+
#
|
25
|
+
# @param queue_name [String, Symbol] The queue name to read from.
|
26
|
+
# @return [String] The first message in the queue.
|
27
|
+
# @raise ArgumentError If +queue_name+ is nil or the empty String.
|
28
|
+
def dequeue_blocking(queue_name)
|
29
|
+
dequeue_with_timeout(queue_name, 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Dequeues a message, or returns +nil+ if the timeout is exceeded.
|
33
|
+
#
|
34
|
+
# @param queue_name [String, Symbol] The queue name to read from.
|
35
|
+
# @param timeout [#to_i] The number of seconds to wait before returning nil.
|
36
|
+
# @return [String, nil] The first message in the queue, or nil if the timeout was exceeded.
|
37
|
+
# @raise ArgumentError If +queue_name+ is nil or the empty String.
|
38
|
+
def dequeue_with_timeout(queue_name, timeout)
|
39
|
+
raise ArgumentError, "Queue name argument was nil - must not be" if queue_name.nil? || queue_name.to_s.empty?
|
40
|
+
|
41
|
+
@redis.blpop(queue_name.to_s, timeout)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# A simple API for queueing and dequeueing messages.
|
2
|
+
#
|
3
|
+
# In the Gang of Four book, the phrase "Program to an interface, not an implementation" made me think I shouldn't
|
4
|
+
# bind my software directly to Redis, but to an API from which I could change the implementation at any time. If
|
5
|
+
# I ever need to replace Redis with RabbitMQ, it will be possible to do so, given my software is coded to this
|
6
|
+
# interface.
|
7
|
+
#
|
8
|
+
# All SimpleQueues implementations support three methods:
|
9
|
+
# * +enqueue+
|
10
|
+
# * +dequeue_with_timeout+
|
11
|
+
# * +dequeue_blocking+
|
12
|
+
module SimpleQueues
|
13
|
+
autoload :Redis, "simple_queues/redis"
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "simple_queues/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "simple_queues"
|
7
|
+
s.version = SimpleQueues::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["François Beausoleil"]
|
10
|
+
s.email = ["francois@teksol.info"]
|
11
|
+
s.homepage = "https://github.com/francois/simple_queues"
|
12
|
+
s.summary = %q{Simple enqueue/dequeue API for working with queues}
|
13
|
+
s.description = %q{Program to an interface, not an implementation - hides Redis (used as a queue) behind a simple interface}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency 'redis'
|
21
|
+
s.add_development_dependency 'rspec'
|
22
|
+
s.add_development_dependency 'yard'
|
23
|
+
s.add_development_dependency 'RedCloth'
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SimpleQueues::Redis, "dequeue_blocking" do
|
4
|
+
let :redis do
|
5
|
+
double("redis")
|
6
|
+
end
|
7
|
+
|
8
|
+
let :queue do
|
9
|
+
SimpleQueues::Redis.new(redis)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "requests an infinite timeout from Redis #blpop" do
|
13
|
+
redis.should_receive(:blpop).with("pages_to_crawl", 0)
|
14
|
+
queue.dequeue_blocking("pages_to_crawl")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "accepts symbols as queue names and translates them to strings" do
|
18
|
+
redis.should_receive(:blpop).with("pages_to_crawl", 0)
|
19
|
+
queue.dequeue_blocking(:pages_to_crawl)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises an ArgumentError when the queue name is nil or empty" do
|
23
|
+
lambda { queue.dequeue_blocking(nil) }.should raise_error(ArgumentError)
|
24
|
+
lambda { queue.dequeue_blocking("") }.should raise_error(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe SimpleQueues::Redis, "dequeue_with_timeout" do
|
29
|
+
let :redis do
|
30
|
+
double("redis")
|
31
|
+
end
|
32
|
+
|
33
|
+
let :queue do
|
34
|
+
SimpleQueues::Redis.new(redis)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should call Redis' BLPOP with the requested timeout" do
|
38
|
+
redis.should_receive(:blpop).with("pages_to_crawl", 42)
|
39
|
+
queue.dequeue_with_timeout(:pages_to_crawl, 42)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "accepts symbols as queue names and translates them to strings" do
|
43
|
+
redis.should_receive(:blpop).with("pages_to_crawl", 5)
|
44
|
+
queue.dequeue_with_timeout(:pages_to_crawl, 5)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises an ArgumentError when the queue name is nil or empty" do
|
48
|
+
lambda { queue.dequeue_with_timeout(nil) }.should raise_error(ArgumentError)
|
49
|
+
lambda { queue.dequeue_with_timeout("") }.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe SimpleQueues::Redis, "enqueue" do
|
4
|
+
let :redis do
|
5
|
+
double("redis")
|
6
|
+
end
|
7
|
+
|
8
|
+
let :queue do
|
9
|
+
SimpleQueues::Redis.new(redis)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should enqueue to the end of the list" do
|
13
|
+
redis.should_receive(:rpush).with("q", "message")
|
14
|
+
queue.enqueue("q", "message")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should reject invalid queue names" do
|
18
|
+
lambda { queue.enqueue(nil, "") }.should raise_error(ArgumentError)
|
19
|
+
lambda { queue.enqueue("", "") }.should raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should reject nil messages" do
|
23
|
+
lambda { queue.enqueue(:a, nil) }.should raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should allow blank messages (although does this make sense?)" do
|
27
|
+
redis.should_receive(:rpush)
|
28
|
+
lambda { queue.enqueue(:a, "") }.should_not raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it "accepts symbols as queue names, translating to a string" do
|
32
|
+
redis.should_receive(:rpush).with("pages_to_crawl", "http://blog.teksol.info/")
|
33
|
+
queue.enqueue :pages_to_crawl, "http://blog.teksol.info/"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "translates the message to a String before enqueueing" do
|
37
|
+
redis.should_receive(:rpush).with("ops", "shutdown_and_destroy")
|
38
|
+
queue.enqueue :ops, :shutdown_and_destroy
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "simple_queues"
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_queues
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Fran\xC3\xA7ois Beausoleil"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-02-26 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: RedCloth
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
type: :development
|
71
|
+
version_requirements: *id004
|
72
|
+
description: Program to an interface, not an implementation - hides Redis (used as a queue) behind a simple interface
|
73
|
+
email:
|
74
|
+
- francois@teksol.info
|
75
|
+
executables: []
|
76
|
+
|
77
|
+
extensions: []
|
78
|
+
|
79
|
+
extra_rdoc_files: []
|
80
|
+
|
81
|
+
files:
|
82
|
+
- .gitignore
|
83
|
+
- .rvmrc
|
84
|
+
- Gemfile
|
85
|
+
- LICENSE
|
86
|
+
- README.textile
|
87
|
+
- Rakefile
|
88
|
+
- autotest/discover.rb
|
89
|
+
- lib/simple_queues.rb
|
90
|
+
- lib/simple_queues/redis.rb
|
91
|
+
- lib/simple_queues/version.rb
|
92
|
+
- simple_queues.gemspec
|
93
|
+
- spec/dequeueing_spec.rb
|
94
|
+
- spec/enqueueing_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
has_rdoc: true
|
97
|
+
homepage: https://github.com/francois/simple_queues
|
98
|
+
licenses: []
|
99
|
+
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.3.7
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Simple enqueue/dequeue API for working with queues
|
128
|
+
test_files:
|
129
|
+
- spec/dequeueing_spec.rb
|
130
|
+
- spec/enqueueing_spec.rb
|
131
|
+
- spec/spec_helper.rb
|