karait 0.0.7 → 0.0.8
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 +14 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +24 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/karait.gemspec +50 -0
- data/lib/karait.rb +5 -0
- data/lib/message.rb +86 -0
- data/lib/queue.rb +189 -0
- data/test/helper.rb +18 -0
- data/test/test_message.rb +92 -0
- data/test/test_queue.rb +254 -0
- metadata +20 -93
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "mongo", "~> 1.3.1"
|
4
|
+
|
5
|
+
# Add dependencies to develop your gem here.
|
6
|
+
# Include everything needed to run rake, tests, features, etc.
|
7
|
+
group :development do
|
8
|
+
gem "shoulda", ">= 0"
|
9
|
+
gem "bundler", "~> 1.0.0"
|
10
|
+
gem "jeweler", "~> 1.6.4"
|
11
|
+
gem "rcov", ">= 0"
|
12
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
bson (1.3.1)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
mongo (1.3.1)
|
11
|
+
bson (>= 1.3.1)
|
12
|
+
rake (0.9.2)
|
13
|
+
rcov (0.9.10)
|
14
|
+
shoulda (2.11.3)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
bundler (~> 1.0.0)
|
21
|
+
jeweler (~> 1.6.4)
|
22
|
+
mongo (~> 1.3.1)
|
23
|
+
rcov
|
24
|
+
shoulda
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "karait"
|
18
|
+
gem.homepage = "http://github.com/bcoe/karait"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{A ridiculously simple cross-language queuing system, built on top of MongoDB.}
|
21
|
+
gem.description = %Q{A ridiculously simple cross-language queuing system, built on top of MongoDB.}
|
22
|
+
gem.email = "bencoe@gmail.com"
|
23
|
+
gem.authors = ["bcoe"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
test.rcov_opts << '--exclude "gems/*"'
|
41
|
+
end
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "karait #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.8
|
data/karait.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "karait"
|
8
|
+
s.version = "0.0.8"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["bcoe"]
|
12
|
+
s.date = "2011-11-12"
|
13
|
+
s.description = "A ridiculously simple cross-language queuing system, built on top of MongoDB."
|
14
|
+
s.email = "bencoe@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"karait.gemspec",
|
28
|
+
"lib/karait.rb",
|
29
|
+
"lib/message.rb",
|
30
|
+
"lib/queue.rb",
|
31
|
+
"test/helper.rb",
|
32
|
+
"test/test_message.rb",
|
33
|
+
"test/test_queue.rb"
|
34
|
+
]
|
35
|
+
s.homepage = "http://github.com/bcoe/karait"
|
36
|
+
s.licenses = ["MIT"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = "1.8.11"
|
39
|
+
s.summary = "A ridiculously simple cross-language queuing system, built on top of MongoDB."
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
s.specification_version = 3
|
43
|
+
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
|
+
else
|
46
|
+
end
|
47
|
+
else
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/lib/karait.rb
ADDED
data/lib/message.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Karait
|
2
|
+
class Message
|
3
|
+
|
4
|
+
include Karait
|
5
|
+
|
6
|
+
ASSIGN_VARIABLE_REGEX = /^([a-z_][a-zA-Z_0-9]*)=$/
|
7
|
+
VARIABLE_REGEX = /^([a-z_][a-zA-Z_0-9]*)$/
|
8
|
+
BLACKLIST = {
|
9
|
+
'_meta' => true,
|
10
|
+
'_id' => true,
|
11
|
+
'_expired' => true
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(raw_message={}, queue_collection=nil)
|
15
|
+
@source = raw_message
|
16
|
+
@queue_collection = queue_collection
|
17
|
+
@variables_to_serialize = {}
|
18
|
+
set_expired
|
19
|
+
add_accessors raw_message
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
return @variables_to_serialize
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete
|
27
|
+
@queue_collection.update(
|
28
|
+
{
|
29
|
+
'_id' => @source['_id']
|
30
|
+
},
|
31
|
+
{
|
32
|
+
'$set' => {
|
33
|
+
'_meta.expired' => true
|
34
|
+
}
|
35
|
+
}
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def expired?
|
40
|
+
return @expired
|
41
|
+
end
|
42
|
+
|
43
|
+
def get(key)
|
44
|
+
return @variables_to_serialize[key.to_s]
|
45
|
+
end
|
46
|
+
|
47
|
+
def _get_id
|
48
|
+
return @source['_id']
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def set_expired
|
54
|
+
@expired = false
|
55
|
+
|
56
|
+
current_time = Time.now().to_f
|
57
|
+
meta = @source.fetch('_meta', {})
|
58
|
+
|
59
|
+
return if meta.fetch('expire', -1.0) <= -1.0
|
60
|
+
|
61
|
+
if current_time - meta.fetch('timestamp', 0.0) > meta.fetch('expire', -1.0)
|
62
|
+
@expired = true
|
63
|
+
delete
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_accessors(hash)
|
68
|
+
hash.each do |k, v|
|
69
|
+
if not Message::BLACKLIST.has_key? k
|
70
|
+
@variables_to_serialize[k.to_s] = v
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(sym, *args, &block)
|
76
|
+
if matches = sym.to_s.match(Message::ASSIGN_VARIABLE_REGEX) and args.count == 1
|
77
|
+
@variables_to_serialize[matches[1]] = args[0]
|
78
|
+
elsif matches = sym.to_s.match(Message::VARIABLE_REGEX) and args.count == 0
|
79
|
+
return @variables_to_serialize[matches[1]]
|
80
|
+
else
|
81
|
+
super(sym, *args, &block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
data/lib/queue.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
|
3
|
+
module Karait
|
4
|
+
class Queue
|
5
|
+
|
6
|
+
include Karait
|
7
|
+
|
8
|
+
MESSAGES_READ = 10
|
9
|
+
NO_OBJECT_FOUND_ERROR = 'No matching object found'
|
10
|
+
|
11
|
+
def initialize(opts={})
|
12
|
+
set_instance_variables opts
|
13
|
+
create_mongo_connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(message, opts={})
|
17
|
+
if message.class == Hash
|
18
|
+
message_dict = message
|
19
|
+
else
|
20
|
+
message_dict = message.to_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
message_dict[:_meta] = {
|
24
|
+
:expire => opts.fetch(:expire, -1.0),
|
25
|
+
:timestamp => Time.now().to_f,
|
26
|
+
:expired => false,
|
27
|
+
:visible_after => -1.0
|
28
|
+
}
|
29
|
+
|
30
|
+
message_dict[:_meta][:routing_key] = opts.fetch(:routing_key) if opts[:routing_key]
|
31
|
+
|
32
|
+
@queue_collection.insert(message_dict, :safe => true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def read(opts={})
|
36
|
+
opts = {
|
37
|
+
:messages_read => 10,
|
38
|
+
:visibility_timeout => -1.0,
|
39
|
+
:routing_key => nil,
|
40
|
+
:block => false,
|
41
|
+
:polling_interval => 1,
|
42
|
+
:polling_timeout => nil
|
43
|
+
}.update(opts)
|
44
|
+
|
45
|
+
current_time = Time.new.to_f
|
46
|
+
messages = []
|
47
|
+
|
48
|
+
query = {
|
49
|
+
'_meta.expired' => false,
|
50
|
+
'_meta.visible_after' => {
|
51
|
+
'$lt' => current_time
|
52
|
+
}
|
53
|
+
}
|
54
|
+
if opts[:routing_key] != nil
|
55
|
+
query['_meta.routing_key'] = opts[:routing_key]
|
56
|
+
else
|
57
|
+
query['_meta.routing_key'] = {
|
58
|
+
'$exists' => false
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
update = false
|
63
|
+
if opts[:visibility_timeout] > -1.0
|
64
|
+
update = {
|
65
|
+
'$set' => {
|
66
|
+
'_meta.visible_after' => current_time + opts[:visibility_timeout]
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
raw_messages = []
|
72
|
+
|
73
|
+
# if we want to block, loop and sleep until messages are available (or we time out)
|
74
|
+
if opts[:block]
|
75
|
+
block_until_message_available( query, opts[:polling_interval], opts[:polling_timeout] )
|
76
|
+
end
|
77
|
+
|
78
|
+
if update
|
79
|
+
(0..opts[:messages_read]).each do
|
80
|
+
begin
|
81
|
+
|
82
|
+
raw_message = @queue_collection.find_and_modify(:query => query, :update => update)
|
83
|
+
|
84
|
+
if raw_message
|
85
|
+
raw_messages << raw_message
|
86
|
+
else
|
87
|
+
break
|
88
|
+
end
|
89
|
+
|
90
|
+
rescue Mongo::OperationFailure => operation_failure
|
91
|
+
if not operation_failure.to_s.match(Queue::NO_OBJECT_FOUND_ERROR)
|
92
|
+
raise operation_failure
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
else
|
98
|
+
@queue_collection.find(query).limit(opts[:messages_read]).each do |raw_message|
|
99
|
+
raw_messages << raw_message
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
raw_messages.each do |raw_message|
|
104
|
+
message = Karait::Message.new(raw_message=raw_message, queue_collection=@queue_collection)
|
105
|
+
if not message.expired?
|
106
|
+
messages << message
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
return messages
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete_messages(messages)
|
114
|
+
ids = []
|
115
|
+
messages.each {|message| ids << message._get_id}
|
116
|
+
@queue_collection.update(
|
117
|
+
{
|
118
|
+
'_id' => {
|
119
|
+
'$in' => ids
|
120
|
+
}
|
121
|
+
},
|
122
|
+
{
|
123
|
+
'$set' => {
|
124
|
+
'_meta.expired' => true
|
125
|
+
}
|
126
|
+
},
|
127
|
+
:multi => true,
|
128
|
+
:safe => true
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def block_until_message_available(query, polling_interval=1.0, polling_timeout=None)
|
135
|
+
current_time = Time.new.to_f
|
136
|
+
|
137
|
+
while @queue_collection.find(query).count() == 0
|
138
|
+
if polling_timeout and (Time.new.to_f - current_time) > polling_timeout
|
139
|
+
break
|
140
|
+
end
|
141
|
+
|
142
|
+
sleep polling_interval
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def set_instance_variables(opts)
|
147
|
+
|
148
|
+
defaults = {
|
149
|
+
:host => 'localhost',
|
150
|
+
:port => 27017,
|
151
|
+
:database => 'karait',
|
152
|
+
:queue => 'messages',
|
153
|
+
:average_message_size => 8192,
|
154
|
+
:queue_size => 4096
|
155
|
+
}.merge(opts)
|
156
|
+
|
157
|
+
@host = defaults[:host]
|
158
|
+
@port = defaults[:port]
|
159
|
+
@database = defaults[:database]
|
160
|
+
@queue = defaults[:queue]
|
161
|
+
@average_message_size = defaults[:average_message_size]
|
162
|
+
@queue_size = defaults[:queue_size]
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
def create_mongo_connection
|
167
|
+
@connection = Mongo::Connection.new(
|
168
|
+
@host,
|
169
|
+
@port
|
170
|
+
)
|
171
|
+
@database = @connection[@database]
|
172
|
+
create_capped_collection
|
173
|
+
@queue_collection = @database[@queue]
|
174
|
+
@queue_collection.create_index('_id')
|
175
|
+
@queue_collection.create_index('_meta.routing_key')
|
176
|
+
@queue_collection.create_index('_meta.expired')
|
177
|
+
@queue_collection.create_index('_meta.visible_after')
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_capped_collection
|
181
|
+
@database.create_collection(
|
182
|
+
@queue,
|
183
|
+
:size => (@average_message_size * @queue_size),
|
184
|
+
:capped => true,
|
185
|
+
:max => @queue_size
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'karait'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'karait'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
class TestMessage < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Mongo::Connection.new()['karait_test']['queue_test'].drop()
|
9
|
+
end
|
10
|
+
|
11
|
+
should "should add attribute accessors when initialized with a raw message" do
|
12
|
+
message = Karait::Message.new(
|
13
|
+
raw_message={
|
14
|
+
:apple => 5,
|
15
|
+
:banana => 3
|
16
|
+
}
|
17
|
+
)
|
18
|
+
|
19
|
+
message.apple = 6
|
20
|
+
assert_equal 6, message.apple
|
21
|
+
assert_equal 3, message.banana
|
22
|
+
end
|
23
|
+
|
24
|
+
should "add new attribute accessors for missing methods" do
|
25
|
+
message = Karait::Message.new
|
26
|
+
message.foo = 9
|
27
|
+
assert_equal 9, message.foo
|
28
|
+
end
|
29
|
+
|
30
|
+
should "return a hash of appropriate instance variables when to_hash is called" do
|
31
|
+
message = Karait::Message.new(
|
32
|
+
raw_message={
|
33
|
+
:apple => 5,
|
34
|
+
:banana => 3
|
35
|
+
}
|
36
|
+
)
|
37
|
+
message.apple = 6
|
38
|
+
message.foo = {'bar' => 9}
|
39
|
+
message.bar = [27]
|
40
|
+
|
41
|
+
hash = message.to_hash
|
42
|
+
|
43
|
+
assert_equal 6, hash['apple']
|
44
|
+
assert_equal 3, hash['banana']
|
45
|
+
assert_equal 9, hash['foo']['bar']
|
46
|
+
assert_equal 27, hash['bar'][0]
|
47
|
+
assert_equal 4, hash.keys.count
|
48
|
+
end
|
49
|
+
|
50
|
+
should "not copy blacklisted keys when to_hash called" do
|
51
|
+
raw_message = {
|
52
|
+
'_id' => 'foobar',
|
53
|
+
'_meta' => {
|
54
|
+
'foo' => 2
|
55
|
+
}
|
56
|
+
}
|
57
|
+
message = Karait::Message.new(raw_message=raw_message)
|
58
|
+
hash = message.to_hash
|
59
|
+
|
60
|
+
assert_equal 0, hash.keys.count
|
61
|
+
end
|
62
|
+
|
63
|
+
should "set expired to true when delete is called on a message" do
|
64
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
65
|
+
collection.insert({
|
66
|
+
'routing_key' => 'foobar',
|
67
|
+
'apple' => 3,
|
68
|
+
'banana' => 5,
|
69
|
+
'_meta' => {
|
70
|
+
'timestamp' => 2523939,
|
71
|
+
'expire' => 20393,
|
72
|
+
'expired' => false
|
73
|
+
}
|
74
|
+
})
|
75
|
+
raw_message = collection.find_one({'_meta.expired' => false})
|
76
|
+
assert_equal 3, raw_message['apple']
|
77
|
+
message = Karait::Message.new(raw_message=raw_message, queue_collection=collection)
|
78
|
+
message.delete()
|
79
|
+
assert_equal 0, collection.find({'_meta.expired' => false}).count
|
80
|
+
end
|
81
|
+
|
82
|
+
should "allow a get method to be called to retrieve keys that conflict with class variables, e.g., send" do
|
83
|
+
message = Karait::Message.new(
|
84
|
+
raw_message={
|
85
|
+
:send => 5,
|
86
|
+
:banana => 3
|
87
|
+
}
|
88
|
+
)
|
89
|
+
assert_equal 3, message.get(:banana)
|
90
|
+
assert_equal 5, message.get(:send)
|
91
|
+
end
|
92
|
+
end
|
data/test/test_queue.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'mongo'
|
3
|
+
require 'karait'
|
4
|
+
|
5
|
+
class TestQueue < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Mongo::Connection.new()['karait_test']['queue_test'].drop()
|
9
|
+
end
|
10
|
+
|
11
|
+
should "initialize a capped collection when a queue is created" do
|
12
|
+
queue = Karait::Queue.new(
|
13
|
+
:database => 'karait_test',
|
14
|
+
:queue => 'queue_test',
|
15
|
+
:average_message_size => 8192,
|
16
|
+
:queue_size => 4096
|
17
|
+
)
|
18
|
+
|
19
|
+
options = Mongo::Connection.new()['karait_test']['queue_test'].options
|
20
|
+
|
21
|
+
assert_equal true, options['capped']
|
22
|
+
assert_equal 4096, options['max']
|
23
|
+
assert_equal (8192 * 4096), options['size']
|
24
|
+
end
|
25
|
+
|
26
|
+
should "attach to a mongo queue collection that already exists" do
|
27
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
28
|
+
collection.insert({
|
29
|
+
:message => {
|
30
|
+
:apple => 3,
|
31
|
+
:banana => 5
|
32
|
+
},
|
33
|
+
:_meta => {
|
34
|
+
:timestamp => 2523939,
|
35
|
+
:expire => 20393,
|
36
|
+
:routing_key => 'foo_key'
|
37
|
+
}
|
38
|
+
})
|
39
|
+
assert_equal 1, collection.count()
|
40
|
+
|
41
|
+
queue = Karait::Queue.new(
|
42
|
+
:database => 'karait_test',
|
43
|
+
:queue => 'queue_test'
|
44
|
+
)
|
45
|
+
|
46
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
47
|
+
assert_equal 1, collection.count()
|
48
|
+
assert_equal nil, collection.options()
|
49
|
+
end
|
50
|
+
|
51
|
+
should "write a dictionary into the mongo queue collection" do
|
52
|
+
queue = Karait::Queue.new(
|
53
|
+
:database => 'karait_test',
|
54
|
+
:queue => 'queue_test',
|
55
|
+
:average_message_size => 8192,
|
56
|
+
:queue_size => 4096
|
57
|
+
)
|
58
|
+
|
59
|
+
queue.write({
|
60
|
+
:apple => 5,
|
61
|
+
:banana => 6,
|
62
|
+
:inner_object => {
|
63
|
+
:foo => 1,
|
64
|
+
:bar => 2
|
65
|
+
}
|
66
|
+
})
|
67
|
+
|
68
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
69
|
+
obj = collection.find_one()
|
70
|
+
assert_equal 6, obj['banana']
|
71
|
+
assert_equal 2, obj['inner_object']['bar']
|
72
|
+
assert obj['_meta']['expire']
|
73
|
+
assert obj['_meta']['timestamp']
|
74
|
+
end
|
75
|
+
|
76
|
+
should "write a message object into the mongo queue collection" do
|
77
|
+
queue = Karait::Queue.new(
|
78
|
+
:database => 'karait_test',
|
79
|
+
:queue => 'queue_test'
|
80
|
+
)
|
81
|
+
message = Karait::Message.new
|
82
|
+
message.apple = 5
|
83
|
+
message.banana = {
|
84
|
+
:foo => 2
|
85
|
+
}
|
86
|
+
queue.write message
|
87
|
+
|
88
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
89
|
+
obj = collection.find_one
|
90
|
+
assert_equal 5, obj['apple']
|
91
|
+
assert_equal 2, obj['banana']['foo']
|
92
|
+
end
|
93
|
+
|
94
|
+
should "read a message object from the mongo queue collection" do
|
95
|
+
queue = Karait::Queue.new(
|
96
|
+
:database => 'karait_test',
|
97
|
+
:queue => 'queue_test'
|
98
|
+
)
|
99
|
+
|
100
|
+
write_message = Karait::Message.new
|
101
|
+
write_message.apple = 5
|
102
|
+
write_message.banana = 6
|
103
|
+
write_message.inner_object = {
|
104
|
+
'foo' => 1,
|
105
|
+
'bar' => 2
|
106
|
+
}
|
107
|
+
queue.write write_message
|
108
|
+
|
109
|
+
read_message = queue.read()[0]
|
110
|
+
assert_equal 5, read_message.apple
|
111
|
+
assert_equal 2, read_message.inner_object['bar']
|
112
|
+
assert_equal 3, read_message.to_hash.keys.count
|
113
|
+
end
|
114
|
+
|
115
|
+
should "return messages in fifo order" do
|
116
|
+
queue = Karait::Queue.new(
|
117
|
+
:database => 'karait_test',
|
118
|
+
:queue => 'queue_test'
|
119
|
+
)
|
120
|
+
|
121
|
+
queue.write Karait::Message.new({'foo' => 1})
|
122
|
+
queue.write Karait::Message.new({:foo => 2})
|
123
|
+
queue.write Karait::Message.new({'foo' => 3})
|
124
|
+
messages = queue.read()
|
125
|
+
|
126
|
+
assert_equal 1, messages[0].foo
|
127
|
+
assert_equal 2, messages[1].foo
|
128
|
+
assert_equal 3, messages[2].foo
|
129
|
+
end
|
130
|
+
|
131
|
+
should "only return messages with the appropriate routing key when it's provided" do
|
132
|
+
queue = Karait::Queue.new(
|
133
|
+
:database => 'karait_test',
|
134
|
+
:queue => 'queue_test'
|
135
|
+
)
|
136
|
+
|
137
|
+
queue.write(Karait::Message.new({:foo => 1}), :routing_key => 'foobar')
|
138
|
+
queue.write Karait::Message.new({:foo => 2})
|
139
|
+
|
140
|
+
messages = queue.read(:routing_key => 'foobar')
|
141
|
+
assert_equal 1, messages.count
|
142
|
+
assert_equal 1, messages[0].foo
|
143
|
+
end
|
144
|
+
|
145
|
+
should "only return messages with no routing key when none is provided" do
|
146
|
+
queue = Karait::Queue.new(
|
147
|
+
:database => 'karait_test',
|
148
|
+
:queue => 'queue_test'
|
149
|
+
)
|
150
|
+
|
151
|
+
queue.write(Karait::Message.new({:foo => 1}), :routing_key => 'foobar')
|
152
|
+
queue.write Karait::Message.new({:foo => 2})
|
153
|
+
|
154
|
+
messages = queue.read()
|
155
|
+
assert_equal 1, messages.count
|
156
|
+
assert_equal 2, messages[0].foo
|
157
|
+
end
|
158
|
+
|
159
|
+
should "should no longer return a message when delete is called on it" do
|
160
|
+
queue = Karait::Queue.new(
|
161
|
+
:database => 'karait_test',
|
162
|
+
:queue => 'queue_test'
|
163
|
+
)
|
164
|
+
|
165
|
+
queue.write Karait::Message.new({:foo => 1})
|
166
|
+
messages = queue.read()
|
167
|
+
assert_equal 1, messages.count
|
168
|
+
assert_equal 1, messages[0].foo
|
169
|
+
|
170
|
+
messages[0].delete
|
171
|
+
messages = queue.read()
|
172
|
+
assert_equal 0, messages.count
|
173
|
+
end
|
174
|
+
|
175
|
+
should "not remove message immediately with expire set" do
|
176
|
+
queue = Karait::Queue.new(
|
177
|
+
:database => 'karait_test',
|
178
|
+
:queue => 'queue_test'
|
179
|
+
)
|
180
|
+
|
181
|
+
queue.write(Karait::Message.new({:foo => 1}), :expire => 0.5)
|
182
|
+
sleep(0.1)
|
183
|
+
messages = queue.read()
|
184
|
+
assert_equal 1, messages.count
|
185
|
+
end
|
186
|
+
|
187
|
+
should "remove message once expire time is passed" do
|
188
|
+
queue = Karait::Queue.new(
|
189
|
+
:database => 'karait_test',
|
190
|
+
:queue => 'queue_test'
|
191
|
+
)
|
192
|
+
|
193
|
+
queue.write(Karait::Message.new({:foo => 1}), :expire => 0.1)
|
194
|
+
sleep(0.2)
|
195
|
+
messages = queue.read()
|
196
|
+
assert_equal 0, messages.count
|
197
|
+
|
198
|
+
# Make sure the meta._expired key is actually set.
|
199
|
+
collection = Mongo::Connection.new()['karait_test']['queue_test']
|
200
|
+
raw_message = collection.find_one
|
201
|
+
assert_equal true, raw_message['_meta']['expired']
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
should "remove all messages in array when delete messages called" do
|
206
|
+
queue = Karait::Queue.new(
|
207
|
+
:database => 'karait_test',
|
208
|
+
:queue => 'queue_test'
|
209
|
+
)
|
210
|
+
|
211
|
+
queue.write Karait::Message.new({'foo' => 1})
|
212
|
+
queue.write Karait::Message.new({:foo => 2})
|
213
|
+
queue.write Karait::Message.new({'foo' => 3})
|
214
|
+
messages = queue.read()
|
215
|
+
queue.delete_messages messages
|
216
|
+
messages = queue.read()
|
217
|
+
assert_equal 0, messages.count
|
218
|
+
end
|
219
|
+
|
220
|
+
should "not see message in queue again until visibility timeout has passed" do
|
221
|
+
queue = Karait::Queue.new(
|
222
|
+
:database => 'karait_test',
|
223
|
+
:queue => 'queue_test'
|
224
|
+
)
|
225
|
+
|
226
|
+
queue.write Karait::Message.new({'foo' => 1})
|
227
|
+
queue.write Karait::Message.new({:foo => 2})
|
228
|
+
queue.write Karait::Message.new({'foo' => 3})
|
229
|
+
messages = queue.read(:visibility_timeout => 0.4)
|
230
|
+
assert_equal 3, messages.count
|
231
|
+
messages = queue.read(:visibility_timeout => 0.4)
|
232
|
+
assert_equal 0, messages.count
|
233
|
+
sleep(0.5)
|
234
|
+
messages = queue.read(:visibility_timeout => 0.4)
|
235
|
+
assert_equal 3, messages.count
|
236
|
+
end
|
237
|
+
|
238
|
+
should "block on reading from queue until polling timeout reached" do
|
239
|
+
start_time = Time.new.to_f
|
240
|
+
|
241
|
+
queue = Karait::Queue.new(
|
242
|
+
:database => 'karait_test',
|
243
|
+
:queue => 'queue_test'
|
244
|
+
)
|
245
|
+
|
246
|
+
queue.read( :block => true, :polling_timeout => 0.1 )
|
247
|
+
queue.write Karait::Message.new({'foo' => 1})
|
248
|
+
messages = queue.read( :block => true, :polling_timeout => 0.1 )
|
249
|
+
|
250
|
+
assert_equal 1, messages.count
|
251
|
+
stop_time = Time.new.to_f
|
252
|
+
assert_equal true, stop_time - start_time > 0.1
|
253
|
+
end
|
254
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: karait
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
|
11
|
-
version: 0.0.7
|
9
|
+
- 8
|
10
|
+
version: 0.0.8
|
12
11
|
platform: ruby
|
13
12
|
authors:
|
14
13
|
- bcoe
|
@@ -16,90 +15,9 @@ autorequire:
|
|
16
15
|
bindir: bin
|
17
16
|
cert_chain: []
|
18
17
|
|
19
|
-
date: 2011-11-12 00:00:00
|
20
|
-
|
21
|
-
|
22
|
-
- !ruby/object:Gem::Dependency
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
name: mongo
|
26
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
27
|
-
none: false
|
28
|
-
requirements:
|
29
|
-
- - ~>
|
30
|
-
- !ruby/object:Gem::Version
|
31
|
-
hash: 25
|
32
|
-
segments:
|
33
|
-
- 1
|
34
|
-
- 3
|
35
|
-
- 1
|
36
|
-
segments_generated: true
|
37
|
-
version: 1.3.1
|
38
|
-
requirement: *id001
|
39
|
-
- !ruby/object:Gem::Dependency
|
40
|
-
type: :development
|
41
|
-
prerelease: false
|
42
|
-
name: shoulda
|
43
|
-
version_requirements: &id002 !ruby/object:Gem::Requirement
|
44
|
-
none: false
|
45
|
-
requirements:
|
46
|
-
- - ">="
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
hash: 3
|
49
|
-
segments:
|
50
|
-
- 0
|
51
|
-
segments_generated: true
|
52
|
-
version: "0"
|
53
|
-
requirement: *id002
|
54
|
-
- !ruby/object:Gem::Dependency
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
name: bundler
|
58
|
-
version_requirements: &id003 !ruby/object:Gem::Requirement
|
59
|
-
none: false
|
60
|
-
requirements:
|
61
|
-
- - ~>
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
hash: 23
|
64
|
-
segments:
|
65
|
-
- 1
|
66
|
-
- 0
|
67
|
-
- 0
|
68
|
-
segments_generated: true
|
69
|
-
version: 1.0.0
|
70
|
-
requirement: *id003
|
71
|
-
- !ruby/object:Gem::Dependency
|
72
|
-
type: :development
|
73
|
-
prerelease: false
|
74
|
-
name: jeweler
|
75
|
-
version_requirements: &id004 !ruby/object:Gem::Requirement
|
76
|
-
none: false
|
77
|
-
requirements:
|
78
|
-
- - ~>
|
79
|
-
- !ruby/object:Gem::Version
|
80
|
-
hash: 7
|
81
|
-
segments:
|
82
|
-
- 1
|
83
|
-
- 6
|
84
|
-
- 4
|
85
|
-
segments_generated: true
|
86
|
-
version: 1.6.4
|
87
|
-
requirement: *id004
|
88
|
-
- !ruby/object:Gem::Dependency
|
89
|
-
type: :development
|
90
|
-
prerelease: false
|
91
|
-
name: rcov
|
92
|
-
version_requirements: &id005 !ruby/object:Gem::Requirement
|
93
|
-
none: false
|
94
|
-
requirements:
|
95
|
-
- - ">="
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
hash: 3
|
98
|
-
segments:
|
99
|
-
- 0
|
100
|
-
segments_generated: true
|
101
|
-
version: "0"
|
102
|
-
requirement: *id005
|
18
|
+
date: 2011-11-12 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
103
21
|
description: A ridiculously simple cross-language queuing system, built on top of MongoDB.
|
104
22
|
email: bencoe@gmail.com
|
105
23
|
executables: []
|
@@ -110,9 +28,20 @@ extra_rdoc_files:
|
|
110
28
|
- LICENSE.txt
|
111
29
|
- README.markdown
|
112
30
|
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- Gemfile.lock
|
113
34
|
- LICENSE.txt
|
114
35
|
- README.markdown
|
115
|
-
|
36
|
+
- Rakefile
|
37
|
+
- VERSION
|
38
|
+
- karait.gemspec
|
39
|
+
- lib/karait.rb
|
40
|
+
- lib/message.rb
|
41
|
+
- lib/queue.rb
|
42
|
+
- test/helper.rb
|
43
|
+
- test/test_message.rb
|
44
|
+
- test/test_queue.rb
|
116
45
|
homepage: http://github.com/bcoe/karait
|
117
46
|
licenses:
|
118
47
|
- MIT
|
@@ -129,7 +58,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
58
|
hash: 3
|
130
59
|
segments:
|
131
60
|
- 0
|
132
|
-
segments_generated: true
|
133
61
|
version: "0"
|
134
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
63
|
none: false
|
@@ -139,12 +67,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
67
|
hash: 3
|
140
68
|
segments:
|
141
69
|
- 0
|
142
|
-
segments_generated: true
|
143
70
|
version: "0"
|
144
71
|
requirements: []
|
145
72
|
|
146
73
|
rubyforge_project:
|
147
|
-
rubygems_version: 1.
|
74
|
+
rubygems_version: 1.8.11
|
148
75
|
signing_key:
|
149
76
|
specification_version: 3
|
150
77
|
summary: A ridiculously simple cross-language queuing system, built on top of MongoDB.
|