queue_classic 2.0.0 → 2.0.1

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/lib/queue_classic.rb CHANGED
@@ -12,9 +12,6 @@ require "queue_classic/worker"
12
12
  require "queue_classic/setup"
13
13
 
14
14
  module QC
15
- # ENV["LOG_LEVEL"] is used in Scrolls
16
- Scrolls::Log.start
17
-
18
15
  Root = File.expand_path("..", File.dirname(__FILE__))
19
16
  SqlFunctions = File.join(QC::Root, "/sql/ddl.sql")
20
17
  DropSqlFunctions = File.join(QC::Root, "/sql/drop_ddl.sql")
@@ -12,6 +12,7 @@ module QC
12
12
  result.length > 1 ? result : result.pop
13
13
  rescue PGError => e
14
14
  log(:error => e.inspect)
15
+ disconnect
15
16
  raise
16
17
  end
17
18
  end
@@ -64,6 +65,7 @@ module QC
64
65
 
65
66
  def disconnect
66
67
  connection.finish
68
+ ensure
67
69
  @connection = nil
68
70
  end
69
71
 
@@ -1,4 +1,3 @@
1
- module QC
2
1
  # encoding: UTF-8
3
2
  #
4
3
  # Copyright 2011, 2012 Keith Rarick
@@ -28,6 +27,7 @@ require 'stringio'
28
27
  # Some parts adapted from
29
28
  # http://golang.org/src/pkg/json/decode.go and
30
29
  # http://golang.org/src/pkg/utf8/utf8.go
30
+ module QC
31
31
  module OkJson
32
32
  extend self
33
33
 
@@ -220,9 +220,9 @@ module OkJson
220
220
  end
221
221
 
222
222
 
223
- def nulltok(s); s[0,4] == 'null' && [:val, 'null', nil] end
224
- def truetok(s); s[0,4] == 'true' && [:val, 'true', true] end
225
- def falsetok(s); s[0,5] == 'false' && [:val, 'false', false] end
223
+ def nulltok(s); s[0,4] == 'null' ? [:val, 'null', nil] : [] end
224
+ def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end
225
+ def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end
226
226
 
227
227
 
228
228
  def numtok(s)
@@ -235,6 +235,8 @@ module OkJson
235
235
  else
236
236
  [:val, m[0], Integer(m[0])]
237
237
  end
238
+ else
239
+ []
238
240
  end
239
241
  end
240
242
 
@@ -375,15 +377,6 @@ module OkJson
375
377
  end
376
378
 
377
379
 
378
- def unsubst(u)
379
- if u < Usurrself || u > Umax || surrogate?(u)
380
- return Ucharerr, Ucharerr
381
- end
382
- u -= Usurrself
383
- [Usurr1 + ((u>>10)&0x3ff), Usurr2 + (u&0x3ff)]
384
- end
385
-
386
-
387
380
  def surrogate?(u)
388
381
  Usurr1 <= u && u < Usurr3
389
382
  end
@@ -473,15 +466,18 @@ module OkJson
473
466
  else
474
467
  c = s[r]
475
468
  case true
469
+ when rubydoesenc
470
+ begin
471
+ c.ord # will raise an error if c is invalid UTF-8
472
+ t.write(c)
473
+ rescue
474
+ t.write(Ustrerr)
475
+ end
476
476
  when Spc <= c && c <= ?~
477
477
  t.putc(c)
478
- when rubydoesenc
479
- u = c.ord
480
- surrenc(t, u)
481
478
  else
482
- u, size = uchardec(s, r)
483
- r += size - 1 # we add one more at the bottom of the loop
484
- surrenc(t, u)
479
+ n = ucharcopy(t, s, r) # ensure valid UTF-8 output
480
+ r += n - 1 # r is incremented below
485
481
  end
486
482
  end
487
483
  r += 1
@@ -491,28 +487,6 @@ module OkJson
491
487
  end
492
488
 
493
489
 
494
- def surrenc(t, u)
495
- if u < 0x10000
496
- t.print('\\u')
497
- hexenc4(t, u)
498
- else
499
- u1, u2 = unsubst(u)
500
- t.print('\\u')
501
- hexenc4(t, u1)
502
- t.print('\\u')
503
- hexenc4(t, u2)
504
- end
505
- end
506
-
507
-
508
- def hexenc4(t, u)
509
- t.putc(Hex[(u>>12)&0xf])
510
- t.putc(Hex[(u>>8)&0xf])
511
- t.putc(Hex[(u>>4)&0xf])
512
- t.putc(Hex[u&0xf])
513
- end
514
-
515
-
516
490
  def numenc(x)
517
491
  if ((x.nan? || x.infinite?) rescue false)
518
492
  raise Error, "Numeric cannot be represented: #{x}"
@@ -521,60 +495,77 @@ module OkJson
521
495
  end
522
496
 
523
497
 
524
- # Decodes unicode character u from UTF-8
525
- # bytes in string s at position i.
526
- # Returns u and the number of bytes read.
527
- def uchardec(s, i)
498
+ # Copies the valid UTF-8 bytes of a single character
499
+ # from string s at position i to I/O object t, and
500
+ # returns the number of bytes copied.
501
+ # If no valid UTF-8 char exists at position i,
502
+ # ucharcopy writes Ustrerr and returns 1.
503
+ def ucharcopy(t, s, i)
528
504
  n = s.length - i
529
- return [Ucharerr, 1] if n < 1
505
+ raise Utf8Error if n < 1
530
506
 
531
507
  c0 = s[i].ord
532
508
 
533
509
  # 1-byte, 7-bit sequence?
534
510
  if c0 < Utagx
535
- return [c0, 1]
511
+ t.putc(c0)
512
+ return 1
536
513
  end
537
514
 
538
- # unexpected continuation byte?
539
- return [Ucharerr, 1] if c0 < Utag2
515
+ raise Utf8Error if c0 < Utag2 # unexpected continuation byte?
540
516
 
541
- # need continuation byte
542
- return [Ucharerr, 1] if n < 2
517
+ raise Utf8Error if n < 2 # need continuation byte
543
518
  c1 = s[i+1].ord
544
- return [Ucharerr, 1] if c1 < Utagx || Utag2 <= c1
519
+ raise Utf8Error if c1 < Utagx || Utag2 <= c1
545
520
 
546
521
  # 2-byte, 11-bit sequence?
547
522
  if c0 < Utag3
548
- u = (c0&Umask2)<<6 | (c1&Umaskx)
549
- return [Ucharerr, 1] if u <= Uchar1max
550
- return [u, 2]
523
+ raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
524
+ t.putc(c0)
525
+ t.putc(c1)
526
+ return 2
551
527
  end
552
528
 
553
529
  # need second continuation byte
554
- return [Ucharerr, 1] if n < 3
530
+ raise Utf8Error if n < 3
531
+
555
532
  c2 = s[i+2].ord
556
- return [Ucharerr, 1] if c2 < Utagx || Utag2 <= c2
533
+ raise Utf8Error if c2 < Utagx || Utag2 <= c2
557
534
 
558
535
  # 3-byte, 16-bit sequence?
559
536
  if c0 < Utag4
560
537
  u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
561
- return [Ucharerr, 1] if u <= Uchar2max
562
- return [u, 3]
538
+ raise Utf8Error if u <= Uchar2max
539
+ t.putc(c0)
540
+ t.putc(c1)
541
+ t.putc(c2)
542
+ return 3
563
543
  end
564
544
 
565
545
  # need third continuation byte
566
- return [Ucharerr, 1] if n < 4
546
+ raise Utf8Error if n < 4
567
547
  c3 = s[i+3].ord
568
- return [Ucharerr, 1] if c3 < Utagx || Utag2 <= c3
548
+ raise Utf8Error if c3 < Utagx || Utag2 <= c3
569
549
 
570
550
  # 4-byte, 21-bit sequence?
571
551
  if c0 < Utag5
572
552
  u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
573
- return [Ucharerr, 1] if u <= Uchar3max
574
- return [u, 4]
575
- end
553
+ raise Utf8Error if u <= Uchar3max
554
+ t.putc(c0)
555
+ t.putc(c1)
556
+ t.putc(c2)
557
+ t.putc(c3)
558
+ return 4
559
+ end
560
+
561
+ raise Utf8Error
562
+ rescue Utf8Error
563
+ t.write(Ustrerr)
564
+ return 1
565
+ end
566
+
576
567
 
577
- return [Ucharerr, 1]
568
+ class Utf8Error < ::StandardError
578
569
  end
579
570
 
580
571
 
@@ -595,14 +586,13 @@ module OkJson
595
586
  Uchar2max = (1<<11) - 1
596
587
  Uchar3max = (1<<16) - 1
597
588
  Ucharerr = 0xFFFD # unicode "replacement char"
589
+ Ustrerr = "\xef\xbf\xbd" # unicode "replacement char"
598
590
  Usurrself = 0x10000
599
591
  Usurr1 = 0xd800
600
592
  Usurr2 = 0xdc00
601
593
  Usurr3 = 0xe000
602
- Umax = 0x10ffff
603
594
 
604
595
  Spc = ' '[0]
605
596
  Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
606
- Hex = '0123456789abcdef'
607
597
  end
608
598
  end
@@ -21,11 +21,11 @@ module QC
21
21
  end
22
22
 
23
23
  def delete_all
24
- Queries.delete_all(@name)
24
+ Queries.delete_all(name)
25
25
  end
26
26
 
27
27
  def count
28
- Queries.count(@name)
28
+ Queries.count(name)
29
29
  end
30
30
 
31
31
  end
data/readme.md CHANGED
@@ -135,7 +135,7 @@ end
135
135
  ## Configure
136
136
 
137
137
  All configuration takes place in the form of environment vars.
138
- See [queue_classic.rb](https://github.com/ryandotsmith/queue_classic/blob/master/lib/queue_classic.rb#L29-66)
138
+ See [queue_classic.rb](https://github.com/ryandotsmith/queue_classic/blob/master/lib/queue_classic.rb#L23-62)
139
139
  for a list of options.
140
140
 
141
141
  ## Usage
@@ -262,7 +262,7 @@ require "queue_classic"
262
262
  QC::Worker.new.start
263
263
  ```
264
264
 
265
- #### Sublcass QC::Worker
265
+ #### Subclass QC::Worker
266
266
 
267
267
  Now that we have seen how to run a worker process, let's take a look at how to customize a worker.
268
268
  The class `QC::Worker` will probably suit most of your needs; however, there are some mechanisms
@@ -278,7 +278,7 @@ class MyWorker < QC::Worker
278
278
 
279
279
  # retry the job
280
280
  def handle_failure(job, exception)
281
- @queue.enqueue(job[:method], job[:args])
281
+ @queue.enqueue(job[:method], *job[:args])
282
282
  end
283
283
 
284
284
  # the forked proc needs a new db connection
@@ -444,24 +444,10 @@ database.
444
444
 
445
445
  To achieve this we will create a helper method:
446
446
 
447
- ```ruby
448
-
449
- def qc_txn
450
- begin
451
- QC.database.execute("BEGIN")
452
- yield
453
- QC.database.execute("COMMIT")
454
- rescue Exception
455
- QC.database.execute("ROLLBACK")
456
- raise
457
- end
458
- end
459
- ```
460
-
461
447
  Now in your application code you can do something like:
462
448
 
463
449
  ```ruby
464
- qc_txn do
450
+ QC::Conn.transaction do
465
451
  Account.all.each do |act|
466
452
  QC.enqueue("Emailer.send_notice", act.id)
467
453
  end
@@ -579,3 +565,7 @@ $ createdb queue_classic_test
579
565
  $ export QC_DATABASE_URL="postgres://username:pass@localhost/queue_classic_test"
580
566
  $ rake
581
567
  ```
568
+
569
+ ### License
570
+
571
+ queue_classic is released under the MIT License (http://www.opensource.org/licenses/mit-license.php).
data/test/queue_test.rb CHANGED
@@ -56,4 +56,21 @@ class QueueTest < QCTest
56
56
  assert_equal(0, queue.count)
57
57
  end
58
58
 
59
+ def test_repair_after_error
60
+ queue = QC::Queue.new("queue_classic_jobs", false)
61
+ queue.enqueue("Klass.method")
62
+ assert_equal(1, queue.count)
63
+ connection = QC::Conn.connection
64
+ saved_method = connection.method(:exec)
65
+ def connection.exec(*args)
66
+ raise PGError
67
+ end
68
+ assert_raises(PG::Error) { queue.enqueue("Klass.other_method") }
69
+ assert_equal(1, queue.count)
70
+ queue.enqueue("Klass.other_method")
71
+ assert_equal(2, queue.count)
72
+ rescue PG::Error
73
+ QC::Conn.disconnect
74
+ assert false, "Expected to QC repair after connection error"
75
+ end
59
76
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: queue_classic
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-29 00:00:00.000000000 Z
12
+ date: 2012-07-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.13.2
21
+ version: 0.14.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 0.13.2
29
+ version: 0.14.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: scrolls
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 0.0.8
37
+ version: 0.2.1
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: 0.0.8
45
+ version: 0.2.1
46
46
  description: queue_classic is a queueing library for Ruby apps. (Rails, Sinatra, Etc...)
47
47
  queue_classic features asynchronous job polling, database maintained locks and no
48
48
  ridiculous dependencies. As a matter of fact, queue_classic only requires pg.
@@ -67,7 +67,8 @@ files:
67
67
  - test/queue_test.rb
68
68
  - test/worker_test.rb
69
69
  homepage: http://github.com/ryandotsmith/queue_classic
70
- licenses: []
70
+ licenses:
71
+ - MIT
71
72
  post_install_message:
72
73
  rdoc_options: []
73
74
  require_paths: