ayl-beanstalk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +125 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +20 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/ayl_beanstalk +281 -0
- data/bin/ayl_worker +27 -0
- data/bin/ayl_worker_control +14 -0
- data/lib/ayl-beanstalk.rb +5 -0
- data/lib/ayl-beanstalk/command_line.rb +69 -0
- data/lib/ayl-beanstalk/engine.rb +50 -0
- data/lib/ayl-beanstalk/pool.rb +15 -0
- data/lib/ayl-beanstalk/worker.rb +46 -0
- data/spec/ayl_worker_spec.rb +103 -0
- data/spec/command_line_spec.rb +44 -0
- data/spec/engine_spec.rb +54 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/config/environment.rb +9 -0
- data/spec/support/exit_code_matchers.rb +24 -0
- data/spec/worker_spec.rb +130 -0
- metadata +164 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
gem 'ayl', ">= 0.1.0"
|
6
|
+
gem 'beanstalk-client', ">= 1.1.0"
|
7
|
+
gem 'daemons', ">= 1.1.0"
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
group :development do
|
12
|
+
gem "rspec", ">= 2.3.0"
|
13
|
+
gem "bundler", ">= 1.0.0"
|
14
|
+
gem "jeweler", ">= 1.6.4"
|
15
|
+
gem "rcov", ">= 0"
|
16
|
+
gem 'pry'
|
17
|
+
end
|
18
|
+
|
19
|
+
group :test do
|
20
|
+
gem 'rails', ">= 3.0.0"
|
21
|
+
gem 'pry'
|
22
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.1.2)
|
5
|
+
actionpack (= 3.1.2)
|
6
|
+
mail (~> 2.3.0)
|
7
|
+
actionpack (3.1.2)
|
8
|
+
activemodel (= 3.1.2)
|
9
|
+
activesupport (= 3.1.2)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
i18n (~> 0.6)
|
13
|
+
rack (~> 1.3.5)
|
14
|
+
rack-cache (~> 1.1)
|
15
|
+
rack-mount (~> 0.8.2)
|
16
|
+
rack-test (~> 0.6.1)
|
17
|
+
sprockets (~> 2.1.0)
|
18
|
+
activemodel (3.1.2)
|
19
|
+
activesupport (= 3.1.2)
|
20
|
+
builder (~> 3.0.0)
|
21
|
+
i18n (~> 0.6)
|
22
|
+
activerecord (3.1.2)
|
23
|
+
activemodel (= 3.1.2)
|
24
|
+
activesupport (= 3.1.2)
|
25
|
+
arel (~> 2.2.1)
|
26
|
+
tzinfo (~> 0.3.29)
|
27
|
+
activeresource (3.1.2)
|
28
|
+
activemodel (= 3.1.2)
|
29
|
+
activesupport (= 3.1.2)
|
30
|
+
activesupport (3.1.2)
|
31
|
+
multi_json (~> 1.0)
|
32
|
+
arel (2.2.1)
|
33
|
+
ayl (0.1.0)
|
34
|
+
beanstalk-client (1.1.1)
|
35
|
+
builder (3.0.0)
|
36
|
+
coderay (0.9.8)
|
37
|
+
daemons (1.1.4)
|
38
|
+
diff-lcs (1.1.3)
|
39
|
+
erubis (2.7.0)
|
40
|
+
git (1.2.5)
|
41
|
+
hike (1.2.1)
|
42
|
+
i18n (0.6.0)
|
43
|
+
jeweler (1.6.4)
|
44
|
+
bundler (~> 1.0)
|
45
|
+
git (>= 1.2.5)
|
46
|
+
rake
|
47
|
+
json (1.6.1)
|
48
|
+
mail (2.3.0)
|
49
|
+
i18n (>= 0.4.0)
|
50
|
+
mime-types (~> 1.16)
|
51
|
+
treetop (~> 1.4.8)
|
52
|
+
method_source (0.6.7)
|
53
|
+
ruby_parser (>= 2.3.1)
|
54
|
+
mime-types (1.17.2)
|
55
|
+
multi_json (1.0.3)
|
56
|
+
polyglot (0.3.3)
|
57
|
+
pry (0.9.7.4)
|
58
|
+
coderay (~> 0.9.8)
|
59
|
+
method_source (~> 0.6.7)
|
60
|
+
ruby_parser (>= 2.3.1)
|
61
|
+
slop (~> 2.1.0)
|
62
|
+
rack (1.3.5)
|
63
|
+
rack-cache (1.1)
|
64
|
+
rack (>= 0.4)
|
65
|
+
rack-mount (0.8.3)
|
66
|
+
rack (>= 1.0.0)
|
67
|
+
rack-ssl (1.3.2)
|
68
|
+
rack
|
69
|
+
rack-test (0.6.1)
|
70
|
+
rack (>= 1.0)
|
71
|
+
rails (3.1.2)
|
72
|
+
actionmailer (= 3.1.2)
|
73
|
+
actionpack (= 3.1.2)
|
74
|
+
activerecord (= 3.1.2)
|
75
|
+
activeresource (= 3.1.2)
|
76
|
+
activesupport (= 3.1.2)
|
77
|
+
bundler (~> 1.0)
|
78
|
+
railties (= 3.1.2)
|
79
|
+
railties (3.1.2)
|
80
|
+
actionpack (= 3.1.2)
|
81
|
+
activesupport (= 3.1.2)
|
82
|
+
rack-ssl (~> 1.3.2)
|
83
|
+
rake (>= 0.8.7)
|
84
|
+
rdoc (~> 3.4)
|
85
|
+
thor (~> 0.14.6)
|
86
|
+
rake (0.9.2.2)
|
87
|
+
rcov (0.9.11)
|
88
|
+
rdoc (3.11)
|
89
|
+
json (~> 1.4)
|
90
|
+
rspec (2.7.0)
|
91
|
+
rspec-core (~> 2.7.0)
|
92
|
+
rspec-expectations (~> 2.7.0)
|
93
|
+
rspec-mocks (~> 2.7.0)
|
94
|
+
rspec-core (2.7.1)
|
95
|
+
rspec-expectations (2.7.0)
|
96
|
+
diff-lcs (~> 1.1.2)
|
97
|
+
rspec-mocks (2.7.0)
|
98
|
+
ruby_parser (2.3.1)
|
99
|
+
sexp_processor (~> 3.0)
|
100
|
+
sexp_processor (3.0.8)
|
101
|
+
slop (2.1.0)
|
102
|
+
sprockets (2.1.1)
|
103
|
+
hike (~> 1.2)
|
104
|
+
rack (~> 1.0)
|
105
|
+
tilt (~> 1.1, != 1.3.0)
|
106
|
+
thor (0.14.6)
|
107
|
+
tilt (1.3.3)
|
108
|
+
treetop (1.4.10)
|
109
|
+
polyglot
|
110
|
+
polyglot (>= 0.3.1)
|
111
|
+
tzinfo (0.3.31)
|
112
|
+
|
113
|
+
PLATFORMS
|
114
|
+
ruby
|
115
|
+
|
116
|
+
DEPENDENCIES
|
117
|
+
ayl (>= 0.1.0)
|
118
|
+
beanstalk-client (>= 1.1.0)
|
119
|
+
bundler (>= 1.0.0)
|
120
|
+
daemons (>= 1.1.0)
|
121
|
+
jeweler (>= 1.6.4)
|
122
|
+
pry
|
123
|
+
rails (>= 3.0.0)
|
124
|
+
rcov
|
125
|
+
rspec (>= 2.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 j0hnds@gmail.com
|
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,20 @@
|
|
1
|
+
= ayl-beanstalk
|
2
|
+
|
3
|
+
This gem provides a reference implementation of a beanstalk Engine for the
|
4
|
+
{ayl}[https://github.com/j0hnds/ayl] gem.
|
5
|
+
|
6
|
+
== Contributing to ayl-beanstalk
|
7
|
+
|
8
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
9
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
10
|
+
* Fork the project
|
11
|
+
* Start a feature/bugfix branch
|
12
|
+
* Commit and push until you are happy with your contribution
|
13
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
14
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2011 j0hnds@gmail.com. See LICENSE.txt for
|
19
|
+
further details.
|
20
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
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 = "ayl-beanstalk"
|
18
|
+
gem.homepage = "http://github.com/j0hnds/ayl-beanstalk"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Ayl extension to provide beanstalk support.}
|
21
|
+
gem.description = %Q{Ayl extension to provide beanstalk support.}
|
22
|
+
gem.email = "j0hnds@gmail.com"
|
23
|
+
gem.authors = ["j0hnds@gmail.com"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "ayl-beanstalk #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/ayl_beanstalk
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Use this script to look at the current beanstalk information. For help
|
4
|
+
# running this script, enter 'beastalk_info.rb --help' on the command line.
|
5
|
+
#
|
6
|
+
require 'date'
|
7
|
+
require 'optparse'
|
8
|
+
require 'beanstalk-client'
|
9
|
+
|
10
|
+
SLEEP_COMMAND = {
|
11
|
+
:type => :rails,
|
12
|
+
:code => 'Kernel.sleep(20)' # This message will cause the worker to sleep for 20 seconds
|
13
|
+
}
|
14
|
+
|
15
|
+
#
|
16
|
+
# This class provides the commands for the script. Each public instance method
|
17
|
+
# on the class is exposed as a command by the script. So, for example, if the
|
18
|
+
# user specifies 'beanstalk_info.rb statistics' on the command-line, the script
|
19
|
+
# will invoke the BeanstalkCommander#statistics method.
|
20
|
+
#
|
21
|
+
# When the user asks the script for help ('beanstalk_info.rb --help'), the help
|
22
|
+
# implementation will ask for the public instance methods for the BeanstalkCommander
|
23
|
+
# class so that we can present the user with the valid commands
|
24
|
+
#
|
25
|
+
class BeanstalkCommander
|
26
|
+
|
27
|
+
def initialize(host, port, tube)
|
28
|
+
@host = host
|
29
|
+
@port = port
|
30
|
+
@tube = tube
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Lists the statistics for the specified job number
|
35
|
+
#
|
36
|
+
def job_statistics(job_number)
|
37
|
+
raise "Must specify a job number to get the statistics for" unless job_number
|
38
|
+
format_statistics(pool.open_connections.first.job_stats(job_number))
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Lists the statistics for the current tube on beanstalk
|
43
|
+
#
|
44
|
+
def tube_statistics(*args)
|
45
|
+
puts "Statistics for Tube: #{@tube}"
|
46
|
+
format_statistics(pool.stats_tube(@tube))
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Lists the overall statistics for beanstalk
|
51
|
+
#
|
52
|
+
def statistics(*args)
|
53
|
+
puts "Overall Statistics"
|
54
|
+
puts "------------------"
|
55
|
+
format_statistics(pool.stats)
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Lists the tubes active on beanstalk
|
60
|
+
#
|
61
|
+
def list_tubes(*args)
|
62
|
+
tubes = pool.list_tubes
|
63
|
+
puts "Tubes in use"
|
64
|
+
puts "------------"
|
65
|
+
tubes.each_pair do | host, tubes |
|
66
|
+
puts host
|
67
|
+
tubes.each { | tube | puts " - #{tube}" }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Displays the job information for the specified job id without
|
73
|
+
# reserving the job or removing it from the queue.
|
74
|
+
#
|
75
|
+
def peek_job(job_number)
|
76
|
+
raise "Must specify a job number to peek" unless job_number
|
77
|
+
job = pool.peek_job(job_number)
|
78
|
+
format_job(job)
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Deletes the job with the specified job id.
|
83
|
+
#
|
84
|
+
def delete(job_number)
|
85
|
+
raise "Must specify a job number to delete" unless job_number
|
86
|
+
pool.open_connections.first.delete(job_number)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Displays the most current job on the ready queue
|
91
|
+
#
|
92
|
+
def peek_ready(*args)
|
93
|
+
pool.use(@tube)
|
94
|
+
job = pool.peek_ready
|
95
|
+
format_job(job)
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Kick any buried/delayed jobs in te queue
|
100
|
+
#
|
101
|
+
def kick(*args)
|
102
|
+
pool.use(@tube)
|
103
|
+
pool.open_connections.first.kick 1
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Displays the most current job on the delayed queue
|
108
|
+
#
|
109
|
+
def peek_delayed(*args)
|
110
|
+
pool.use(@tube)
|
111
|
+
job = pool.peek_delayed
|
112
|
+
format_job(job)
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Displays the most current job on the buried queue
|
117
|
+
#
|
118
|
+
def peek_buried(*args)
|
119
|
+
pool.use(@tube)
|
120
|
+
job = pool.peek_buried
|
121
|
+
format_job(job)
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Puts a generic job on the queue (job is specified on the command line)
|
126
|
+
#
|
127
|
+
def put_job(job_body)
|
128
|
+
raise "Must specify a job body to put" unless job_body
|
129
|
+
puts "Putting (#{job_body}) on tube: #{@tube}"
|
130
|
+
pool.use(@tube)
|
131
|
+
pool.put(job_body)
|
132
|
+
puts "Job was put"
|
133
|
+
end
|
134
|
+
|
135
|
+
def put_ayl_job(job_body)
|
136
|
+
raise "Must specify a job body to put" unless job_body
|
137
|
+
puts "Putting (#{job_body}) on tube: #{@tube}"
|
138
|
+
pool.put({:type => :ayl, :code => job_body}.to_yaml)
|
139
|
+
puts "Job was put"
|
140
|
+
end
|
141
|
+
|
142
|
+
def put_sleep_job(*args)
|
143
|
+
puts "Putting job on tube '#{@tube}' that will sleep for 20 seconds."
|
144
|
+
pool.use(@tube)
|
145
|
+
pool.put(SLEEP_COMMAND.to_yaml)
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Reserves the current job on the queue, displays it, and removes it from the
|
150
|
+
# queue. The operation is performed against the current queue.
|
151
|
+
#
|
152
|
+
def eat_job(*args)
|
153
|
+
# Reserve the next job on the queue
|
154
|
+
pool.watch(@tube)
|
155
|
+
job = pool.reserve(10)
|
156
|
+
raise "No job to reserve" unless job
|
157
|
+
puts "Reserved job"
|
158
|
+
format_job(job)
|
159
|
+
job.delete
|
160
|
+
puts "Deleted job"
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# General purpose tube cleaning mechanism. This allows you to move all the jobs
|
165
|
+
# from one tube to another within beanstalk.
|
166
|
+
# The tube specified on the command line (-t tube), will be used as the 'from-tube' and
|
167
|
+
# the argument to the move_jobs command is the 'to-tube'. When done, you will be a
|
168
|
+
# a time-out error (because the 'reserve' command timed out without finding a job).
|
169
|
+
#
|
170
|
+
# It is fast and awesome.
|
171
|
+
#
|
172
|
+
def move_jobs(to_tube)
|
173
|
+
pool.watch(@tube)
|
174
|
+
pool.use(to_tube)
|
175
|
+
while true
|
176
|
+
job = pool.reserve(0)
|
177
|
+
if job
|
178
|
+
pool.put(job.body)
|
179
|
+
job.delete
|
180
|
+
else
|
181
|
+
break
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
#
|
189
|
+
# Returns the connected beanstalk pool
|
190
|
+
#
|
191
|
+
def pool
|
192
|
+
@pool ||= Beanstalk::Pool.new(["#{@host}:#{@port}"])
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Formats the specified job. Can handle the job returned as a hash and as
|
197
|
+
# a job object.
|
198
|
+
#
|
199
|
+
def format_job(job)
|
200
|
+
if job.nil?
|
201
|
+
puts "No job..."
|
202
|
+
elsif job.is_a?(Hash)
|
203
|
+
job.each_pair do | host, job |
|
204
|
+
format_job(job)
|
205
|
+
end
|
206
|
+
else
|
207
|
+
puts "Job: #{job.id}: #{job.body}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Standardized statistics formatting for both tube and general statistics.
|
213
|
+
#
|
214
|
+
def format_statistics(statistics)
|
215
|
+
statistics.keys.sort.each do | key |
|
216
|
+
puts format("%-25s => %13s", key, statistics[key]) if statistics[key].is_a?(Fixnum)
|
217
|
+
puts format("%-25s => %-13s", key, statistics[key]) if statistics[key].is_a?(String)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
BEANSTALK_HOST_DEFAULT = 'localhost'
|
223
|
+
BEANSTALK_PORT_DEFAULT = 11300
|
224
|
+
DEFAULT_APP_TUBE_NAME = "default"
|
225
|
+
|
226
|
+
options = {}
|
227
|
+
|
228
|
+
# Set up the options supported by the script
|
229
|
+
optparse = OptionParser.new do | opts |
|
230
|
+
|
231
|
+
# Set a banner, displayed at the top of the help screen
|
232
|
+
opts.banner = "Usage: #{$0} [options] command"
|
233
|
+
|
234
|
+
options[:host] = BEANSTALK_HOST_DEFAULT
|
235
|
+
opts.on '-h', '--host HOST', "Specify the host running beanstalk. Default (#{BEANSTALK_HOST_DEFAULT})" do | host |
|
236
|
+
options[:host] = host
|
237
|
+
end
|
238
|
+
|
239
|
+
options[:port] = BEANSTALK_PORT_DEFAULT
|
240
|
+
opts.on '-p', '--port PORT', "Specify the beanstalk port number. Default (#{BEANSTALK_PORT_DEFAULT})" do | port |
|
241
|
+
options[:port] = port.to_i
|
242
|
+
end
|
243
|
+
|
244
|
+
options[:tube] = DEFAULT_APP_TUBE_NAME
|
245
|
+
opts.on '-t', '--tube TUBE', "Specify the tube name. Default (#{DEFAULT_APP_TUBE_NAME})" do | tube |
|
246
|
+
options[:tube] = tube
|
247
|
+
end
|
248
|
+
|
249
|
+
opts.on '-h', '--help', 'Display the help message' do
|
250
|
+
puts opts
|
251
|
+
puts
|
252
|
+
puts "Valid Commands: "
|
253
|
+
# The false parameter in the instance_methods call is used to eliminate methods from the
|
254
|
+
# super-classes.
|
255
|
+
BeanstalkCommander.instance_methods(false).each do | command |
|
256
|
+
puts " - #{command}"
|
257
|
+
end
|
258
|
+
puts
|
259
|
+
exit
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
# Parse out the command line
|
265
|
+
optparse.parse!
|
266
|
+
|
267
|
+
begin
|
268
|
+
beanstalker = BeanstalkCommander.new(options[:host],
|
269
|
+
options[:port],
|
270
|
+
options[:tube])
|
271
|
+
|
272
|
+
raise "Must specify a command to execute." if ARGV.empty?
|
273
|
+
command = ARGV.first
|
274
|
+
raise "Invalid command: #{command}" unless beanstalker.respond_to?(command)
|
275
|
+
|
276
|
+
arguments = ARGV[1..-1]
|
277
|
+
beanstalker.send(command, *arguments)
|
278
|
+
rescue Exception => ex
|
279
|
+
puts "Error: #{ex.message}"
|
280
|
+
exit
|
281
|
+
end
|
data/bin/ayl_worker
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'ayl-beanstalk/command_line'
|
4
|
+
|
5
|
+
BASE_EXIT_CODE = 64
|
6
|
+
NO_APP_PATH_SPECIFIED = BASE_EXIT_CODE + 0
|
7
|
+
|
8
|
+
options = Ayl::CommandLine.parse!
|
9
|
+
|
10
|
+
if options[:app_path].nil?
|
11
|
+
puts "Must specify an application path"
|
12
|
+
exit(NO_APP_PATH_SPECIFIED)
|
13
|
+
end
|
14
|
+
|
15
|
+
if options[:rails_app]
|
16
|
+
require 'rails'
|
17
|
+
ENV['RAILS_ENV'] = options[:env]
|
18
|
+
end
|
19
|
+
|
20
|
+
require File.join(options[:app_path], options[:app_require])
|
21
|
+
|
22
|
+
Ayl::MessageOptions.default_queue_name = options[:tube]
|
23
|
+
|
24
|
+
worker = Ayl::Engine.get_active_engine.worker
|
25
|
+
worker.eval_binding = binding
|
26
|
+
|
27
|
+
worker.process_messages
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'daemons'
|
3
|
+
require 'ayl-beanstalk/command_line'
|
4
|
+
|
5
|
+
sdir = File.dirname(File.absolute_path($0))
|
6
|
+
sname = File.basename($0).gsub('_control', '')
|
7
|
+
spath = File.join(sdir, sname)
|
8
|
+
|
9
|
+
options = Ayl::CommandLine.parse! Ayl::CommandLine.grab_app_arguments
|
10
|
+
|
11
|
+
raise "Must specify the path to the PID file" if options[:pid_path].nil?
|
12
|
+
raise "PID path does not exist" if !File.exists?(options[:pid_path])
|
13
|
+
|
14
|
+
Daemons.run(spath, :log_dir => '/tmp', :log_output => true, :dir_mode => :normal, :dir => options[:pid_path])
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Ayl
|
4
|
+
|
5
|
+
module CommandLine
|
6
|
+
|
7
|
+
BEANSTALK_TUBE_DEFAULT = 'default'
|
8
|
+
APP_REQUIRE_DEFAULT = 'config/environment'
|
9
|
+
RAILS_ENVIRONMENT_DEFAULT = 'production'
|
10
|
+
|
11
|
+
MARKER = '--'
|
12
|
+
|
13
|
+
def self.grab_app_arguments(argv=ARGV)
|
14
|
+
marker_index = argv.index(MARKER)
|
15
|
+
raise "No argument marker found!" if marker_index.nil?
|
16
|
+
argv[marker_index + 1..-1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse!(argv=ARGV)
|
20
|
+
{}.tap do | options |
|
21
|
+
|
22
|
+
optparse = OptionParser.new do | opts |
|
23
|
+
|
24
|
+
# Set a banner, displayed at the top of the help screen.
|
25
|
+
opts.banner = "Usage: #{$0} [options]"
|
26
|
+
|
27
|
+
options[:tube] = BEANSTALK_TUBE_DEFAULT
|
28
|
+
opts.on '-t', '--tube TUBE', "Specify the beanstalk tube to listen to. Default (#{BEANSTALK_TUBE_DEFAULT})." do |tube|
|
29
|
+
options[:tube] = tube
|
30
|
+
end
|
31
|
+
|
32
|
+
options[:env] = RAILS_ENVIRONMENT_DEFAULT
|
33
|
+
opts.on '-e', '--environment ENVIRONMENT', "Specify the Rails environment to use" do |environment|
|
34
|
+
options[:env] = environment
|
35
|
+
end
|
36
|
+
|
37
|
+
options[:app_path] = nil
|
38
|
+
opts.on '-a', '--app-path APP_PATH', "Specify the path to the rails app" do |app_path|
|
39
|
+
options[:app_path] = app_path
|
40
|
+
end
|
41
|
+
|
42
|
+
options[:rails_app] = false
|
43
|
+
opts.on '-r', '--rails', "Indicate that we are starting a rails application" do
|
44
|
+
options[:rails_app] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
options[:app_require] = APP_REQUIRE_DEFAULT
|
48
|
+
opts.on '-c', '--require APP_REQUIRE', "The file to require when the worker starts up" do | app_require |
|
49
|
+
options[:app_require] = app_require
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.on '-p', '--pid-path PID_PATH', "The path to the pid file" do | pid_path |
|
53
|
+
options[:pid_path] = pid_path
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on '-h', '--help', 'Display the help message' do
|
57
|
+
puts opts
|
58
|
+
exit(0)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
optparse.parse!(argv)
|
63
|
+
end # End of .tap
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
module Beanstalk
|
4
|
+
|
5
|
+
class Engine
|
6
|
+
include Ayl::Logging
|
7
|
+
include Ayl::Beanstalk::Pool
|
8
|
+
|
9
|
+
def initialize(host='localhost', port=11300)
|
10
|
+
logger.info "#{self.class.name}.initialize(#{host.inspect}, #{port})"
|
11
|
+
@host = host
|
12
|
+
@port = port
|
13
|
+
end
|
14
|
+
|
15
|
+
def asynchronous?() true end
|
16
|
+
|
17
|
+
def is_connected?
|
18
|
+
connected = true
|
19
|
+
begin
|
20
|
+
pool
|
21
|
+
rescue ::Beanstalk::NotConnected => ex
|
22
|
+
logger.error "#{self.class.name} not connected error: #{ex}"
|
23
|
+
connected = false
|
24
|
+
end
|
25
|
+
connected
|
26
|
+
end
|
27
|
+
|
28
|
+
def submit(message)
|
29
|
+
log_call(:submit) do
|
30
|
+
begin
|
31
|
+
pool.use(message.options.queue_name)
|
32
|
+
code = message.to_rrepr
|
33
|
+
logger.info "#{self.class.name} submitting '#{code}' to tube '#{message.options.queue_name}'"
|
34
|
+
pool.yput(message.to_hash, message.options.priority, message.options.delay, message.options.time_to_run)
|
35
|
+
rescue Exception => ex
|
36
|
+
logger.error "Error submitting message to beanstalk: #{ex}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def worker
|
42
|
+
Ayl::Beanstalk::Worker.new(@host, @port)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Ayl
|
2
|
+
|
3
|
+
module Beanstalk
|
4
|
+
|
5
|
+
class Worker < Ayl::Worker
|
6
|
+
include Ayl::Logging
|
7
|
+
include Ayl::Beanstalk::Pool
|
8
|
+
|
9
|
+
def initialize(host='localhost', port=11300)
|
10
|
+
logger.info "#{self.class.name}.initialize(#{host.inspect}, #{port})"
|
11
|
+
@host = host
|
12
|
+
@port = port
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_messages
|
16
|
+
logger.info "#{self.class.name} entering process_messages loop"
|
17
|
+
# trap('TERM') { puts "## Got the term signal"; @stop = true }
|
18
|
+
# trap('INT') { puts "## Got the int signal"; @stop = true }
|
19
|
+
# Set the queue that we will be watching
|
20
|
+
pool.watch(Ayl::MessageOptions.default_queue_name)
|
21
|
+
while true
|
22
|
+
break if @stop
|
23
|
+
job = pool.reserve
|
24
|
+
break if job.nil?
|
25
|
+
begin
|
26
|
+
process_message(Ayl::Message.from_hash(job.ybody))
|
27
|
+
job.delete
|
28
|
+
rescue Ayl::UnrecoverableMessageException => ex
|
29
|
+
logger.error "#{self.class.name} Unrecoverable exception in process_messages: #{ex}"
|
30
|
+
job.delete
|
31
|
+
rescue Exception => ex
|
32
|
+
logger.error "#{self.class.name} Exception in process_messages: #{ex}\n#{ex.backtrace.join("\n")}"
|
33
|
+
if job.age > 60
|
34
|
+
job.delete
|
35
|
+
else
|
36
|
+
job.decay
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'optparse'
|
3
|
+
require 'ayl-beanstalk/command_line'
|
4
|
+
|
5
|
+
describe "ayl_worker script" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
ARGV.clear
|
9
|
+
@ayl_script = File.join(File.dirname(File.expand_path(__FILE__)), '../bin/ayl_worker')
|
10
|
+
end
|
11
|
+
|
12
|
+
context "handling command line" do
|
13
|
+
|
14
|
+
it "requires that an application path be specified" do
|
15
|
+
Object.any_instance.stub(:puts)
|
16
|
+
lambda { load(@ayl_script, true) }.should exit_with_code(64)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should exit with a status code of 0 if help is invoked" do
|
20
|
+
ARGV << '--help'
|
21
|
+
Ayl::CommandLine.stub(:puts)
|
22
|
+
lambda { load(@ayl_script, true) }.should exit_with_code(0)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should exit when an invalid argument is specified" do
|
26
|
+
ARGV << '--not-real'
|
27
|
+
|
28
|
+
lambda { load(@ayl_script, true) }.should raise_error(OptionParser::InvalidOption)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set the correct tube name when specified on the command line" do
|
32
|
+
ARGV.concat [ "-a", "support", "-t", "the-tube" ]
|
33
|
+
|
34
|
+
Ayl::MessageOptions.should_receive(:default_queue_name=).with('the-tube')
|
35
|
+
|
36
|
+
mock_worker = mock("Worker")
|
37
|
+
mock_worker.should_receive(:process_messages)
|
38
|
+
mock_worker.should_receive(:eval_binding=)
|
39
|
+
|
40
|
+
mock_active_engine = mock("ActiveEngine")
|
41
|
+
mock_active_engine.should_receive(:worker).and_return(mock_worker)
|
42
|
+
|
43
|
+
Ayl::Engine.should_receive(:get_active_engine).and_return(mock_active_engine)
|
44
|
+
|
45
|
+
load(@ayl_script, true)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should set the default tube name when not specified on the command line" do
|
49
|
+
ARGV.concat [ "-a", "support" ]
|
50
|
+
|
51
|
+
Ayl::MessageOptions.should_receive(:default_queue_name=).with('default')
|
52
|
+
|
53
|
+
mock_worker = mock("Worker")
|
54
|
+
mock_worker.should_receive(:process_messages)
|
55
|
+
mock_worker.should_receive(:eval_binding=)
|
56
|
+
|
57
|
+
mock_active_engine = mock("ActiveEngine")
|
58
|
+
mock_active_engine.should_receive(:worker).and_return(mock_worker)
|
59
|
+
|
60
|
+
Ayl::Engine.should_receive(:get_active_engine).and_return(mock_active_engine)
|
61
|
+
|
62
|
+
load(@ayl_script, true)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should default to a rails production environment if not specified" do
|
66
|
+
ARGV.concat [ "-a", "support", "-r" ]
|
67
|
+
|
68
|
+
Ayl::MessageOptions.should_receive(:default_queue_name=).with('default')
|
69
|
+
|
70
|
+
mock_worker = mock("Worker")
|
71
|
+
mock_worker.should_receive(:process_messages)
|
72
|
+
mock_worker.should_receive(:eval_binding=)
|
73
|
+
|
74
|
+
mock_active_engine = mock("ActiveEngine")
|
75
|
+
mock_active_engine.should_receive(:worker).and_return(mock_worker)
|
76
|
+
|
77
|
+
Ayl::Engine.should_receive(:get_active_engine).and_return(mock_active_engine)
|
78
|
+
ENV.should_receive(:[]=).with('RAILS_ENV', 'production')
|
79
|
+
|
80
|
+
load(@ayl_script, true)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should use the specified rails environment" do
|
84
|
+
ARGV.concat [ "-a", "support", "-r", "-e", "development" ]
|
85
|
+
|
86
|
+
Ayl::MessageOptions.should_receive(:default_queue_name=).with('default')
|
87
|
+
|
88
|
+
mock_worker = mock("Worker")
|
89
|
+
mock_worker.should_receive(:process_messages)
|
90
|
+
mock_worker.should_receive(:eval_binding=)
|
91
|
+
|
92
|
+
mock_active_engine = mock("ActiveEngine")
|
93
|
+
mock_active_engine.should_receive(:worker).and_return(mock_worker)
|
94
|
+
|
95
|
+
Ayl::Engine.should_receive(:get_active_engine).and_return(mock_active_engine)
|
96
|
+
ENV.should_receive(:[]=).with('RAILS_ENV', 'development')
|
97
|
+
|
98
|
+
load(@ayl_script, true)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ayl-beanstalk/command_line'
|
3
|
+
|
4
|
+
describe Ayl::CommandLine do
|
5
|
+
|
6
|
+
context "when extracting the appropriate arguments from the command line" do
|
7
|
+
|
8
|
+
it "grab only the arguments following the application marker" do
|
9
|
+
raw_argv = %w{ start -- -a path-to-app -r -e development }
|
10
|
+
|
11
|
+
app_arguments = %w{ -a path-to-app -r -e development }
|
12
|
+
|
13
|
+
Ayl::CommandLine.grab_app_arguments(raw_argv).should == app_arguments
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raise an exception if no application marker exists in the command line arguments" do
|
17
|
+
raw_argv = %w{ start -a path-to-app -r -e development }
|
18
|
+
lambda { Ayl::CommandLine.grab_app_arguments(raw_argv) }.should raise_exception
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when parsing the command line" do
|
24
|
+
|
25
|
+
it "should extract the correct options from the command line when the short options are used" do
|
26
|
+
argv = %w{ -t tube_name -e development -a app_path -r -c config/environment -p pid_path }
|
27
|
+
parsed_options = Ayl::CommandLine.parse!(argv)
|
28
|
+
parsed_options.should == { :tube => 'tube_name', :env => 'development', :app_path => 'app_path', :rails_app => true, :app_require => 'config/environment', :pid_path => 'pid_path' }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should extract the correct options from the command line when the long options are used" do
|
32
|
+
argv = %w{ --tube tube_name --environment development --app-path app_path --rails --require config/environment --pid-path pid_path }
|
33
|
+
parsed_options = Ayl::CommandLine.parse!(argv)
|
34
|
+
parsed_options.should == { :tube => 'tube_name', :env => 'development', :app_path => 'app_path', :rails_app => true, :app_require => 'config/environment', :pid_path => 'pid_path' }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise an exception if invalid arguments are provided" do
|
38
|
+
argv = %w{ --tuber tube_name --environmen development --apppath app_path --rail --require config/environment --pidpath pid_path }
|
39
|
+
lambda { Ayl::CommandLine.parse!(argv) }.should raise_exception
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/spec/engine_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'beanstalk-client'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/errors'
|
5
|
+
|
6
|
+
describe Ayl::Beanstalk::Engine do
|
7
|
+
|
8
|
+
context "Standard API" do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Kernel.stub(:puts)
|
12
|
+
@engine = Ayl::Beanstalk::Engine.new
|
13
|
+
@engine.stub_chain(:logger, :info)
|
14
|
+
@engine.stub_chain(:logger, :error)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should respond true to the asynchronous? message" do
|
18
|
+
@engine.asynchronous?.should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return true if it has a valid connection to beanstalk" do
|
22
|
+
mock_pool = mock("Beanstalk::Pool")
|
23
|
+
|
24
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
25
|
+
|
26
|
+
@engine.is_connected?.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return false if it does not have a valid connection to beanstalk" do
|
30
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_raise(::Beanstalk::NotConnected)
|
31
|
+
|
32
|
+
@engine.is_connected?.should be_false
|
33
|
+
end
|
34
|
+
|
35
|
+
context "Message Submission" do
|
36
|
+
|
37
|
+
before(:each) do
|
38
|
+
@msg = Ayl::Message.new(23, :to_s, Ayl::MessageOptions.new, 2)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should submit the specified message to beanstalk" do
|
42
|
+
mock_pool = mock("Beanstalk::Pool")
|
43
|
+
mock_pool.should_receive(:use).with("default")
|
44
|
+
mock_pool.should_receive(:yput).with( { :type => :ayl, :code => "23.to_s(2)" }, 512, 0, 120)
|
45
|
+
|
46
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
47
|
+
|
48
|
+
@engine.submit(@msg)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'ayl-beanstalk'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ExitCodeMatchers
|
2
|
+
|
3
|
+
RSpec::Matchers.define :exit_with_code do | code |
|
4
|
+
actual = nil
|
5
|
+
match do |block|
|
6
|
+
begin
|
7
|
+
block.call
|
8
|
+
rescue SystemExit => ex
|
9
|
+
actual = ex.status
|
10
|
+
end
|
11
|
+
actual and actual == code
|
12
|
+
end
|
13
|
+
|
14
|
+
failure_message_for_should do |block|
|
15
|
+
"expected block to call exit(#{code}) but exit" + (actual.nil? ? " not called" : "(#{actual}) was called")
|
16
|
+
end
|
17
|
+
failure_message_for_should_not do |block|
|
18
|
+
"expected block not to call exit(#{code})"
|
19
|
+
end
|
20
|
+
description do
|
21
|
+
"expect block to call exit(#{code})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/worker_spec.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'beanstalk-client'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_record/errors'
|
5
|
+
|
6
|
+
describe Ayl::Beanstalk::Worker do
|
7
|
+
|
8
|
+
context "Message Processing" do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Kernel.stub(:puts)
|
12
|
+
@worker = Ayl::Beanstalk::Worker.new
|
13
|
+
@worker.stub_chain(:logger, :info)
|
14
|
+
@worker.stub_chain(:logger, :error)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should wait for a message to be received from beanstalk and process it" do
|
18
|
+
mock_job = mock("Beanstalk::Job")
|
19
|
+
mock_job.should_receive(:delete)
|
20
|
+
mock_job.should_receive(:ybody).and_return({ :type => :ayl, :code => "23.to_s(2)" })
|
21
|
+
|
22
|
+
mock_pool = mock("Beanstalk::Pool")
|
23
|
+
mock_pool.should_receive(:watch).with("default")
|
24
|
+
# Returns nil on the second call.
|
25
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
26
|
+
|
27
|
+
mock_message = mock("Ayl::Message")
|
28
|
+
mock_message.should_receive(:evaluate)
|
29
|
+
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "23.to_s(2)" }).and_return(mock_message)
|
30
|
+
|
31
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
32
|
+
|
33
|
+
@worker.process_messages
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should raise an UnrecoverableMessageException when the message body is not a valid hash" do
|
38
|
+
mock_job = mock("Beanstalk::Job")
|
39
|
+
mock_job.should_receive(:delete)
|
40
|
+
mock_job.should_receive(:ybody).and_return("a string")
|
41
|
+
mock_job.should_not_receive(:age)
|
42
|
+
|
43
|
+
mock_pool = mock("Beanstalk::Pool")
|
44
|
+
mock_pool.should_receive(:watch).with("default")
|
45
|
+
# Returns nil on the second call.
|
46
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
47
|
+
|
48
|
+
Ayl::Message.stub(:from_hash).with("a string").and_raise(Ayl::UnrecoverableMessageException)
|
49
|
+
|
50
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
51
|
+
|
52
|
+
@worker.process_messages
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should raise an UnrecoverableJobException when the message body is not a valid job type" do
|
56
|
+
mock_job = mock("Beanstalk::Job")
|
57
|
+
mock_job.should_receive(:delete)
|
58
|
+
mock_job.should_receive(:ybody).and_return({ :type => :junk, :code => "Dog" })
|
59
|
+
mock_job.should_not_receive(:age)
|
60
|
+
|
61
|
+
mock_pool = mock("Beanstalk::Pool")
|
62
|
+
mock_pool.should_receive(:watch).with("default")
|
63
|
+
# Returns nil on the second call.
|
64
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
65
|
+
|
66
|
+
Ayl::Message.stub(:from_hash).with({ :type => :junk, :code => "Dog" }).and_raise(Ayl::UnrecoverableMessageException)
|
67
|
+
|
68
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
69
|
+
|
70
|
+
@worker.process_messages
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should raise an UnrecoverableJobException when there is no code in the message body" do
|
74
|
+
mock_job = mock("Beanstalk::Job")
|
75
|
+
mock_job.should_receive(:delete)
|
76
|
+
mock_job.should_receive(:ybody).and_return({ :type => :ayl })
|
77
|
+
mock_job.should_not_receive(:age)
|
78
|
+
|
79
|
+
mock_pool = mock("Beanstalk::Pool")
|
80
|
+
mock_pool.should_receive(:watch).with("default")
|
81
|
+
# Returns nil on the second call.
|
82
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
83
|
+
|
84
|
+
Ayl::Message.stub(:from_hash).with({ :type => :ayl }).and_raise(Ayl::UnrecoverableMessageException)
|
85
|
+
|
86
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
87
|
+
|
88
|
+
@worker.process_messages
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should decay a job that receives an active-record exception on receipt of message that is less than 60 seconds old" do
|
92
|
+
mock_job = mock("Beanstalk::Job")
|
93
|
+
mock_job.should_receive(:decay)
|
94
|
+
mock_job.should_receive(:ybody).and_return({ :type => :ayl, :code => "Dog" })
|
95
|
+
mock_job.should_receive(:age).and_return(10)
|
96
|
+
|
97
|
+
mock_pool = mock("Beanstalk::Pool")
|
98
|
+
mock_pool.should_receive(:watch).with("default")
|
99
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
100
|
+
|
101
|
+
mock_message = mock("Ayl::Message")
|
102
|
+
mock_message.should_receive(:evaluate).and_raise(ActiveRecord::RecordNotFound)
|
103
|
+
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "Dog" }).and_return(mock_message)
|
104
|
+
|
105
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
106
|
+
|
107
|
+
@worker.process_messages
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should delete a job that receives an active-record exception on receipt of message that is more than 60 seconds old" do
|
111
|
+
mock_job = mock("Beanstalk::Job")
|
112
|
+
mock_job.should_receive(:delete)
|
113
|
+
mock_job.should_receive(:ybody).and_return({ :type => :ayl, :code => "Dog" })
|
114
|
+
mock_job.should_receive(:age).and_return(65)
|
115
|
+
|
116
|
+
mock_pool = mock("Beanstalk::Pool")
|
117
|
+
mock_pool.should_receive(:watch).with("default")
|
118
|
+
mock_pool.should_receive(:reserve).and_return(mock_job, nil)
|
119
|
+
|
120
|
+
mock_message = mock("Ayl::Message")
|
121
|
+
mock_message.should_receive(:evaluate).and_raise(ActiveRecord::RecordNotFound)
|
122
|
+
Ayl::Message.stub(:from_hash).with({ :type => :ayl, :code => "Dog" }).and_return(mock_message)
|
123
|
+
|
124
|
+
::Beanstalk::Pool.should_receive(:new).with([ "localhost:11300" ]).and_return(mock_pool)
|
125
|
+
|
126
|
+
@worker.process_messages
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ayl-beanstalk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- j0hnds@gmail.com
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-20 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ayl
|
16
|
+
requirement: &87968020 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.1.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *87968020
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: beanstalk-client
|
27
|
+
requirement: &87967750 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *87967750
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: daemons
|
38
|
+
requirement: &87967500 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.1.0
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *87967500
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &87967220 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.3.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *87967220
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: bundler
|
60
|
+
requirement: &87966960 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.0.0
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *87966960
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: jeweler
|
71
|
+
requirement: &87966690 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.6.4
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *87966690
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rcov
|
82
|
+
requirement: &87966350 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *87966350
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: pry
|
93
|
+
requirement: &87966010 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *87966010
|
102
|
+
description: Ayl extension to provide beanstalk support.
|
103
|
+
email: j0hnds@gmail.com
|
104
|
+
executables:
|
105
|
+
- ayl_beanstalk
|
106
|
+
- ayl_worker
|
107
|
+
- ayl_worker_control
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files:
|
110
|
+
- LICENSE.txt
|
111
|
+
- README.rdoc
|
112
|
+
files:
|
113
|
+
- .document
|
114
|
+
- .rspec
|
115
|
+
- Gemfile
|
116
|
+
- Gemfile.lock
|
117
|
+
- LICENSE.txt
|
118
|
+
- README.rdoc
|
119
|
+
- Rakefile
|
120
|
+
- VERSION
|
121
|
+
- bin/ayl_beanstalk
|
122
|
+
- bin/ayl_worker
|
123
|
+
- bin/ayl_worker_control
|
124
|
+
- lib/ayl-beanstalk.rb
|
125
|
+
- lib/ayl-beanstalk/command_line.rb
|
126
|
+
- lib/ayl-beanstalk/engine.rb
|
127
|
+
- lib/ayl-beanstalk/pool.rb
|
128
|
+
- lib/ayl-beanstalk/worker.rb
|
129
|
+
- spec/ayl_worker_spec.rb
|
130
|
+
- spec/command_line_spec.rb
|
131
|
+
- spec/engine_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/support/config/environment.rb
|
134
|
+
- spec/support/exit_code_matchers.rb
|
135
|
+
- spec/worker_spec.rb
|
136
|
+
homepage: http://github.com/j0hnds/ayl-beanstalk
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
none: false
|
145
|
+
requirements:
|
146
|
+
- - ! '>='
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
segments:
|
150
|
+
- 0
|
151
|
+
hash: 349899887
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 1.8.10
|
161
|
+
signing_key:
|
162
|
+
specification_version: 3
|
163
|
+
summary: Ayl extension to provide beanstalk support.
|
164
|
+
test_files: []
|