gustin-rudeq 2.1.0.2
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/MIT-LICENSE +20 -0
- data/README +64 -0
- data/Rakefile +22 -0
- data/generators/rude_q/USAGE +24 -0
- data/generators/rude_q/rude_q_generator.rb +27 -0
- data/generators/rude_q/templates/rude_q_migration.rb +18 -0
- data/generators/rude_q/templates/rude_q_model.rb +3 -0
- data/generators/rude_q/templates/rude_q_model_spec.rb +71 -0
- data/lib/rude_q.rb +220 -0
- data/lib/rude_q/scope.rb +24 -0
- data/lib/rude_q/tasks.rb +25 -0
- data/lib/rude_q/worker.rb +66 -0
- data/spec/database.yml +4 -0
- data/spec/models/rude_queue.rb +14 -0
- data/spec/models/something.rb +3 -0
- data/spec/rude_q_spec.rb +372 -0
- data/spec/schema.rb +19 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/worker_spec.rb +49 -0
- data/tasks/rails.rake +1 -0
- metadata +84 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 [Matthew Rudy Jacobs]
|
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
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
== Author
|
2
|
+
Matthew Rudy Jacobs
|
3
|
+
|
4
|
+
== Contact
|
5
|
+
MatthewRudyJacobs@gmail.com
|
6
|
+
|
7
|
+
RudeQ
|
8
|
+
=============
|
9
|
+
A simple DB based queue,
|
10
|
+
designed for situations where a server based queue is unnecessary.
|
11
|
+
|
12
|
+
|
13
|
+
INSTALL
|
14
|
+
============
|
15
|
+
This plugin requires Rails 2.* currently, and has only been tested on MySQL.
|
16
|
+
|
17
|
+
On rails 2.1 you can install straight from github:
|
18
|
+
ruby script/plugin install git://github.com/matthewrudy/rudeq.git
|
19
|
+
|
20
|
+
Else just check it out into your plugins directory:
|
21
|
+
git clone git://github.com/matthewrudy/rudeq.git vendor/plugins/rudeq
|
22
|
+
|
23
|
+
USAGE
|
24
|
+
============
|
25
|
+
After you've installed it just run
|
26
|
+
rake queue:setup
|
27
|
+
|
28
|
+
matthew@iRudy:~/code/jbequeueing $ rake queue:setup
|
29
|
+
(in /Users/matthew/code/jbequeueing)
|
30
|
+
exists app/models/
|
31
|
+
exists spec/fixtures/
|
32
|
+
exists spec/models/
|
33
|
+
create app/models/rude_queue.rb
|
34
|
+
create spec/fixtures/rude_queues.yml
|
35
|
+
create spec/models/rude_queue_spec.rb
|
36
|
+
exists db/migrate
|
37
|
+
create db/migrate/029_create_rude_queues.rb
|
38
|
+
|
39
|
+
and you're done.
|
40
|
+
Fully tested, fully index... BOOM!
|
41
|
+
|
42
|
+
Now run migrations, start up a console, and;
|
43
|
+
|
44
|
+
RudeQueue.set(:queue_name, RandomObject)
|
45
|
+
RudeQueue.get(:queue_name)
|
46
|
+
RudeQueue.fetch(:queue_name) do |data|
|
47
|
+
process(data)
|
48
|
+
end
|
49
|
+
|
50
|
+
And, to keep the queue running fast,
|
51
|
+
set up a cron job to run
|
52
|
+
|
53
|
+
rake queue:cleanup
|
54
|
+
|
55
|
+
the cleanup will remove any queued items which have been processed longer than an hour ago.
|
56
|
+
|
57
|
+
rake queue:cleanup CLEANUP_TIME=86,400
|
58
|
+
|
59
|
+
will clear processed queue items processed longer than 86,400 seconds ago (1 day)
|
60
|
+
|
61
|
+
Try Yourself!
|
62
|
+
|
63
|
+
Copyright (c) 2008 [Matthew Rudy Jacobs Email: MatthewRudyJacobs@gmail.com],
|
64
|
+
released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
desc 'Default: run the specs.'
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc 'Run specs for rude_q plugin'
|
10
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
11
|
+
t.spec_opts = ['--options', "\"spec/spec.opts\""]
|
12
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the rude_q plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'RudeQ'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Description:
|
2
|
+
The rude_q generator creates a fully functioning RudeQ.
|
3
|
+
|
4
|
+
The generator takes a model name as its argument. The model name may be
|
5
|
+
given in CamelCase or under_score and should not be suffixed with 'Model'.
|
6
|
+
|
7
|
+
The generator creates a model class in app/models, an RSpec spec in
|
8
|
+
spec/models, database fixtures in spec/fixtures/plural_name.yml, and a migration
|
9
|
+
in db/migrate.
|
10
|
+
|
11
|
+
All the generated files are fully functioning, and no extra work should be necessary.
|
12
|
+
|
13
|
+
Example:
|
14
|
+
./script/generate rude_q ProcessQueue
|
15
|
+
|
16
|
+
This will create a ProcessQueue model:
|
17
|
+
Model: app/models/process_queue.rb
|
18
|
+
Spec: spec/models/process_queue_spec.rb
|
19
|
+
Fixtures: spec/fixtures/process_queues.yml
|
20
|
+
Migration: db/migrate/XXX_add_process_queues.rb
|
21
|
+
|
22
|
+
Run migrations, and you can use it immediately;
|
23
|
+
ProcessQueue.set(queue_name, value)
|
24
|
+
ProcessQueue.get(queue_name)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class RudeQGenerator < Rails::Generator::NamedBase
|
2
|
+
def manifest
|
3
|
+
|
4
|
+
record do |m|
|
5
|
+
# Check for class naming collisions.
|
6
|
+
m.class_collisions class_path, class_name
|
7
|
+
|
8
|
+
# Model, spec, and fixture directories.
|
9
|
+
m.directory File.join('app/models', class_path)
|
10
|
+
m.directory File.join('spec/fixtures', class_path)
|
11
|
+
m.directory File.join('spec/models', class_path)
|
12
|
+
|
13
|
+
# Model class, spec and fixtures.
|
14
|
+
m.template 'rude_q_model.rb', File.join('app/models', class_path, "#{file_name}.rb")
|
15
|
+
m.template 'model:fixtures.yml', File.join('spec/fixtures', class_path, "#{table_name}.yml")
|
16
|
+
m.template 'rude_q_model_spec.rb', File.join('spec/models', class_path, "#{file_name}_spec.rb")
|
17
|
+
|
18
|
+
unless options[:skip_migration]
|
19
|
+
m.migration_template 'rude_q_migration.rb', 'db/migrate', :assigns => {
|
20
|
+
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
|
21
|
+
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class <%= migration_name %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%= table_name %> do |t|
|
4
|
+
t.string :queue_name
|
5
|
+
t.text :data
|
6
|
+
t.boolean :processed, :default => false, :null => false
|
7
|
+
<% unless options[:skip_timestamps] %>
|
8
|
+
t.timestamps
|
9
|
+
<% end -%>
|
10
|
+
end
|
11
|
+
add_index :<%= table_name %>, :processed
|
12
|
+
add_index :<%= table_name %>, [:queue_name, :processed]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :<%= table_name %>
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../spec_helper'
|
2
|
+
|
3
|
+
describe <%= class_name %> do
|
4
|
+
before(:each) do
|
5
|
+
@<%= file_name %> = <%= class_name %>.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be valid" do
|
9
|
+
@<%= file_name %>.should be_valid
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "get and set" do
|
13
|
+
it "should work with strings" do
|
14
|
+
<%= class_name %>.set('abcde', "Something to set")
|
15
|
+
<%= class_name %>.get('abcde').should == "Something to set"
|
16
|
+
end
|
17
|
+
it "should work with symbols" do
|
18
|
+
<%= class_name %>.set('abcde', :a_symbol)
|
19
|
+
<%= class_name %>.get('abcde').should == :a_symbol
|
20
|
+
end
|
21
|
+
it "should work with arrays" do
|
22
|
+
array = [1, :b, "C"]
|
23
|
+
<%= class_name %>.set('abcde', array)
|
24
|
+
<%= class_name %>.get('abcde').should == array
|
25
|
+
end
|
26
|
+
it "should work with hashes" do
|
27
|
+
hash = {:symbol => "A string", "stringy" => 23, 74 => :cheese}
|
28
|
+
<%= class_name %>.set('abcde', hash)
|
29
|
+
<%= class_name %>.get('abcde').should == hash
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should :get in the same order they are :set" do
|
33
|
+
<%= class_name %>.set('abcde', :first)
|
34
|
+
<%= class_name %>.set('abcde', "second")
|
35
|
+
|
36
|
+
<%= class_name %>.get('abcde').should == :first
|
37
|
+
|
38
|
+
<%= class_name %>.set('abcde', 33.3333)
|
39
|
+
|
40
|
+
<%= class_name %>.get('abcde').should == "second"
|
41
|
+
<%= class_name %>.get('abcde').should == 33.3333
|
42
|
+
<%= class_name %>.get('abcde').should be(nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should keep queues seperated" do
|
46
|
+
<%= class_name %>.set('queue_1', :data_1)
|
47
|
+
<%= class_name %>.set('queue_2', "DATA2")
|
48
|
+
|
49
|
+
<%= class_name %>.get('queue_2').should == "DATA2"
|
50
|
+
<%= class_name %>.get('queue_2').should be(nil)
|
51
|
+
<%= class_name %>.get('queue_1').should == :data_1
|
52
|
+
<%= class_name %>.get('queue_1').should be(nil)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should work with queue name as strings or symbols" do
|
56
|
+
<%= class_name %>.set(:bah, "something about bah")
|
57
|
+
<%= class_name %>.get("bah").should == "something about bah"
|
58
|
+
|
59
|
+
<%= class_name %>.set("girah", {:craziness => "embodied"})
|
60
|
+
<%= class_name %>.get(:girah).should == {:craziness => "embodied"}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should work with queue name as strings or integers" do
|
64
|
+
<%= class_name %>.set(23, "something about bah")
|
65
|
+
<%= class_name %>.get("23").should == "something about bah"
|
66
|
+
|
67
|
+
<%= class_name %>.set("34", {:craziness => "embodied"})
|
68
|
+
<%= class_name %>.get(34).should == {:craziness => "embodied"}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/rude_q.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
# RudeQ
|
2
|
+
|
3
|
+
# simply doing;
|
4
|
+
# class RudeQueue < ActiveRecord::Base
|
5
|
+
# include RudeQ
|
6
|
+
# end
|
7
|
+
# will include RudeQ::ClassMethods
|
8
|
+
# :get
|
9
|
+
# :set
|
10
|
+
module RudeQ
|
11
|
+
|
12
|
+
def self.included(mod) # :nodoc:
|
13
|
+
mod.extend(ClassMethods)
|
14
|
+
mod.send(:include, InstanceMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
def data # :nodoc:
|
19
|
+
YAML.load(self[:data])
|
20
|
+
end
|
21
|
+
def data=(value) # :nodoc:
|
22
|
+
self[:data] = YAML.dump(value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
# Cleanup old processed items
|
28
|
+
#
|
29
|
+
# RudeQueue.cleanup!
|
30
|
+
# RudeQueue.cleanup!(1.week)
|
31
|
+
def cleanup!(expiry=1.hour)
|
32
|
+
self.delete_all(["processed = ? AND updated_at < ?", true, expiry.to_i.ago])
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add any serialize-able +data+ to the queue +queue_name+ (strings and symbols are treated the same)
|
36
|
+
# RudeQueue.set(:sausage_queue, Sausage.new(:sauce => "yummy"))
|
37
|
+
# RudeQueue.set("sausage_queue", Sausage.new(:other => true))
|
38
|
+
#
|
39
|
+
# >> RudeQueue.get("sausage_queue")
|
40
|
+
# -> *yummy sausage*
|
41
|
+
# >> RudeQueue.get(:sausage_queue)
|
42
|
+
# -> *other_sausage*
|
43
|
+
# >> RudeQueue.get(:sausage_queue)
|
44
|
+
# -> nil
|
45
|
+
def set(queue_name, data)
|
46
|
+
queue_name = sanitize_queue_name(queue_name)
|
47
|
+
|
48
|
+
self.create!(:queue_name => queue_name, :data => data)
|
49
|
+
return nil # in line with Starling
|
50
|
+
end
|
51
|
+
|
52
|
+
# Grab the first item from the queue *queue_name* (strings and symbols are treated the same)
|
53
|
+
# - it should always come out the same as it went in
|
54
|
+
# - they should always come out in the same order they went in
|
55
|
+
# - it will return a nil if there is no unprocessed entry in the queue
|
56
|
+
#
|
57
|
+
# >> RudeQueue.get(21)
|
58
|
+
# -> {:a => "hash"}
|
59
|
+
# >> RudeQueue.get(:a_symbol)
|
60
|
+
# -> 255
|
61
|
+
# >> RudeQueue.get("a string")
|
62
|
+
# -> nil
|
63
|
+
def get(queue_name)
|
64
|
+
qname = sanitize_queue_name(queue_name)
|
65
|
+
|
66
|
+
fetch_with_lock(qname) do |record|
|
67
|
+
if record
|
68
|
+
processed!(record)
|
69
|
+
return record.data
|
70
|
+
else
|
71
|
+
return nil # Starling waits indefinitely for a corresponding queue item
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Grab the first item from the queue, and execute the supplied block if there is one
|
77
|
+
# - it will return the value of the block
|
78
|
+
#
|
79
|
+
# >> RudeQueue.fetch(:my_queue) do |data|
|
80
|
+
# >> Monster.devour(data)
|
81
|
+
# >> end
|
82
|
+
# -> nil
|
83
|
+
#
|
84
|
+
# >> status = RudeQueue.fetch(:my_queue) do |data|
|
85
|
+
# >> process(data) # returns the value :update in this case
|
86
|
+
# >> end
|
87
|
+
# -> :update
|
88
|
+
# >> status
|
89
|
+
# -> :update
|
90
|
+
def fetch(queue_name, &block)
|
91
|
+
if data = get(queue_name)
|
92
|
+
return block.call(data)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# A snapshot count of unprocessed items for the given +queue_name+
|
97
|
+
#
|
98
|
+
# >> RudeQueue.backlog
|
99
|
+
# -> 265
|
100
|
+
# >> RudeQueue.backlog(:one_queue)
|
101
|
+
# -> 212
|
102
|
+
# >> RudeQueue.backlog(:another_queue)
|
103
|
+
# -> 53
|
104
|
+
#
|
105
|
+
def backlog(queue_name=nil)
|
106
|
+
conditions = {:processed => false}
|
107
|
+
if queue_name
|
108
|
+
conditions[:queue_name] = sanitize_queue_name(queue_name)
|
109
|
+
end
|
110
|
+
self.count(:conditions => conditions)
|
111
|
+
end
|
112
|
+
|
113
|
+
def fetch_with_lock(qname, &block) # :nodoc:
|
114
|
+
lock = case queue_options[:lock]
|
115
|
+
when :pessimistic then RudeQ::PessimisticLock
|
116
|
+
when :token then RudeQ::TokenLock
|
117
|
+
else
|
118
|
+
raise(ArgumentError, "bad queue_option for :lock - #{queue_options[:lock].inspect}")
|
119
|
+
end
|
120
|
+
lock.fetch_with_lock(self, qname, &block)
|
121
|
+
end
|
122
|
+
|
123
|
+
# class method to make it more easily stubbed
|
124
|
+
def processed!(record) # :nodoc:
|
125
|
+
case queue_options[:processed]
|
126
|
+
when :set_flag
|
127
|
+
record.update_attribute(:processed, true)
|
128
|
+
when :destroy
|
129
|
+
record.destroy
|
130
|
+
else
|
131
|
+
raise(ArgumentError, "bad queue_option for :processed - #{queue_options[:processed].inspect}")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
protected :processed!
|
135
|
+
|
136
|
+
# configure your RudeQ
|
137
|
+
# ==== :processed - what do we do after retrieving a queue item?
|
138
|
+
# * <tt>:set_flag</tt> - set the +processed+ flag to +true+ (keep data in the db) [*default*]
|
139
|
+
# * <tt>:destroy</tt> - destroy the processed item (keep our queue as lean as possible
|
140
|
+
#
|
141
|
+
# ==== :lock - what locking method should we use?
|
142
|
+
# * <tt>:pessimistic</tt> - RudeQ::PessimisticLock [*default*]
|
143
|
+
# * <tt>:token</tt> - RudeQ::TokenLock
|
144
|
+
def queue_options
|
145
|
+
@queue_options ||= {:processed => :set_flag, :lock => :pessimistic}
|
146
|
+
end
|
147
|
+
|
148
|
+
def data # :nodoc:
|
149
|
+
YAML.load(self[:data])
|
150
|
+
end
|
151
|
+
def data=(value) # :nodoc:
|
152
|
+
self[:data] = YAML.dump(value)
|
153
|
+
end
|
154
|
+
private
|
155
|
+
|
156
|
+
def sanitize_queue_name(queue_name) # :nodoc:
|
157
|
+
queue_name.to_s
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# uses standard ActiveRecord :lock => true
|
162
|
+
# this will invoke a lock on the particular queue
|
163
|
+
# eg. daemon1: RudeQueue.get(:abc)
|
164
|
+
# daemon2: RudeQueue.get(:abc) - will have to wait for daemon1 to finish
|
165
|
+
# daemon3: RudeQueue.get(:def) - will avoid the lock
|
166
|
+
module PessimisticLock
|
167
|
+
class << self
|
168
|
+
|
169
|
+
def fetch_with_lock(klass, qname) # :nodoc:
|
170
|
+
klass.transaction do
|
171
|
+
record = klass.find(:first,
|
172
|
+
:conditions => {:queue_name => qname, :processed => false},
|
173
|
+
:lock => true, :order => "id ASC", :limit => 1)
|
174
|
+
|
175
|
+
return yield(record)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# a crazy hack around database locking
|
183
|
+
# that I thought was a good idea
|
184
|
+
# turns out we can't make it use transactions properly
|
185
|
+
# without creating a whole table lock
|
186
|
+
# which misses the point
|
187
|
+
#
|
188
|
+
# also, it doesn't work on SQLite as it requires "UPDATE ... LIMIT 1 ORDER id ASC"
|
189
|
+
# and as of RudeQueue2, you'll need to manually add the "token" column
|
190
|
+
module TokenLock
|
191
|
+
class << self
|
192
|
+
|
193
|
+
require 'digest/sha1'
|
194
|
+
|
195
|
+
def fetch_with_lock(klass, qname) # :nodoc:
|
196
|
+
token = get_unique_token
|
197
|
+
klass.update_all(["token = ?", token], ["queue_name = ? AND processed = ? AND token IS NULL", qname, false], :limit => 1, :order => "id ASC")
|
198
|
+
record = klass.find_by_queue_name_and_token_and_processed(qname, token, false)
|
199
|
+
|
200
|
+
return yield(record)
|
201
|
+
end
|
202
|
+
|
203
|
+
def token_count! # :nodoc:
|
204
|
+
@token_count ||= 0
|
205
|
+
@token_count += 1
|
206
|
+
return @token_count
|
207
|
+
end
|
208
|
+
|
209
|
+
def get_unique_token # :nodoc:
|
210
|
+
digest = Digest::SHA1.new
|
211
|
+
digest << Time.now.to_s
|
212
|
+
digest << Process.pid.to_s
|
213
|
+
digest << Socket.gethostname
|
214
|
+
digest << self.token_count!.to_s # multiple requests from the same pid in the same second get different token
|
215
|
+
return digest.hexdigest
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
data/lib/rude_q/scope.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../rude_q"
|
2
|
+
|
3
|
+
module RudeQ
|
4
|
+
class Scope
|
5
|
+
|
6
|
+
def initialize(queue_name)
|
7
|
+
@queue_name = queue_name
|
8
|
+
end
|
9
|
+
attr_reader :queue_name
|
10
|
+
|
11
|
+
def set(data)
|
12
|
+
RudeQueue.set(self.queue_name, data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get()
|
16
|
+
RudeQueue.get(self.queue_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def backlog()
|
20
|
+
RudeQueue.backlog(self.queue_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/rude_q/tasks.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
namespace :queue do
|
2
|
+
desc "Generates your RudeQueue model"
|
3
|
+
task :setup => :environment do
|
4
|
+
require 'rails_generator'
|
5
|
+
require 'rails_generator/scripts/generate'
|
6
|
+
Rails::Generator::Scripts::Generate.new.run(["rude_q", ENV["QUEUE"] || "RudeQueue"])
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Removes all the old queue items"
|
10
|
+
task :cleanup => :environment do
|
11
|
+
queue_model = (ENV["QUEUE"] || "RudeQueue").constantize
|
12
|
+
args = [ENV["CLEANUP_TIME"]].compact
|
13
|
+
queue_model.cleanup!(*args) # no arg if no CLEANUP_TIME specified
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
#namespace :spec do
|
18
|
+
# namespace :plugins do
|
19
|
+
# desc "Runs the examples for RudeQ"
|
20
|
+
# Spec::Rake::SpecTask.new(:rude_q) do |t|
|
21
|
+
# t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
22
|
+
# t.spec_files = FileList['vendor/plugins/rude_q/spec/**/*_spec.rb']
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# example worker class: lib/my_worker.rb
|
2
|
+
# class MyWorker < RudeQ::Worker
|
3
|
+
# def queue_name
|
4
|
+
# :my_queue
|
5
|
+
# end
|
6
|
+
#
|
7
|
+
# def do_work(data)
|
8
|
+
# MyMailer.send(data)
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# example rake file: lib/tasks/worker.rake
|
13
|
+
# namespace :worker do
|
14
|
+
# desc "fire off a worker"
|
15
|
+
# task :do => :environment do
|
16
|
+
# worker = MyWorker.new
|
17
|
+
# worker.do!
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# then add a cron job to run "cd /path/to/wherever && rake worker:do RAILS_ENV=production"
|
22
|
+
module RudeQ
|
23
|
+
class Worker
|
24
|
+
|
25
|
+
def queue_name
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
|
29
|
+
def do_work(data)
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
33
|
+
def do!
|
34
|
+
logger.info("starting up")
|
35
|
+
if work = self.queue.get
|
36
|
+
logger.info("found some work")
|
37
|
+
do_work(work)
|
38
|
+
else
|
39
|
+
logger.info("couldn't find any work")
|
40
|
+
end
|
41
|
+
logger.info("finished for now")
|
42
|
+
end
|
43
|
+
|
44
|
+
def logger
|
45
|
+
unless @logger
|
46
|
+
@logger = Logger.new(RAILS_ROOT + "/log/#{self.class.to_s.underscore}_#{RAILS_ENV}.log")
|
47
|
+
class << @logger
|
48
|
+
def format_message(severity, timestamp, progname, msg)
|
49
|
+
"#{timestamp.strftime('%Y%m%d-%H:%M:%S')} (#{$$}) #{msg}\n"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return @logger
|
54
|
+
end
|
55
|
+
|
56
|
+
class << self
|
57
|
+
def queue
|
58
|
+
RudeQ::Scope.new(self.new.queue_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def queue
|
63
|
+
@queue ||= self.class.queue
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class RudeQueue < ActiveRecord::Base
|
2
|
+
include RudeQ
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def processed_with_raise_hack!(*args)
|
6
|
+
processed_without_raise_hack!(*args)
|
7
|
+
raise RuntimeError if raise_on_processed # want to be able to raise afterwards to check transactions
|
8
|
+
end
|
9
|
+
alias_method_chain :processed!, :raise_hack
|
10
|
+
attr_accessor :raise_on_processed
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
data/spec/rude_q_spec.rb
ADDED
@@ -0,0 +1,372 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe RudeQ::ClassMethods do # RudeQueue extends ClassMethods
|
4
|
+
before(:each) do
|
5
|
+
RudeQueue.delete_all
|
6
|
+
RudeQueue.raise_on_processed = false
|
7
|
+
create_some_noise
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_some_noise
|
11
|
+
RudeQueue.create!(:queue_name => "doNT use this in Specs", :data => {:not => "to be messed with"})
|
12
|
+
RudeQueue.create!(:queue_name => "abcde", :data => {:same_as => "the specs but already processed"}, :processed => true)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "get and set" do
|
16
|
+
it "should work with strings" do
|
17
|
+
RudeQueue.set('abcde', "Something to set")
|
18
|
+
RudeQueue.get('abcde').should == "Something to set"
|
19
|
+
end
|
20
|
+
it "should work with symbols" do
|
21
|
+
RudeQueue.set('abcde', :a_symbol)
|
22
|
+
RudeQueue.get('abcde').should == :a_symbol
|
23
|
+
end
|
24
|
+
it "should work with arrays" do
|
25
|
+
array = [1, :b, "C"]
|
26
|
+
RudeQueue.set('abcde', array)
|
27
|
+
RudeQueue.get('abcde').should == array
|
28
|
+
end
|
29
|
+
it "should work with hashes" do
|
30
|
+
hash = {:symbol => "A string", "stringy" => 23, 74 => :cheese}
|
31
|
+
RudeQueue.set('abcde', hash)
|
32
|
+
RudeQueue.get('abcde').should == hash
|
33
|
+
end
|
34
|
+
it "should work with integers" do
|
35
|
+
RudeQueue.set('abcde', 7816327370)
|
36
|
+
RudeQueue.get('abcde').should == 7816327370
|
37
|
+
end
|
38
|
+
it "should work with ActiveRecords" do
|
39
|
+
record = Something.create!(:name => "MatthewRudy")
|
40
|
+
|
41
|
+
RudeQueue.set('abcde', record)
|
42
|
+
RudeQueue.get('abcde').should == record
|
43
|
+
end
|
44
|
+
it "should resolve booleans correctly" do
|
45
|
+
RudeQueue.set('abcde', true)
|
46
|
+
RudeQueue.get('abcde').should == true
|
47
|
+
|
48
|
+
RudeQueue.set('abcde', false)
|
49
|
+
RudeQueue.get('abcde').should == false
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should :get in the same order they are :set" do
|
53
|
+
RudeQueue.set('abcde', :first)
|
54
|
+
RudeQueue.set('abcde', "second")
|
55
|
+
|
56
|
+
RudeQueue.get('abcde').should == :first
|
57
|
+
|
58
|
+
RudeQueue.set('abcde', 33.3333)
|
59
|
+
|
60
|
+
RudeQueue.get('abcde').should == "second"
|
61
|
+
RudeQueue.get('abcde').should == 33.3333
|
62
|
+
RudeQueue.get('abcde').should be(nil)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should keep queues seperated" do
|
66
|
+
RudeQueue.set('queue_1', :data_1)
|
67
|
+
RudeQueue.set('queue_2', "DATA2")
|
68
|
+
|
69
|
+
RudeQueue.get('queue_2').should == "DATA2"
|
70
|
+
RudeQueue.get('queue_2').should be(nil)
|
71
|
+
RudeQueue.get('queue_1').should == :data_1
|
72
|
+
RudeQueue.get('queue_1').should be(nil)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should call to_s on inputs" do
|
76
|
+
qname = stub("fake input")
|
77
|
+
qname.should_receive(:to_s).exactly(:twice).and_return("fake queue name")
|
78
|
+
|
79
|
+
RudeQueue.set(qname, ["Data"])
|
80
|
+
RudeQueue.get(qname).should == ["Data"]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should work with queue name as strings or symbols" do
|
84
|
+
RudeQueue.set(:bah, "something about bah")
|
85
|
+
RudeQueue.get("bah").should == "something about bah"
|
86
|
+
|
87
|
+
RudeQueue.set("girah", {:craziness => "embodied"})
|
88
|
+
RudeQueue.get(:girah).should == {:craziness => "embodied"}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".set" do
|
93
|
+
it "should delegate to :create!" do
|
94
|
+
RudeQueue.should_receive(:create!).with(:queue_name => 'abcde', :data => :magical_planet)
|
95
|
+
RudeQueue.set('abcde', :magical_planet)
|
96
|
+
end
|
97
|
+
it "should return nil" do
|
98
|
+
RudeQueue.set('abcde', "something").should be(nil)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ".get" do
|
103
|
+
it "should revert a record if something goes wrong before it finishes" do
|
104
|
+
RudeQueue.raise_on_processed = true
|
105
|
+
RudeQueue.set('abcde', :this_will_remain_unprocessed)
|
106
|
+
|
107
|
+
# confirm the object is in the db
|
108
|
+
record = RudeQueue.find(:first, :order => "id DESC")
|
109
|
+
record.queue_name.should == 'abcde'
|
110
|
+
record.data.should == :this_will_remain_unprocessed
|
111
|
+
record.processed?.should == false
|
112
|
+
record.token.should == nil
|
113
|
+
|
114
|
+
lambda {RudeQueue.get('abcde')}.should raise_error(RuntimeError)
|
115
|
+
|
116
|
+
record.reload
|
117
|
+
record.queue_name.should == 'abcde'
|
118
|
+
record.data.should == :this_will_remain_unprocessed
|
119
|
+
record.processed?.should == false
|
120
|
+
record.token.should == nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "fetch" do
|
125
|
+
describe "with data" do
|
126
|
+
|
127
|
+
before(:each) do
|
128
|
+
RudeQueue.set(:fetch_queue, "some data")
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return the value of the block" do
|
132
|
+
rtn = RudeQueue.fetch(:fetch_queue) do |data|
|
133
|
+
data.should == "some data"
|
134
|
+
:the_return
|
135
|
+
end
|
136
|
+
rtn.should == :the_return
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should execute the block with the data" do
|
140
|
+
self.should_receive(:something)
|
141
|
+
RudeQueue.fetch(:fetch_queue) do |data|
|
142
|
+
self.something
|
143
|
+
data.should == "some data"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "without data" do
|
150
|
+
|
151
|
+
it "should not execute the block" do
|
152
|
+
self.should_not_receive(:something)
|
153
|
+
RudeQueue.fetch(:fetch_queue) do |data|
|
154
|
+
raise(Exception, "this should never get here")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should return nil" do
|
159
|
+
rtn = RudeQueue.fetch(:fetch_queue) do |data|
|
160
|
+
raise(Exception, "again this shouldnt happen")
|
161
|
+
end
|
162
|
+
rtn.should be_nil
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "queue_options" do
|
169
|
+
describe :processed do
|
170
|
+
describe "set to :destroy" do
|
171
|
+
before(:each) do
|
172
|
+
@old_processed = RudeQueue.queue_options[:processed]
|
173
|
+
RudeQueue.queue_options[:processed] = :destroy
|
174
|
+
end
|
175
|
+
after(:each) do
|
176
|
+
RudeQueue.queue_options[:processed] = @old_processed
|
177
|
+
end
|
178
|
+
it "should delete processed items" do
|
179
|
+
count = RudeQueue.count
|
180
|
+
|
181
|
+
RudeQueue.set(:abcde, "some value")
|
182
|
+
RudeQueue.count.should == (count + 1)
|
183
|
+
|
184
|
+
RudeQueue.get(:abcde).should == "some value"
|
185
|
+
RudeQueue.count.should == count
|
186
|
+
end
|
187
|
+
end
|
188
|
+
describe "set to something crazy" do
|
189
|
+
before(:each) do
|
190
|
+
@old_processed = RudeQueue.queue_options[:processed]
|
191
|
+
RudeQueue.queue_options[:processed] = :something_crazy
|
192
|
+
end
|
193
|
+
after(:each) do
|
194
|
+
RudeQueue.queue_options[:processed] = @old_processed
|
195
|
+
end
|
196
|
+
it "should raise an exception" do
|
197
|
+
RudeQueue.set(:abcde, "some value")
|
198
|
+
lambda {RudeQueue.get(:abcde)}.should raise_error(ArgumentError)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe ".backlog" do
|
205
|
+
it "should count the unprocessed items for the provided queue_name" do
|
206
|
+
RudeQueue.delete_all
|
207
|
+
|
208
|
+
RudeQueue.backlog(:abcde).should == 0
|
209
|
+
RudeQueue.backlog().should == 0
|
210
|
+
|
211
|
+
RudeQueue.set(:abcde, "a value")
|
212
|
+
RudeQueue.backlog(:abcde).should == 1
|
213
|
+
RudeQueue.backlog().should == 1
|
214
|
+
|
215
|
+
RudeQueue.set(:something_else, "another value")
|
216
|
+
3.times { RudeQueue.set(:abcde, :add_three_more)}
|
217
|
+
|
218
|
+
RudeQueue.backlog(:abcde).should == 4
|
219
|
+
RudeQueue.backlog().should == 5
|
220
|
+
|
221
|
+
RudeQueue.get(:abcde).should == "a value"
|
222
|
+
RudeQueue.backlog(:abcde).should == 3
|
223
|
+
RudeQueue.backlog().should == 4
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe ".cleanup!" do
|
228
|
+
it "should use :delete_all" do
|
229
|
+
RudeQueue.should_receive(:delete_all) # not :destroy_all
|
230
|
+
RudeQueue.cleanup!
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should allow string inputs" do
|
234
|
+
RudeQueue.cleanup!("3600")
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should allow integer inputs" do
|
238
|
+
RudeQueue.cleanup!(3600)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should not clear unprocessed items" do
|
242
|
+
RudeQueue.set('abcde', :giraffe)
|
243
|
+
RudeQueue.set('abcde', :monkey)
|
244
|
+
RudeQueue.count.should >= 2
|
245
|
+
|
246
|
+
RudeQueue.cleanup!
|
247
|
+
|
248
|
+
RudeQueue.count.should >=2
|
249
|
+
RudeQueue.get('abcde').should == :giraffe
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should not clear old unprocessed items" do
|
253
|
+
RudeQueue.set('abcde', :giraffe)
|
254
|
+
giraffe = RudeQueue.find(:first, :conditions => {:data => :giraffe})
|
255
|
+
|
256
|
+
time_now = Time.now
|
257
|
+
Time.stub!(:now).and_return(time_now + 1.year)
|
258
|
+
|
259
|
+
giraffe.updated_at.should < 2.weeks.ago
|
260
|
+
|
261
|
+
RudeQueue.cleanup!
|
262
|
+
|
263
|
+
giraffe.reload
|
264
|
+
RudeQueue.get('abcde').should == :giraffe
|
265
|
+
end
|
266
|
+
|
267
|
+
it "should not clear processed items newer than the argument" do
|
268
|
+
RudeQueue.set('abcde', :giraffe)
|
269
|
+
RudeQueue.get('abcde').should == :giraffe
|
270
|
+
|
271
|
+
giraffe = RudeQueue.find(:first, :conditions => {:data => :giraffe})
|
272
|
+
|
273
|
+
time_now = Time.now
|
274
|
+
Time.stub!(:now).and_return(time_now + 1.week - 5.minutes)
|
275
|
+
|
276
|
+
giraffe.updated_at.should > 1.week.ago
|
277
|
+
giraffe.processed.should be(true)
|
278
|
+
|
279
|
+
RudeQueue.cleanup!(1.week)
|
280
|
+
|
281
|
+
giraffe.reload
|
282
|
+
end
|
283
|
+
|
284
|
+
it "should not clear processed items newer than one hour, by default" do
|
285
|
+
RudeQueue.set('abcde', :giraffe)
|
286
|
+
RudeQueue.get('abcde').should == :giraffe
|
287
|
+
|
288
|
+
giraffe = RudeQueue.find(:first, :conditions => {:data => :giraffe})
|
289
|
+
|
290
|
+
time_now = Time.now
|
291
|
+
Time.stub!(:now).and_return(time_now + 59.minutes)
|
292
|
+
|
293
|
+
giraffe.updated_at.should > 1.hour.ago
|
294
|
+
giraffe.processed.should be(true)
|
295
|
+
|
296
|
+
RudeQueue.cleanup!()
|
297
|
+
|
298
|
+
giraffe.reload
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should clear processed items older than the argument" do
|
302
|
+
RudeQueue.set('abcde', :giraffe)
|
303
|
+
RudeQueue.get('abcde').should == :giraffe
|
304
|
+
|
305
|
+
giraffe = RudeQueue.find(:first, :conditions => {:data => :giraffe})
|
306
|
+
|
307
|
+
time_now = Time.now
|
308
|
+
Time.stub!(:now).and_return(time_now + 1.week + 5.minutes)
|
309
|
+
|
310
|
+
giraffe.updated_at.should < 1.week.ago
|
311
|
+
giraffe.processed.should be(true)
|
312
|
+
|
313
|
+
RudeQueue.cleanup!(1.week)
|
314
|
+
|
315
|
+
lambda { giraffe.reload }.should raise_error(ActiveRecord::RecordNotFound)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should clear processed items older than one hour, by default" do
|
319
|
+
RudeQueue.set('abcde', :giraffe)
|
320
|
+
RudeQueue.get('abcde').should == :giraffe
|
321
|
+
|
322
|
+
giraffe = RudeQueue.find(:first, :conditions => {:data => :giraffe})
|
323
|
+
|
324
|
+
time_now = Time.now()
|
325
|
+
Time.stub!(:now).and_return(time_now + 61.minutes)
|
326
|
+
|
327
|
+
giraffe.updated_at.should < 1.hour.ago
|
328
|
+
giraffe.processed.should be(true)
|
329
|
+
|
330
|
+
RudeQueue.cleanup!
|
331
|
+
|
332
|
+
lambda { giraffe.reload }.should raise_error(ActiveRecord::RecordNotFound)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
describe RudeQ::TokenLock do
|
338
|
+
|
339
|
+
describe ".get_unique_token" do
|
340
|
+
it "should create a unique token" do
|
341
|
+
lots_of_tokens = Array.new(50) do
|
342
|
+
RudeQ::TokenLock.get_unique_token
|
343
|
+
end
|
344
|
+
lots_of_tokens.uniq.should == lots_of_tokens
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should create a unique token even if time stands still" do
|
348
|
+
time_now = Time.now
|
349
|
+
Time.should_receive(:now).at_least(50).times.and_return(time_now)
|
350
|
+
lots_of_tokens = Array.new(50) do
|
351
|
+
RudeQ::TokenLock.get_unique_token
|
352
|
+
end
|
353
|
+
lots_of_tokens.uniq.should == lots_of_tokens
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# it "should not return a processed item with the same token" do
|
358
|
+
# @token = "tokEEEannn"
|
359
|
+
#
|
360
|
+
# RudeQ::TokenLock.should respond_to(:get_unique_token) # ensure our stub is safe
|
361
|
+
# RudeQ::TokenLock.should_receive(:get_unique_token).exactly(3).times.and_return(@token)
|
362
|
+
#
|
363
|
+
# @existing = RudeQueue.create!(:queue_name => 'abcde', :data => :old_data, :token => @token, :processed => true)
|
364
|
+
#
|
365
|
+
# RudeQueue.get('abcde').should be(nil)
|
366
|
+
#
|
367
|
+
# RudeQueue.set('abcde', :new_data)
|
368
|
+
# RudeQueue.get('abcde').should == :new_data
|
369
|
+
# RudeQueue.get('abcde').should be(nil)
|
370
|
+
# end
|
371
|
+
|
372
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
create_table :rude_queues, :force => true do |t|
|
3
|
+
t.string :queue_name
|
4
|
+
t.text :data
|
5
|
+
t.string :token, :default => nil
|
6
|
+
t.boolean :processed, :default => false, :null => false
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
add_index :rude_queues, :processed
|
11
|
+
add_index :rude_queues, [:queue_name, :processed]
|
12
|
+
|
13
|
+
create_table :somethings, :force => true do |t|
|
14
|
+
t.string :name
|
15
|
+
t.integer :count
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
current_dir = File.dirname(__FILE__)
|
6
|
+
require "#{current_dir}/../lib/rude_q"
|
7
|
+
require "#{current_dir}/../lib/rude_q/worker"
|
8
|
+
require "#{current_dir}/../lib/rude_q/scope"
|
9
|
+
require "#{current_dir}/models/rude_queue"
|
10
|
+
require "#{current_dir}/models/something"
|
11
|
+
config = YAML::load(IO.read(current_dir + '/database.yml'))
|
12
|
+
ActiveRecord::Base.logger = Logger.new(current_dir + "/debug.log")
|
13
|
+
ActiveRecord::Base.establish_connection(config['rude_q_test'])
|
14
|
+
load(current_dir + "/schema.rb")
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class ExampleWorker < RudeQ::Worker
|
4
|
+
def queue_name
|
5
|
+
:some_queue
|
6
|
+
end
|
7
|
+
|
8
|
+
# for the test, we'll just append each bit of data to a variable
|
9
|
+
attr_accessor :processed_data
|
10
|
+
|
11
|
+
def do_work(data)
|
12
|
+
self.processed_data ||= []
|
13
|
+
self.processed_data << data
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe RudeQ::Worker do
|
18
|
+
before(:each) do
|
19
|
+
@it = ExampleWorker.new
|
20
|
+
RudeQueue.delete_all
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "queue" do
|
24
|
+
it "should expose RudeQueue.get scoped for the worker's queue" do
|
25
|
+
RudeQueue.set(:some_queue, ["some data for the worker"])
|
26
|
+
@it.queue.get.should == ["some data for the worker"]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should expose RudeQueue.set scoped for the worker's queue" do
|
30
|
+
@it.queue.set(:some_other_data_for_the_worker)
|
31
|
+
RudeQueue.get(:some_queue).should == :some_other_data_for_the_worker
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should expose RudeQueue.backlog scoped for the worker's queue" do
|
35
|
+
RudeQueue.set(:who_knows, 1)
|
36
|
+
RudeQueue.set(:my_mum, 23)
|
37
|
+
|
38
|
+
RudeQueue.backlog.should == 2
|
39
|
+
RudeQueue.backlog(:some_queue).should == 0
|
40
|
+
@it.queue.backlog.should == 0
|
41
|
+
|
42
|
+
RudeQueue.set(:some_queue, "purple")
|
43
|
+
|
44
|
+
RudeQueue.backlog.should == 3
|
45
|
+
RudeQueue.backlog(:some_queue).should == 1
|
46
|
+
@it.queue.backlog.should == 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/tasks/rails.rake
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../lib/rude_q/tasks')
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gustin-rudeq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew Rudy Jacobs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: A simple DB queueing library built on top of ActiveRecord.
|
26
|
+
email: MatthewRudyJacobs@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
files:
|
34
|
+
- README
|
35
|
+
- Rakefile
|
36
|
+
- MIT-LICENSE
|
37
|
+
- lib/rude_q/worker.rb
|
38
|
+
- lib/rude_q/scope.rb
|
39
|
+
- lib/rude_q.rb
|
40
|
+
- generators/rude_q/templates/rude_q_model.rb
|
41
|
+
- generators/rude_q/templates/rude_q_model_spec.rb
|
42
|
+
- generators/rude_q/templates/rude_q_migration.rb
|
43
|
+
- generators/rude_q/rude_q_generator.rb
|
44
|
+
- generators/rude_q/USAGE
|
45
|
+
- spec/spec.opts
|
46
|
+
- spec/worker_spec.rb
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
- spec/database.yml
|
49
|
+
- spec/rude_q_spec.rb
|
50
|
+
- spec/models/rude_queue.rb
|
51
|
+
- spec/models/something.rb
|
52
|
+
- spec/schema.rb
|
53
|
+
- tasks/rails.rake
|
54
|
+
- lib/rude_q/tasks.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/matthewrudy/rudeq
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options:
|
59
|
+
- --main
|
60
|
+
- README
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.2.0
|
79
|
+
signing_key:
|
80
|
+
specification_version: 2
|
81
|
+
summary: ActiveRecord-based DB-queue
|
82
|
+
test_files:
|
83
|
+
- spec/rude_q_spec.rb
|
84
|
+
- spec/worker_spec.rb
|