quebert 2.0.3 → 2.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 063bc22b230d2549c427562deb220554b1b6b931
4
- data.tar.gz: 0f4db324c52f319abe1229112f0ea4219172f47d
3
+ metadata.gz: 13a07e8336155e0c777ed1295201b2d7d23e9bee
4
+ data.tar.gz: 6e3929be1a44ee006809f3b24a1ef605aed7605d
5
5
  SHA512:
6
- metadata.gz: b001d8710708db59714b9722c75cf0919ab1520a369ff2c3c246645450a02ba0e47235be186f805984d46693c7766b654bc579e73f8bca176c7176e37935e2ad
7
- data.tar.gz: 76331a2a7b8600945285f562802b80f68954909d0b0a62f8b5ae857c8c5d782d5c364e74438f38ef542a91588ea2f2ba8dc7d46fa6abd107c59c07c46f4b5d28
6
+ metadata.gz: f56a3426063aa8353e6e52f8c61bb7a76e73bdbced8df6a919872979fb33102ce0e9cbd536046ccc30fa21d1e57b38b994d275abcfd560366d6b451cf7507257
7
+ data.tar.gz: 2f464763bb13a6eefd45ebc46f8a2dbc3a326dff7e5968960319f37a52bcf10c3d5d5c3abf64a55d6ca009a30b12d705b65367f47d0e0ab9572969aeb5651427
data/.gitignore CHANGED
@@ -1,6 +1,9 @@
1
1
  ## MAC OS
2
2
  .DS_Store
3
3
 
4
+ ## RUBY
5
+ .bundle
6
+
4
7
  ## TEXTMATE
5
8
  *.tmproj
6
9
  tmtags
@@ -19,4 +22,4 @@ rdoc
19
22
  pkg
20
23
  Gemfile.lock
21
24
 
22
- ## PROJECT::SPECIFIC
25
+ ## PROJECT::SPECIFIC
data/.rbenv-version CHANGED
@@ -1 +1 @@
1
- 1.9.3-p484
1
+ 2.1.2
data/README.md CHANGED
@@ -18,6 +18,7 @@ Quebert is a serious project. Its used in a production environment at Poll Every
18
18
  * Rails/ActiveRecord integration similar to async_observer
19
19
  * Pluggable exception handling (for Hoptoad integration)
20
20
  * Run workers with pid, log, and config files. These do not daemonize (do it yourself punk!)
21
+ * Provide custom hooks to be called before, after & around jobs are run
21
22
 
22
23
  Some features that are currently missing that I will soon add include:
23
24
 
@@ -53,6 +54,25 @@ Then perform the jobs!
53
54
  Quebert.backend.reserve.perform # => 6
54
55
  Quebert.backend.reserve.perform # => 15
55
56
 
57
+ ## Before/After/Around Hooks
58
+
59
+ Quebert has support for providing custom hooks to be called before, after & around your jobs are being run.
60
+ A common example is making sure that any active ActiveRecord database connections are put back on the connection pool after a job is done:
61
+
62
+ Quebert.config.after_job do
63
+ ActiveRecord::Base.clear_active_connections!
64
+ end
65
+
66
+ Quebert.config.before_job do |job|
67
+ # all hooks take an optional job argument
68
+ # in case you want to do something with that job
69
+ end
70
+
71
+ Quebert.config.around_job do |job|
72
+ # this hook gets called twice
73
+ # once before & once after a job is performed
74
+ end
75
+
56
76
  ## Async Sender
57
77
 
58
78
  Take any ol' class and include the Quebert::AsyncSender.
@@ -29,13 +29,60 @@ module Quebert
29
29
  end
30
30
  self
31
31
  end
32
-
32
+
33
33
  def worker
34
34
  @worker ||= Struct.new(:exception_handler).new
35
35
  end
36
-
36
+
37
37
  def self.from_hash(hash)
38
38
  new.from_hash(hash) # Config this puppy up from a config hash
39
39
  end
40
+
41
+ def before_job(job = nil, &block)
42
+ if job
43
+ before_hooks.each do |h|
44
+ h.call(job)
45
+ end
46
+ else
47
+ before_hooks << block if block
48
+ end
49
+ self
50
+ end
51
+
52
+ def after_job(job = nil, &block)
53
+ if job
54
+ after_hooks.each do |h|
55
+ h.call(job)
56
+ end
57
+ else
58
+ after_hooks << block if block
59
+ end
60
+ self
61
+ end
62
+
63
+ def around_job(job = nil, &block)
64
+ if job
65
+ around_hooks.each do |h|
66
+ h.call(job)
67
+ end
68
+ else
69
+ around_hooks << block if block
70
+ end
71
+ self
72
+ end
73
+
74
+ private
75
+
76
+ def before_hooks
77
+ @before_hooks ||= []
78
+ end
79
+
80
+ def after_hooks
81
+ @after_hooks ||= []
82
+ end
83
+
84
+ def around_hooks
85
+ @around_hooks ||= []
86
+ end
40
87
  end
41
88
  end
@@ -14,86 +14,79 @@ module Quebert
14
14
 
15
15
  def initialize(beanstalk_job, queue)
16
16
  @beanstalk_job, @queue = beanstalk_job, queue
17
-
18
- begin
19
- @job = Job.from_json(beanstalk_job.body)
20
- rescue Job::Delete
21
- beanstalk_job.delete
22
- log "Deleted on initialization", :error
23
- rescue Job::Release
24
- beanstalk_job.release @job.priority, @job.delay
25
- log "Released on initialization with priority: #{@job.priority} and delay: #{@job.delay}", :error
26
- rescue Job::Bury
27
- beanstalk_job.bury
28
- log "Buried on initialization", :error
29
- rescue Exception => e
30
- beanstalk_job.bury
31
- log "Exception caught on initialization. #{e.inspect}", :error
32
- raise e
33
- end
17
+ @job = Job.from_json(beanstalk_job.body)
18
+ rescue Job::Delete
19
+ beanstalk_job.delete
20
+ log "Deleted on initialization", :error
21
+ rescue Job::Release
22
+ beanstalk_job.release @job.priority, @job.delay
23
+ log "Released on initialization with priority: #{@job.priority} and delay: #{@job.delay}", :error
24
+ rescue Job::Bury
25
+ beanstalk_job.bury
26
+ log "Buried on initialization", :error
27
+ rescue Exception => e
28
+ beanstalk_job.bury
29
+ log "Exception caught on initialization. #{e.inspect}", :error
30
+ raise
34
31
  end
35
-
36
- def perform
37
- begin
38
- log "Performing with args #{job.args.inspect}"
39
- log "Beanstalk Job Stats: #{beanstalk_job.stats.inspect}"
40
32
 
41
- result = false
42
- time = Benchmark.realtime do
43
- result = job.perform!
44
- beanstalk_job.delete
45
- end
33
+ def perform
34
+ log "Performing with args #{job.args.inspect}"
35
+ log "Beanstalk Job Stats: #{beanstalk_job.stats.inspect}"
46
36
 
47
- log "Completed in #{(time*1000*1000).to_i/1000.to_f} ms\n"
48
- result
49
- rescue Job::Delete
50
- log "Deleting job", :error
37
+ result = false
38
+ time = Benchmark.realtime do
39
+ result = job.perform!
51
40
  beanstalk_job.delete
52
- log "Job deleted", :error
53
- rescue Job::Release
54
- log "Releasing with priority: #{@job.priority} and delay: #{@job.delay}", :error
55
- beanstalk_job.release :pri => @job.priority, :delay => @job.delay
56
- log "Job released", :error
57
- rescue Job::Bury
58
- log "Burrying job", :error
59
- beanstalk_job.bury
60
- log "Job burried", :error
61
- rescue Job::Timeout => e
62
- log "Job timed out!", :error
63
- retry_with_delay
64
- raise e
65
- rescue Job::Retry => e
66
- # The difference between the Retry and Timeout class is that
67
- # Retry does not log an exception where as Timeout does
68
- log "Manually retrying with delay"
69
- retry_with_delay
70
- rescue Exception => e
71
- log "Exception caught on perform. Burying job. #{e.inspect} #{e.backtrace.join("\n")}", :error
72
- beanstalk_job.bury
73
- log "Job buried", :error
74
- raise e
75
41
  end
42
+
43
+ log "Completed in #{(time*1000*1000).to_i/1000.to_f} ms\n"
44
+ result
45
+ rescue Job::Delete
46
+ log "Deleting job", :error
47
+ beanstalk_job.delete
48
+ log "Job deleted", :error
49
+ rescue Job::Release
50
+ log "Releasing with priority: #{@job.priority} and delay: #{@job.delay}", :error
51
+ beanstalk_job.release :pri => @job.priority, :delay => @job.delay
52
+ log "Job released", :error
53
+ rescue Job::Bury
54
+ log "Burrying job", :error
55
+ beanstalk_job.bury
56
+ log "Job burried", :error
57
+ rescue Job::Timeout => e
58
+ log "Job timed out. Retrying with delay. #{e.inspect} #{e.backtrace.join("\n")}", :error
59
+ retry_with_delay
60
+ raise
61
+ rescue Job::Retry
62
+ # The difference between the Retry and Timeout class is that
63
+ # Retry does not log an exception where as Timeout does
64
+ log "Manually retrying with delay"
65
+ retry_with_delay
66
+ rescue Exception => e
67
+ log "Exception caught on perform. Burying job. #{e.inspect} #{e.backtrace.join("\n")}", :error
68
+ beanstalk_job.bury
69
+ log "Job buried", :error
70
+ raise
76
71
  end
77
72
 
78
73
  protected
79
74
  def retry_with_delay
80
- begin
81
- delay = TIMEOUT_RETRY_DELAY_SEED + TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"].to_i
75
+ delay = TIMEOUT_RETRY_DELAY_SEED + TIMEOUT_RETRY_GROWTH_RATE**beanstalk_job.stats["releases"].to_i
82
76
 
83
- if delay > MAX_TIMEOUT_RETRY_DELAY
84
- log "Max retry delay exceeded. Burrying job"
85
- beanstalk_job.bury
86
- log "Job burried"
87
- else
88
- log "TTR exceeded. Releasing with priority: #{@job.priority} and delay: #{delay}"
89
- beanstalk_job.release :pri => @job.priority, :delay => delay
90
- log "Job released"
91
- end
92
- rescue ::Beaneater::NotFoundError
93
- log "Job ran longer than allowed. Beanstalk already deleted it!!!!", :error
94
- # Sometimes the timer doesn't behave correctly and this job actually runs longer than
95
- # allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
77
+ if delay > MAX_TIMEOUT_RETRY_DELAY
78
+ log "Max retry delay exceeded. Burrying job"
79
+ beanstalk_job.bury
80
+ log "Job burried"
81
+ else
82
+ log "TTR exceeded. Releasing with priority: #{@job.priority} and delay: #{delay}"
83
+ beanstalk_job.release :pri => @job.priority, :delay => delay
84
+ log "Job released"
96
85
  end
86
+ rescue ::Beaneater::NotFoundError
87
+ log "Job ran longer than allowed. Beanstalk already deleted it!!!!", :error
88
+ # Sometimes the timer doesn't behave correctly and this job actually runs longer than
89
+ # allowed. At that point the beanstalk job no longer exists anymore. Lets let it go and don't blow up.
97
90
  end
98
91
 
99
92
  def log(message, level=:info)
data/lib/quebert/job.rb CHANGED
@@ -51,10 +51,18 @@ module Quebert
51
51
 
52
52
  # Runs the perform method that somebody else should be implementing
53
53
  def perform!
54
+ Quebert.config.before_job(self)
55
+ Quebert.config.around_job(self)
56
+
54
57
  # Honor the timeout and kill the job in ruby-space. Beanstalk
55
58
  # should be cleaning up this job and returning it to the queue
56
59
  # as well.
57
- ::Timeout.timeout(@ttr, Job::Timeout){ perform(*args) }
60
+ val = ::Timeout.timeout(@ttr, Job::Timeout){ perform(*args) }
61
+
62
+ Quebert.config.around_job(self)
63
+ Quebert.config.after_job(self)
64
+
65
+ val
58
66
  end
59
67
 
60
68
  # Accepts arguments that override the job options and enqueu this stuff.
@@ -1,3 +1,3 @@
1
1
  module Quebert
2
- VERSION = "2.0.3"
2
+ VERSION = "2.0.4"
3
3
  end
data/spec/job_spec.rb CHANGED
@@ -153,4 +153,35 @@ describe Quebert::Job do
153
153
  }.should raise_exception(Quebert::Job::Timeout)
154
154
  end
155
155
  end
156
+
157
+ context "before, after & around hooks" do
158
+ it "should call each type of hook as expected" do
159
+ before_jobs = []
160
+ after_jobs = []
161
+ around_jobs = []
162
+
163
+ jobs = (1..10).map do |i|
164
+ Adder.new(i, i)
165
+ end
166
+
167
+ Quebert.config.before_job do |job|
168
+ before_jobs << job
169
+ end
170
+
171
+ Quebert.config.after_job do |job|
172
+ after_jobs << job
173
+ end
174
+
175
+ Quebert.config.around_job do |job|
176
+ around_jobs << job
177
+ end
178
+
179
+ jobs.each(&:perform!)
180
+
181
+ before_jobs.should eql jobs
182
+ after_jobs.should eql jobs
183
+ # around_job hooks are called twice per job (before & after its performed)
184
+ around_jobs.should eql jobs.zip(jobs).flatten
185
+ end
186
+ end
156
187
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quebert
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brad Gessler
@@ -10,34 +10,34 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-07-18 00:00:00.000000000 Z
13
+ date: 2014-10-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - '>='
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - '>='
26
+ - - ">="
27
27
  - !ruby/object:Gem::Version
28
28
  version: '0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: beaneater
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - '>='
33
+ - - ">="
34
34
  - !ruby/object:Gem::Version
35
35
  version: '0'
36
36
  type: :runtime
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - '>='
40
+ - - ">="
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0'
43
43
  - !ruby/object:Gem::Dependency
@@ -62,11 +62,11 @@ executables:
62
62
  extensions: []
63
63
  extra_rdoc_files: []
64
64
  files:
65
- - .document
66
- - .gitignore
67
- - .rbenv-version
68
- - .rspec
69
- - .travis.yml
65
+ - ".document"
66
+ - ".gitignore"
67
+ - ".rbenv-version"
68
+ - ".rspec"
69
+ - ".travis.yml"
70
70
  - Gemfile
71
71
  - Guardfile
72
72
  - LICENSE.md
@@ -121,17 +121,17 @@ require_paths:
121
121
  - lib
122
122
  required_ruby_version: !ruby/object:Gem::Requirement
123
123
  requirements:
124
- - - '>='
124
+ - - ">="
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  required_rubygems_version: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - '>='
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
132
  requirements: []
133
133
  rubyforge_project: quebert
134
- rubygems_version: 2.2.0
134
+ rubygems_version: 2.2.2
135
135
  signing_key:
136
136
  specification_version: 4
137
137
  summary: A worker queue framework built around beanstalkd