mongo-dequeue 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'json'
6
+ gem 'mongo'
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "shoulda", ">= 0"
11
+ gem "bundler", "~> 1.0.0"
12
+ gem "jeweler", "~> 1.6.2"
13
+ gem "rcov", ">= 0"
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ bson (1.3.1)
5
+ git (1.2.5)
6
+ jeweler (1.6.2)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ json (1.5.3)
11
+ mongo (1.3.1)
12
+ bson (>= 1.3.1)
13
+ rake (0.9.2)
14
+ rcov (0.9.9)
15
+ shoulda (2.11.3)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ bundler (~> 1.0.0)
22
+ jeweler (~> 1.6.2)
23
+ json
24
+ mongo
25
+ rcov
26
+ shoulda
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 TelegramSam
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
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,84 @@
1
+ = Mongo::Dequeue
2
+
3
+ A de-duplicating priority queue that uses mongodb as the storage engine.
4
+
5
+ Heavily inspired by Mongo::Queue https://github.com/skiz/mongo_queue
6
+
7
+ == Features
8
+ * Atomic Locking (provided by Mongo)
9
+ * Thread Safe
10
+ * Priority Support
11
+ * Fully Tested
12
+ * Simple API
13
+
14
+ == Examples
15
+
16
+ === Setting up a queue
17
+ Mongo::Dequeue requires a Mongo::Connection to your Mongo Server, and allows you to configure the database,
18
+ collection, and the default read timeout of items in your queue. Items popped from the queue must be confirmed
19
+ complete, or they will be reissued after the timeout specified.
20
+
21
+ db = Mongo::Connection.new
22
+
23
+ options = {
24
+ :database => 'whateverdb',
25
+ :collection => 'my_queue',
26
+ :timeout => 60
27
+ }
28
+
29
+ queue = Mongo::Dequeue.new(db, options)
30
+
31
+ === Pushing Items
32
+ Items have a body, passed as the first argument to push(). A body can be a string, number, hash, or array. These
33
+ values are preserved in the Mongo collection used to store the queue, allowing you to inspect the queue and see these values.
34
+ The optional options argument is a hash of values. you can set a custom duplicate_key that will be used to de-duplicate
35
+ queue items. There are no keys or reserved values in the body of an item. Pass in anything you like.
36
+
37
+
38
+ options = {
39
+ :duplicate_key => 'match'
40
+ }
41
+
42
+ body = "foo"
43
+ body = 5
44
+ body = {
45
+ :stuff => "here"
46
+ }
47
+ body = ["a", "b", "c"]
48
+
49
+ queue.push(body,options)
50
+
51
+ === Popping Items
52
+ Pop items with the pop() method. Specifying a timeout (in seconds) will override the default timeout set in
53
+ the queue options. After the timeout, the popped item will be available in another pop() call.
54
+
55
+ item = queue.pop(:timeout => 1)
56
+
57
+ The pop call will return a hash, with an :id and a :body. After the queue item is done, be sure to call the
58
+ complete() method, with the id of the message.
59
+
60
+ queue.complete(item[:id])
61
+
62
+ Note that completed items are not removed from the database immediately, allowing inspection of completion times,
63
+ duplication numbers, etc. Calling the cleanup() method removes completed items.
64
+
65
+ queue.cleanup()
66
+
67
+ === Queue Status
68
+ You can call the +stats+ method to take a quick peek at your queue. Locked items are items that have been popped,
69
+ but not confirmed.
70
+
71
+ queue.stats
72
+ # => {:available => 3, :locked => 2, :complete => 5, :total => 10}
73
+
74
+ === Mongo::Queue Comparison
75
+
76
+ * Items are not deleted immediately after completion.
77
+ * Item locks auto-expire
78
+ * Items are de-duplicated
79
+ * No reserved words in item body
80
+
81
+ === TODO
82
+
83
+ * Adjust auto generated duplication_key to be more accurate.
84
+ * Full examples
data/Rakefile ADDED
@@ -0,0 +1,36 @@
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 = "mongo-dequeue"
18
+ gem.homepage = "http://github.com/TelegramSam/Dequeue"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Mongo based de-duplicating pritority queue}
21
+ gem.description = %Q{A de-duplicating priority queue that uses mongodb as the storage engine.}
22
+ gem.email = "telegramsam@gmail.com"
23
+ gem.authors = ["TelegramSam"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/rdoctask'
29
+ Rake::RDocTask.new do |rdoc|
30
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
31
+
32
+ rdoc.rdoc_dir = 'rdoc'
33
+ rdoc.title = "mongo-dequeue #{version}"
34
+ rdoc.rdoc_files.include('README*')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,149 @@
1
+ require 'digest/md5'
2
+ require 'json'
3
+ require 'mongo'
4
+
5
+ # heavily inspired by https://github.com/skiz/mongo_queue
6
+
7
+ class Mongo::Dequeue
8
+ attr_reader :connection, :config
9
+
10
+ DEFAULT_CONFIG = {
11
+ :database => 'mongo_dequeue',
12
+ :collection => 'mongo_dequeue',
13
+ :timeout => 300,
14
+ :default_priority => 3
15
+ }.freeze
16
+
17
+ # Create a new instance of MongoDequeue with the provided mongodb connection and optional configuration.
18
+ # See +DEFAULT_CONFIG+ for default configuration and possible configuration options.
19
+ #
20
+ # Example:
21
+ # db = Mongo::Connection.new('localhost')
22
+ # config = {:timeout => 90, :attempts => 2}
23
+ # queue = Mongo::Queue.new(db, config)
24
+ #
25
+ def initialize(connection, opts={})
26
+ @connection = connection
27
+ @config = DEFAULT_CONFIG.merge(opts)
28
+ end
29
+
30
+ # Remove all items from the queue. Use with caution!
31
+ def flush!
32
+ collection.drop
33
+ end
34
+
35
+ # Insert a new item into the queue.
36
+ #
37
+ # Example:
38
+ # queue.insert(:name => 'Billy', :email => 'billy@example.com', :message => 'Here is the thing you asked for')
39
+ def push(body, item_opts = {})
40
+ dup_key = item_opts[:duplicate_key] || Mongo::Dequeue.generate_duplicate_key(body)
41
+
42
+ selector = {
43
+ :duplicate_key => dup_key,
44
+ :complete => false,
45
+ :locked_at => nil
46
+ }
47
+ item = {
48
+ '$set' => {
49
+ :body => body,
50
+ :inserted_at => Time.now.utc,
51
+ :complete => false,
52
+ :locked_at => nil,
53
+ :completed_at => nil,
54
+ :priority => item_opts[:priority] || @config[:default_priority],
55
+ :duplicate_key => dup_key
56
+ },
57
+ '$inc' => {:count => 1 }
58
+ }
59
+
60
+ id = collection.update(selector, item, :upsert => true)
61
+ end
62
+
63
+ # Lock and return the next queue message if one is available. Returns nil if none are available. Be sure to
64
+ # review the README.rdoc regarding proper usage of the locking process identifier (locked_by).
65
+ # Example:
66
+ # doc = queue.pop()
67
+
68
+ # {:body=>"foo", :id=>"4e039c372b70275e345206e4"}
69
+
70
+ def pop(opts = {})
71
+ begin
72
+ timeout = opts[:timeout] || @config[:timeout]
73
+ cmd = BSON::OrderedHash.new
74
+ cmd['findandmodify'] = @config[:collection]
75
+ cmd['update'] = {'$set' => {:locked_till => Time.now.utc+timeout}}
76
+ cmd['query'] = {:complete => false, '$or'=>[{:locked_till=> nil},{:locked_till=>{'$lt'=>Time.now.utc}}] }
77
+ cmd['sort'] = {:priority=>-1,:inserted_at=>1}
78
+ cmd['limit'] = 1
79
+ cmd['new'] = true
80
+ result = collection.db.command(cmd)
81
+ rescue Mongo::OperationFailure => of
82
+ return nil
83
+ end
84
+ return {
85
+ :body => result['value']['body'],
86
+ :id => result['value']['_id'].to_s
87
+ }
88
+ end
89
+
90
+
91
+ # Remove the document from the queue. This should be called when the work is done and the document is no longer needed.
92
+ # You must provide the process identifier that the document was locked with to complete it.
93
+ def complete(id)
94
+ cmd = BSON::OrderedHash.new
95
+ cmd['findandmodify'] = @config[:collection]
96
+ cmd['query'] = {:_id => BSON::ObjectId.from_string(id), :complete => false}
97
+ cmd['update'] = {:completed_at => Time.now.utc, :complete => true}
98
+ cmd['limit'] = 1
99
+ collection.db.command(cmd)
100
+ end
101
+
102
+ # Removes completed job history
103
+ def cleanup()
104
+ collection.remove({:complete=>true});
105
+ end
106
+
107
+ # Provides some information about what is in the queue. We are using an eval to ensure that a
108
+ # lock is obtained during the execution of this query so that the results are not skewed.
109
+ # please be aware that it will lock the database during the execution, so avoid using it too
110
+ # often, even though it it very tiny and should be relatively fast.
111
+ def stats
112
+ js = "function queue_stat(){
113
+ return db.eval(
114
+ function(){
115
+ var nowutc = new Date();
116
+ var a = db.#{config[:collection]}.count({'complete': false, '$or':[{'locked_till':null},{'locked_till':{'$lt':nowutc}}] });
117
+ var c = db.#{config[:collection]}.count({'complete': true});
118
+ var t = db.#{config[:collection]}.count();
119
+ var l = db.#{config[:collection]}.count({'complete': false, 'locked_till': {'$gte':nowutc} });
120
+ return [a, c, t, l];
121
+ }
122
+ );
123
+ }"
124
+ available, complete, total, locked = collection.db.eval(js)
125
+ { :locked => locked.to_i,
126
+ :complete => complete.to_i,
127
+ :available => available.to_i,
128
+ :total => total.to_i }
129
+ end
130
+
131
+ def self.generate_duplicate_key(body)
132
+ return Digest::MD5.hexdigest(body) if body.class == "String"
133
+ return Digest::MD5.hexdigest(body) if body.class == "Fixnum"
134
+ #else
135
+ return Digest::MD5.hexdigest(body.to_json) #won't ever match a duplicate. Need a better way to handle hashes and arrays.
136
+ end
137
+
138
+
139
+ protected
140
+
141
+ def value_of(result) #:nodoc:
142
+ result['okay'] == 0 ? nil : result['value']
143
+ end
144
+
145
+ def collection #:nodoc:
146
+ @connection.db(@config[:database]).collection(@config[:collection])
147
+ end
148
+
149
+ end
@@ -0,0 +1,256 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'pp'
3
+
4
+ describe Mongo::Dequeue do
5
+
6
+ def insert_and_inspect(body, options={})
7
+ @queue.push(body,options)
8
+ @queue.send(:collection).find_one
9
+ end
10
+
11
+
12
+ before(:all) do
13
+ opts = {
14
+ :database => 'mongo_queue_spec',
15
+ :collection => 'spec',
16
+ :timeout => 60}
17
+ @db = Mongo::Connection.new('localhost', nil, :pool_size => 4)
18
+ @queue = Mongo::Dequeue.new(@db, opts)
19
+ end
20
+
21
+ before(:each) do
22
+ @queue.flush!
23
+ end
24
+
25
+ describe "Configuration" do
26
+
27
+ it "should set the connection" do
28
+ @queue.connection.should be(@db)
29
+ end
30
+
31
+ it "should allow database option" do
32
+ @queue.config[:database].should eql('mongo_queue_spec')
33
+ end
34
+
35
+ it "should allow collection option" do
36
+ @queue.config[:collection].should eql('spec')
37
+ end
38
+
39
+ it "should allow timeout option" do
40
+ @queue.config[:timeout].should eql(60)
41
+ end
42
+
43
+ it "should have a sane set of defaults" do
44
+ q = Mongo::Dequeue.new(nil)
45
+ q.config[:collection].should eql 'mongo_dequeue'
46
+ q.config[:timeout].should eql 300
47
+ end
48
+ end
49
+
50
+ describe "Inserting a standard Job" do
51
+ before(:each) do
52
+ @item = insert_and_inspect({:message => 'MongoQueueSpec', :foo => 5})
53
+ end
54
+
55
+ it "should set priority to 3 by default" do
56
+ @item['priority'].should be(3)
57
+ end
58
+
59
+ it "should not be complete" do
60
+ @item['complete'].should be false
61
+ end
62
+
63
+ it "should have a null completed_at" do
64
+ @item['completed_at'].should be nil
65
+ end
66
+
67
+ it "should set a null locked_at" do
68
+ @item['locked_at'].should be nil
69
+ end
70
+
71
+ it "should have no duplicates" do
72
+ @item['count'].should be 1
73
+ end
74
+
75
+ it "should have a duplicate_key" do
76
+ @item['duplicate_key'].should_not be nil
77
+ end
78
+
79
+ it "should return struct body properly" do
80
+ @item['body']['message'].should eql('MongoQueueSpec')
81
+ @item['body']['foo'].should be(5)
82
+ end
83
+ end
84
+
85
+ describe "Inserting different body types" do
86
+ before(:each) do
87
+ @queue.flush!
88
+ end
89
+
90
+ it "should handle a struct" do
91
+ i = insert_and_inspect({:message => 'MongoQueueSpec', :foo => 5})
92
+ i['body']['message'].should eql('MongoQueueSpec')
93
+ i['body']['foo'].should be(5)
94
+ end
95
+
96
+ it "should handle a string" do
97
+ i = insert_and_inspect("foobarbaz")
98
+ i['body'].should eql "foobarbaz"
99
+ end
100
+
101
+ it "should handle a number" do
102
+ i = insert_and_inspect(42)
103
+ i['body'].should be 42
104
+ end
105
+ end
106
+
107
+ describe "Deduplicating messages" do
108
+ before(:each) do
109
+ @queue.flush!
110
+ end
111
+
112
+ it "should combine identical bodies of type string" do
113
+ a = insert_and_inspect("foo")
114
+ b = insert_and_inspect("foo")
115
+ @queue.send(:collection).count.should be 1
116
+ end
117
+
118
+ it "should not combine different bodies of type string" do
119
+ a = insert_and_inspect("foo")
120
+ b = insert_and_inspect("bar")
121
+ @queue.send(:collection).count.should be 2
122
+ b['count'].should be 1
123
+ end
124
+
125
+ it "should combine identical bodies of type struct" do
126
+ pending "Test after we have a better way of handling structs"
127
+ a = insert_and_inspect({:a=>'a',:b=>'b'})
128
+ b = insert_and_inspect({:a=>'a',:b=>'b'})
129
+ c = insert_and_inspect({:b=>'b',:a=>'a'})
130
+ @queue.send(:collection).count.should be 1
131
+ end
132
+
133
+ it "should not combine different bodies of type struct" do
134
+ a = insert_and_inspect({:a=>'a',:b=>'b'})
135
+ b = insert_and_inspect({:a=>'a',:c=>'c'})
136
+ @queue.send(:collection).count.should be 2
137
+ b['count'].should be 1
138
+ end
139
+
140
+ it "should combine based on duplication_key" do
141
+ a = insert_and_inspect({:a=>'a',:b=>'b'}, :duplicate_key => 'match')
142
+ b = insert_and_inspect({:a=>'a',:c=>'c'}, :duplicate_key => 'match')
143
+ @queue.send(:collection).count.should be 1
144
+ b['count'].should be 2
145
+ end
146
+
147
+ it "should not combine based on duplication_key" do
148
+ a = insert_and_inspect("foo", :duplicate_key => 'match')
149
+ b = insert_and_inspect("foo", :duplicate_key => 'nomatch')
150
+ @queue.send(:collection).count.should be 2
151
+ b['count'].should be 1
152
+ end
153
+
154
+ end
155
+
156
+ describe "Popping messages" do
157
+ before(:each) do
158
+ @queue.flush!
159
+ end
160
+
161
+ it "should return message" do
162
+ a = insert_and_inspect("foo")
163
+ m = @queue.pop
164
+ m[:body].should eq "foo"
165
+ @queue.send(:collection).count.should be 1
166
+ end
167
+
168
+ it "should return an id" do
169
+ a = insert_and_inspect("foo")
170
+ m = @queue.pop
171
+ m[:id].should_not be nil
172
+ @queue.send(:collection).count.should be 1
173
+ end
174
+
175
+ it "should return nil when queue is empty" do
176
+ m = @queue.pop
177
+ m.should be nil
178
+ @queue.send(:collection).count.should be 0
179
+ end
180
+
181
+ it "should complete ok" do
182
+ a = insert_and_inspect("foo")
183
+ m = @queue.pop
184
+ @queue.complete(m[:id])
185
+ m2 = @queue.pop
186
+ m2.should be nil
187
+ @queue.send(:collection).count.should be 1
188
+ end
189
+
190
+ it "should pop again after timeout" do
191
+ a = insert_and_inspect("foo")
192
+ m = @queue.pop(:timeout => 1)
193
+ sleep(2)
194
+ m2 = @queue.pop
195
+ m2[:id].should eq m[:id]
196
+ @queue.send(:collection).count.should be 1
197
+ end
198
+
199
+ it "should pop in order" do
200
+ @a = insert_and_inspect("a")
201
+ @b = insert_and_inspect("b")
202
+ @c = insert_and_inspect("c")
203
+ @d = insert_and_inspect("d")
204
+
205
+ @ap = @queue.pop
206
+ @bp = @queue.pop
207
+ @cp = @queue.pop
208
+ @dp = @queue.pop
209
+
210
+ @ap[:body].should eq "a"
211
+ @bp[:body].should eq "b"
212
+ @cp[:body].should eq "c"
213
+ @dp[:body].should eq "d"
214
+ end
215
+
216
+ end
217
+
218
+ describe "Stats" do
219
+ before(:all) do
220
+ @queue.flush!
221
+ @a = insert_and_inspect("a")
222
+
223
+ @b = insert_and_inspect("b")
224
+ @c = insert_and_inspect("c")
225
+ @d = insert_and_inspect("d")
226
+ @e = insert_and_inspect("e")
227
+
228
+ @ap = @queue.pop(:timeout => 1)
229
+ @bp = @queue.pop
230
+ @cp = @queue.pop
231
+
232
+ sleep(2)
233
+
234
+ @queue.complete(@bp[:id])
235
+
236
+ @stats = @queue.stats
237
+ end
238
+ #locked, complete, available, total
239
+ it "should count complete" do
240
+ @stats[:complete].should == 1
241
+ end
242
+ it "should count total" do
243
+ @stats[:total].should == 5
244
+ end
245
+ it "should count available" do
246
+ @stats[:available].should == 3
247
+ end
248
+ it "should count locked" do
249
+ @stats[:locked].should == 1
250
+ end
251
+
252
+
253
+ end
254
+
255
+
256
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ require 'mongo'
6
+ require 'mongo-dequeue'
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo-dequeue
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - TelegramSam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-29 00:00:00 -06:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: json
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: mongo
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: shoulda
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: bundler
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.0.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: jeweler
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.6.2
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rcov
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ description: A de-duplicating priority queue that uses mongodb as the storage engine.
83
+ email: telegramsam@gmail.com
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - LICENSE.txt
90
+ - README.rdoc
91
+ files:
92
+ - Gemfile
93
+ - Gemfile.lock
94
+ - LICENSE.txt
95
+ - README.rdoc
96
+ - Rakefile
97
+ - VERSION
98
+ - lib/mongo-dequeue.rb
99
+ - spec/mongo_dequeue_spec.rb
100
+ - spec/spec_helper.rb
101
+ has_rdoc: true
102
+ homepage: http://github.com/TelegramSam/Dequeue
103
+ licenses:
104
+ - MIT
105
+ post_install_message:
106
+ rdoc_options: []
107
+
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 4221506242229050956
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project:
128
+ rubygems_version: 1.5.3
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Mongo based de-duplicating pritority queue
132
+ test_files: []
133
+