queue_classic 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,7 +6,11 @@ module QC
6
6
  end
7
7
 
8
8
  def enqueue(job,*params)
9
- queue.enqueue(job,params)
9
+ if job.respond_to?(:details) and job.respond_to?(:params)
10
+ queue.enqueue(job.signature, (job.params || []))
11
+ else
12
+ queue.enqueue(job,params)
13
+ end
10
14
  end
11
15
 
12
16
  def dequeue
@@ -17,6 +21,10 @@ module QC
17
21
  queue.delete(job)
18
22
  end
19
23
 
24
+ def delete_all
25
+ queue.delete_all
26
+ end
27
+
20
28
  def queue_length
21
29
  queue.length
22
30
  end
@@ -26,7 +34,11 @@ module QC
26
34
  method = job.method
27
35
  params = job.params
28
36
 
29
- klass.send(method,params)
37
+ if params.class == Array
38
+ klass.send(method,*params)
39
+ else
40
+ klass.send(method,params)
41
+ end
30
42
  end
31
43
 
32
44
  def logging_enabled?
@@ -3,9 +3,6 @@ module QC
3
3
 
4
4
  def initialize(args={})
5
5
  @db_string = args[:database]
6
- @connection = connection
7
- execute("SET client_min_messages TO 'warning'")
8
- with_log("setup PG LISTEN") { execute("LISTEN jobs") }
9
6
  end
10
7
 
11
8
  def <<(details)
@@ -18,7 +15,7 @@ module QC
18
15
  end
19
16
 
20
17
  def delete(job)
21
- with_log("deleting job #{job.id}") { execute("DELETE FROM jobs WHERE id = #{job.id}") }
18
+ execute("DELETE FROM jobs WHERE id = #{job.id}")
22
19
  job
23
20
  end
24
21
 
@@ -28,7 +25,7 @@ module QC
28
25
 
29
26
  def lock_head
30
27
  job = nil
31
- @connection.transaction do
28
+ connection.transaction do
32
29
  if job = find_one {"SELECT * FROM jobs WHERE locked_at IS NULL ORDER BY id ASC LIMIT 1 FOR UPDATE"}
33
30
  execute("UPDATE jobs SET locked_at = (CURRENT_TIMESTAMP) WHERE id = #{job.id} AND locked_at IS NULL")
34
31
  end
@@ -40,37 +37,32 @@ module QC
40
37
  if job = lock_head
41
38
  job
42
39
  else
43
- @connection.wait_for_notify {|e,p,msg| job = lock_head if msg == "new-job" }
40
+ execute("LISTEN jobs")
41
+ connection.wait_for_notify {|e,p,msg| job = lock_head if msg == "new-job" }
44
42
  job
45
43
  end
46
44
  end
47
45
 
48
46
  def each
49
47
  execute("SELECT * FROM jobs ORDER BY id ASC").each do |r|
50
- yield(JSON.parse(r["details"]))
48
+ yield Job.new(r)
51
49
  end
52
50
  end
53
51
 
54
52
  def execute(sql)
55
- @connection.async_exec(sql)
53
+ connection.exec(sql)
56
54
  end
57
55
 
58
56
  def find_one
59
57
  res = execute(yield)
60
58
  if res.count > 0
61
- res.map do |r|
62
- Job.new(
63
- "id" => r["id"],
64
- "details" => JSON.parse( r["details"]),
65
- "locked_at" => r["locked_at"]
66
- )
67
- end.pop
59
+ res.map {|r| Job.new(r)}.pop
68
60
  end
69
61
  end
70
62
 
71
63
  def connection
72
64
  db_params = URI.parse(@db_string)
73
- if db_params.scheme == "postgres"
65
+ @connection ||= if db_params.scheme == "postgres"
74
66
  PGconn.connect(
75
67
  :dbname => db_params.path.gsub("/",""),
76
68
  :user => db_params.user,
@@ -82,19 +74,5 @@ module QC
82
74
  end
83
75
  end
84
76
 
85
- def with_log(msg)
86
- res = yield
87
- if QC.logging_enabled?
88
- log(msg)
89
- log(res.cmd_status) if res.respond_to?(:cmd_status)
90
- log(res.result_error_message) if res.respond_to?(:result_error_message)
91
- end
92
- res
93
- end
94
-
95
- def log(msg)
96
- puts "| \t" + msg
97
- end
98
-
99
77
  end
100
78
  end
@@ -4,19 +4,24 @@ module QC
4
4
 
5
5
  def initialize(args={})
6
6
  @id = args["id"]
7
- @details = args["details"]
7
+ @details = JSON.parse(args["details"])
8
8
  @locked_at = args["locked_at"]
9
9
  end
10
10
 
11
11
  def klass
12
- Kernel.const_get(details["job"].split(".").first)
12
+ eval(details["job"].split(".").first)
13
13
  end
14
14
 
15
15
  def method
16
16
  details["job"].split(".").last
17
17
  end
18
18
 
19
+ def signature
20
+ details["job"]
21
+ end
22
+
19
23
  def params
24
+ return [] unless details["params"]
20
25
  params = details["params"]
21
26
  if params.length == 1
22
27
  return params[0]
@@ -19,6 +19,10 @@ module QC
19
19
  @data.delete(job)
20
20
  end
21
21
 
22
+ def delete_all
23
+ @data.each {|j| delete(j) }
24
+ end
25
+
22
26
  def length
23
27
  @data.count
24
28
  end
@@ -1,11 +1,11 @@
1
1
  # Queue Classic
2
- __Beta 0.2.0__
2
+ __Beta 0.2.1__
3
3
 
4
- __Queue Classic 0.2.0 is in Beta.__ I have been using this library with 30-50 Heroku workers and have had great results. However, your mileage may vary.
4
+ __Queue Classic 0.2.1 is in Beta.__ I have been using this library with 30-50 Heroku workers and have had great results. However, your mileage may vary.
5
5
 
6
6
  I am using this in production applications and plan to maintain and support this library for a long time.
7
7
 
8
- Queue Classic is an alternative queueing library for Ruby apps (Rails, Sinatra, Etc...) Queue Classic features __asynchronous__ job polling, database maintained locks and
8
+ Queue Classic is an alternative queueing library for Ruby apps (Rails, Sinatra, Etc...) Queue Classic features a blocking dequeue, database maintained locks and
9
9
  no ridiculous dependencies. As a matter of fact, Queue Classic only requires the __pg__ and __json__.
10
10
 
11
11
  ## Installation
@@ -1,14 +1,31 @@
1
1
  require File.expand_path("../helper.rb", __FILE__)
2
2
 
3
- class ApiTest < MiniTest::Unit::TestCase
4
- include DatabaseHelpers
3
+ context "QC::Api" do
4
+ setup { clean_database }
5
5
 
6
- def test_enqueue_takes_a_job
7
- clean_database
6
+ test "enqueue takes a job without params" do
7
+ QC.enqueue "Notifier.send"
8
8
 
9
- assert_equal 0, QC.queue_length
10
- res = QC.enqueue "Notifier.send", {}
11
- assert_equal 1, QC.queue_length
9
+ job = QC.dequeue
10
+ assert_equal({"job" => "Notifier.send", "params" => [] }, job.details)
12
11
  end
12
+
13
+ test "enqueue takes a hash" do
14
+ QC.enqueue "Notifier.send", {:arg => 1}
15
+
16
+ job = QC.dequeue
17
+ assert_equal({"job" => "Notifier.send", "params" => [{"arg" => 1}] }, job.details)
18
+ end
19
+
20
+ test "enqueue takes a job" do
21
+ h = {"id" => 1, "details" => {"job" => 'Notifier.send'}.to_json, "locked_at" => nil}
22
+ job = QC::Job.new(h)
23
+ QC.enqueue(job)
24
+
25
+ job = QC.dequeue
26
+ assert_equal({"job" => "Notifier.send", "params" => []}, job.details)
27
+ end
28
+
29
+
13
30
  end
14
31
 
@@ -47,8 +47,10 @@ class DurableArrayTest < MiniTest::Unit::TestCase
47
47
  job = array.first
48
48
 
49
49
  assert_equal( {"job" => "one"}, job.details)
50
+
51
+ assert_equal(1,array.count)
50
52
  array.delete(job)
51
- assert_nil array.first
53
+ assert_equal(0,array.count)
52
54
  end
53
55
 
54
56
  def test_delete_returns_job_after_delete
@@ -61,7 +63,6 @@ class DurableArrayTest < MiniTest::Unit::TestCase
61
63
  assert_equal({"job" => "one"}, job.details)
62
64
 
63
65
  res = array.delete(job)
64
- assert_nil(array.first)
65
66
  assert_equal({"job" => "one"}, res.details)
66
67
  end
67
68
 
@@ -72,7 +73,7 @@ class DurableArrayTest < MiniTest::Unit::TestCase
72
73
  array << {"job" => "one"}
73
74
  array << {"job" => "two"}
74
75
  results = []
75
- array.each {|v| results << v}
76
+ array.each {|v| results << v.details}
76
77
  assert_equal([{"job" => "one"},{"job" => "two"}], results)
77
78
  end
78
79
 
@@ -16,3 +16,19 @@ def set_data_store(store=nil)
16
16
  )
17
17
  )
18
18
  end
19
+
20
+ def context(*args, &block)
21
+ return super unless (name = args.first) && block
22
+ klass = Class.new(MiniTest::Unit::TestCase) do
23
+ def self.test(name, &block)
24
+ define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
25
+ end
26
+ def self.xtest(*args) end
27
+ def self.setup(&block) define_method(:setup, &block) end
28
+ def self.teardown(&block) define_method(:teardown, &block) end
29
+ end
30
+ (class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
31
+
32
+ klass.send :include, DatabaseHelpers
33
+ klass.class_eval &block
34
+ end
@@ -0,0 +1,82 @@
1
+ require File.expand_path("../helper.rb", __FILE__)
2
+
3
+ context "QC::Job" do
4
+
5
+ test "initialize takes details as JSON" do
6
+ job = QC::Job.new(
7
+ "id" => 1,
8
+ "details" => "{\"arg\":1}",
9
+ "locked_at" => nil
10
+ )
11
+ assert_equal({"arg" => 1}, job.details)
12
+ end
13
+
14
+ test "signature returns the class and method" do
15
+ job = QC::Job.new(
16
+ "id" => 1,
17
+ "details" => {:job => "Class.method", :params => []}.to_json,
18
+ "locked_at" => nil
19
+ )
20
+ assert_equal "Class.method", job.signature
21
+ end
22
+
23
+ test "method returns the class method" do
24
+ job = QC::Job.new(
25
+ "id" => 1,
26
+ "details" => {:job => "Class.method", :params => []}.to_json,
27
+ "locked_at" => nil
28
+ )
29
+ assert_equal "method", job.method
30
+ end
31
+
32
+ test "klass returns the class" do
33
+ class WhoHa; end
34
+ job = QC::Job.new(
35
+ "id" => 1,
36
+ "details" => {:job => "WhoHa.method", :params => []}.to_json,
37
+ "locked_at" => nil
38
+ )
39
+ assert_equal WhoHa, job.klass
40
+ end
41
+
42
+ test "klass returns the class when scoped to module" do
43
+ module Mod
44
+ class K
45
+ end
46
+ end
47
+
48
+ job = QC::Job.new(
49
+ "id" => 1,
50
+ "details" => {:job => "Mod::K.method", :params => []}.to_json,
51
+ "locked_at" => nil
52
+ )
53
+ assert_equal Mod::K, job.klass
54
+ end
55
+
56
+ test "params returns empty array when nil" do
57
+ job = QC::Job.new(
58
+ "id" => 1,
59
+ "details" => {:job => "Mod::K.method", :params => nil}.to_json,
60
+ "locked_at" => nil
61
+ )
62
+ assert_equal [], job.params
63
+ end
64
+
65
+ test "params returns 1 items when there is 1 param" do
66
+ job = QC::Job.new(
67
+ "id" => 1,
68
+ "details" => {:job => "Mod::K.method", :params => ["arg"]}.to_json,
69
+ "locked_at" => nil
70
+ )
71
+ assert_equal "arg", job.params
72
+ end
73
+
74
+ test "params retuns many items when there are many params" do
75
+ job = QC::Job.new(
76
+ "id" => 1,
77
+ "details" => {:job => "Mod::K.method", :params => ["arg","arg"]}.to_json,
78
+ "locked_at" => nil
79
+ )
80
+ assert_equal ["arg","arg"], job.params
81
+ end
82
+ end
@@ -19,4 +19,15 @@ class QueueTest < MiniTest::Unit::TestCase
19
19
  assert_equal 1, QC::Queue.instance.length
20
20
  end
21
21
 
22
+ def test_queue_delete_all
23
+ QC::Queue.instance.setup :data_store => []
24
+
25
+ QC::Queue.instance.enqueue "job","params"
26
+ QC::Queue.instance.enqueue "job","params"
27
+
28
+ assert_equal 2, QC::Queue.instance.length
29
+ QC::Queue.instance.delete_all
30
+ assert_equal 0, QC::Queue.instance.length
31
+ end
32
+
22
33
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 0
9
- version: 0.2.0
8
+ - 1
9
+ version: 0.2.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ryan Smith
@@ -55,7 +55,6 @@ extra_rdoc_files: []
55
55
 
56
56
  files:
57
57
  - readme.markdown
58
- - lib/notifier.rb
59
58
  - lib/queue_classic/api.rb
60
59
  - lib/queue_classic/durable_array.rb
61
60
  - lib/queue_classic/job.rb
@@ -67,6 +66,7 @@ files:
67
66
  - test/database_helpers.rb
68
67
  - test/durable_array_test.rb
69
68
  - test/helper.rb
69
+ - test/job_test.rb
70
70
  - test/queue_test.rb
71
71
  - test/worker_test.rb
72
72
  has_rdoc: true
@@ -104,5 +104,6 @@ summary: Queue Classic is an alternative queueing library for Ruby apps (Rails,
104
104
  test_files:
105
105
  - test/api_test.rb
106
106
  - test/durable_array_test.rb
107
+ - test/job_test.rb
107
108
  - test/queue_test.rb
108
109
  - test/worker_test.rb
@@ -1,7 +0,0 @@
1
- class Notifier
2
-
3
- def self.deliver(msg)
4
- `say #{msg}`
5
- end
6
-
7
- end