jerefrer-resque 1.1.0

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.
Files changed (58) hide show
  1. data/.kick +26 -0
  2. data/HISTORY.md +10 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +703 -0
  5. data/Rakefile +61 -0
  6. data/bin/resque +69 -0
  7. data/bin/resque-web +47 -0
  8. data/config.ru +8 -0
  9. data/deps.rip +5 -0
  10. data/examples/async_helper.rb +31 -0
  11. data/examples/demo/README.markdown +71 -0
  12. data/examples/demo/Rakefile +3 -0
  13. data/examples/demo/app.rb +27 -0
  14. data/examples/demo/config.ru +19 -0
  15. data/examples/demo/job.rb +12 -0
  16. data/examples/god/resque.god +52 -0
  17. data/examples/god/stale.god +26 -0
  18. data/examples/instance.rb +11 -0
  19. data/examples/simple.rb +30 -0
  20. data/init.rb +1 -0
  21. data/lib/resque/errors.rb +7 -0
  22. data/lib/resque/failure/base.rb +58 -0
  23. data/lib/resque/failure/hoptoad.rb +88 -0
  24. data/lib/resque/failure/redis.rb +33 -0
  25. data/lib/resque/failure.rb +63 -0
  26. data/lib/resque/helpers.rb +57 -0
  27. data/lib/resque/job.rb +91 -0
  28. data/lib/resque/server/public/idle.png +0 -0
  29. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  30. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  31. data/lib/resque/server/public/poll.png +0 -0
  32. data/lib/resque/server/public/ranger.js +21 -0
  33. data/lib/resque/server/public/reset.css +48 -0
  34. data/lib/resque/server/public/style.css +75 -0
  35. data/lib/resque/server/public/working.png +0 -0
  36. data/lib/resque/server/views/error.erb +1 -0
  37. data/lib/resque/server/views/failed.erb +35 -0
  38. data/lib/resque/server/views/key.erb +17 -0
  39. data/lib/resque/server/views/layout.erb +41 -0
  40. data/lib/resque/server/views/next_more.erb +10 -0
  41. data/lib/resque/server/views/overview.erb +4 -0
  42. data/lib/resque/server/views/queues.erb +46 -0
  43. data/lib/resque/server/views/stats.erb +62 -0
  44. data/lib/resque/server/views/workers.erb +78 -0
  45. data/lib/resque/server/views/working.erb +67 -0
  46. data/lib/resque/server.rb +174 -0
  47. data/lib/resque/stat.rb +53 -0
  48. data/lib/resque/tasks.rb +24 -0
  49. data/lib/resque/version.rb +3 -0
  50. data/lib/resque/worker.rb +406 -0
  51. data/lib/resque.rb +184 -0
  52. data/tasks/redis.rake +125 -0
  53. data/tasks/resque.rake +2 -0
  54. data/test/redis-test.conf +132 -0
  55. data/test/resque_test.rb +160 -0
  56. data/test/test_helper.rb +90 -0
  57. data/test/worker_test.rb +220 -0
  58. metadata +121 -0
@@ -0,0 +1,406 @@
1
+ module Resque
2
+ # A Resque Worker processes jobs. On platforms that support fork(2),
3
+ # the worker will fork off a child to process each job. This ensures
4
+ # a clean slate when beginning the next job and cuts down on gradual
5
+ # memory growth as well as low level failures.
6
+ #
7
+ # It also ensures workers are always listening to signals from you,
8
+ # their master, and can react accordingly.
9
+ class Worker
10
+ include Resque::Helpers
11
+ extend Resque::Helpers
12
+
13
+ # Whether the worker should log basic info to STDOUT
14
+ attr_accessor :verbose
15
+
16
+ # Whether the worker should log lots of info to STDOUT
17
+ attr_accessor :very_verbose
18
+
19
+ # Boolean indicating whether this worker can or can not fork.
20
+ # Automatically set if a fork(2) fails.
21
+ attr_accessor :cant_fork
22
+
23
+ attr_writer :to_s
24
+
25
+ # Returns an array of all worker objects.
26
+ def self.all
27
+ redis.smembers(:workers).map { |id| find(id) }
28
+ end
29
+
30
+ # Returns an array of all worker objects currently processing
31
+ # jobs.
32
+ def self.working
33
+ names = all
34
+ return [] unless names.any?
35
+ names.map! { |name| "worker:#{name}" }
36
+ redis.mapped_mget(*names).keys.map do |key|
37
+ find key.sub("worker:", '')
38
+ end
39
+ end
40
+
41
+ # Returns a single worker object. Accepts a string id.
42
+ def self.find(worker_id)
43
+ if exists? worker_id
44
+ queues = worker_id.split(':')[-1].split(',')
45
+ worker = new(*queues)
46
+ worker.to_s = worker_id
47
+ worker
48
+ else
49
+ nil
50
+ end
51
+ end
52
+
53
+ # Alias of `find`
54
+ def self.attach(worker_id)
55
+ find(worker_id)
56
+ end
57
+
58
+ # Given a string worker id, return a boolean indicating whether the
59
+ # worker exists
60
+ def self.exists?(worker_id)
61
+ redis.sismember(:workers, worker_id)
62
+ end
63
+
64
+ # Workers should be initialized with an array of string queue
65
+ # names. The order is important: a Worker will check the first
66
+ # queue given for a job. If none is found, it will check the
67
+ # second queue name given. If a job is found, it will be
68
+ # processed. Upon completion, the Worker will again check the
69
+ # first queue given, and so forth. In this way the queue list
70
+ # passed to a Worker on startup defines the priorities of queues.
71
+ #
72
+ # If passed a single "*", this Worker will operate on all queues
73
+ # in alphabetical order. Queues can be dynamically added or
74
+ # removed without needing to restart workers using this method.
75
+ def initialize(*queues)
76
+ @queues = queues
77
+ validate_queues
78
+ end
79
+
80
+ # A worker must be given a queue, otherwise it won't know what to
81
+ # do with itself.
82
+ #
83
+ # You probably never need to call this.
84
+ def validate_queues
85
+ if @queues.nil? || @queues.empty?
86
+ raise NoQueueError.new("Please give each worker at least one queue.")
87
+ end
88
+ end
89
+
90
+ # This is the main workhorse method. Called on a Worker instance,
91
+ # it begins the worker life cycle.
92
+ #
93
+ # The following events occur during a worker's life cycle:
94
+ #
95
+ # 1. startup: Signals are registered, dead workers are pruned,
96
+ # and this worker is registered.
97
+ # 2. work loop: Jobs are pulled from a queue and processed
98
+ # 3. teardown: This worker is unregistered.
99
+ #
100
+ # Can be passed an integered representing the polling
101
+ # frequency. The default is 5 seconds, but for a semi-active site
102
+ # you may want to use a smaller value.
103
+ #
104
+ # Also accepts a block which will be passed the job as soon as it
105
+ # has completed processing. Useful for testing.
106
+ def work(interval = 5, &block)
107
+ $0 = "resque: Starting"
108
+ startup
109
+
110
+ loop do
111
+ break if @shutdown
112
+
113
+ if job = reserve
114
+ log "got: #{job.inspect}"
115
+
116
+ if @child = fork
117
+ procline = "resque: Forked #{@child} at #{Time.now.to_i}"
118
+ $0 = procline
119
+ log! procline
120
+ Process.wait
121
+ else
122
+ procline = "resque: Processing #{job.queue} since #{Time.now.to_i}"
123
+ $0 = procline
124
+ log! procline
125
+ process(job, &block)
126
+ exit! unless @cant_fork
127
+ end
128
+
129
+ @child = nil
130
+ else
131
+ break if interval.to_i == 0
132
+ log! "Sleeping for #{interval.to_i}"
133
+ $0 = "resque: Waiting for #{@queues.join(',')}"
134
+ sleep interval.to_i
135
+ end
136
+ end
137
+
138
+ ensure
139
+ unregister_worker
140
+ end
141
+
142
+ # Processes a single job. If none is given, it will try to produce
143
+ # one.
144
+ def process(job = nil)
145
+ return unless job ||= reserve
146
+
147
+ begin
148
+ working_on job
149
+ job.perform
150
+ rescue Object => e
151
+ log "#{job.inspect} failed: #{e.inspect}"
152
+ job.fail(e)
153
+ failed!
154
+ else
155
+ log "done: #{job.inspect}"
156
+ ensure
157
+ yield job if block_given?
158
+ done_working
159
+ end
160
+ end
161
+
162
+ # Attempts to grab a job off one of the provided queues. Returns
163
+ # nil if no job can be found.
164
+ def reserve
165
+ queues.each do |queue|
166
+ log! "Checking #{queue}"
167
+ if job = Resque::Job.reserve(queue)
168
+ log! "Found job on #{queue}"
169
+ return job
170
+ end
171
+ end
172
+
173
+ nil
174
+ end
175
+
176
+ # Returns a list of queues to use when searching for a job.
177
+ # A splat ("*") means you want every queue (in alpha order) - this
178
+ # can be useful for dynamically adding new queues.
179
+ def queues
180
+ @queues[0] == "*" ? Resque.queues.sort : @queues
181
+ end
182
+
183
+ # Not every platform supports fork. Here we do our magic to
184
+ # determine if yours does.
185
+ def fork
186
+ @cant_fork = true if $TESTING
187
+
188
+ return if @cant_fork
189
+
190
+ begin
191
+ Kernel.fork
192
+ rescue NotImplementedError
193
+ @cant_fork = true
194
+ nil
195
+ end
196
+ end
197
+
198
+ # Runs all the methods needed when a worker begins its lifecycle.
199
+ def startup
200
+ enable_gc_optimizations
201
+ register_signal_handlers
202
+ prune_dead_workers
203
+ register_worker
204
+ end
205
+
206
+ # Enables GC Optimizations if you're running REE.
207
+ # http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
208
+ def enable_gc_optimizations
209
+ if GC.respond_to?(:copy_on_write_friendly=)
210
+ GC.copy_on_write_friendly = true
211
+ end
212
+ end
213
+
214
+ # Registers the various signal handlers a worker responds to.
215
+ #
216
+ # TERM: Shutdown immediately, stop processing jobs.
217
+ # INT: Shutdown immediately, stop processing jobs.
218
+ # QUIT: Shutdown after the current job has finished processing.
219
+ # USR1: Kill the forked child immediately, continue processing jobs.
220
+ def register_signal_handlers
221
+ trap('TERM') { shutdown! }
222
+ trap('INT') { shutdown! }
223
+ unless defined? JRUBY_VERSION
224
+ trap('QUIT') { shutdown }
225
+ trap('USR1') { kill_child }
226
+ end
227
+ log! "Registered signals"
228
+ end
229
+
230
+ # Schedule this worker for shutdown. Will finish processing the
231
+ # current job.
232
+ def shutdown
233
+ log 'Exiting...'
234
+ @shutdown = true
235
+ end
236
+
237
+ # Kill the child and shutdown immediately.
238
+ def shutdown!
239
+ shutdown
240
+ kill_child
241
+ end
242
+
243
+ # Kills the forked child immediately, without remorse. The job it
244
+ # is processing will not be completed.
245
+ def kill_child
246
+ if @child
247
+ log! "Killing child at #{@child}"
248
+ Process.kill("KILL", @child) rescue nil
249
+ end
250
+ end
251
+
252
+ # Looks for any workers which should be running on this server
253
+ # and, if they're not, removes them from Redis.
254
+ #
255
+ # This is a form of garbage collection. If a server is killed by a
256
+ # hard shutdown, power failure, or something else beyond our
257
+ # control, the Resque workers will not die gracefully and therefor
258
+ # will leave stale state information in Redis.
259
+ #
260
+ # By checking the current Redis state against the actual
261
+ # environment, we can determine if Redis is old and clean it up a bit.
262
+ def prune_dead_workers
263
+ Worker.all.each do |worker|
264
+ host, pid, queues = worker.id.split(':')
265
+ next unless host == hostname
266
+ next if worker_pids.include?(pid)
267
+ log! "Pruning dead worker: #{worker}"
268
+ worker.unregister_worker
269
+ end
270
+ end
271
+
272
+ # Registers ourself as a worker. Useful when entering the worker
273
+ # lifecycle on startup.
274
+ def register_worker
275
+ redis.sadd(:workers, self)
276
+ started!
277
+ end
278
+
279
+ # Unregisters ourself as a worker. Useful when shutting down.
280
+ def unregister_worker
281
+ done_working
282
+
283
+ redis.srem(:workers, self)
284
+ redis.del("worker:#{self}:started")
285
+
286
+ Stat.clear("processed:#{self}")
287
+ Stat.clear("failed:#{self}")
288
+ end
289
+
290
+ # Given a job, tells Redis we're working on it. Useful for seeing
291
+ # what workers are doing and when.
292
+ def working_on(job)
293
+ job.worker = self
294
+ data = encode \
295
+ :queue => job.queue,
296
+ :run_at => Time.now.to_s,
297
+ :payload => job.payload
298
+ redis.set("worker:#{self}", data)
299
+ end
300
+
301
+ # Called when we are done working - clears our `working_on` state
302
+ # and tells Redis we processed a job.
303
+ def done_working
304
+ processed!
305
+ redis.del("worker:#{self}")
306
+ end
307
+
308
+ # How many jobs has this worker processed? Returns an int.
309
+ def processed
310
+ Stat["processed:#{self}"]
311
+ end
312
+
313
+ # Tell Redis we've processed a job.
314
+ def processed!
315
+ Stat << "processed"
316
+ Stat << "processed:#{self}"
317
+ end
318
+
319
+ # How many failed jobs has this worker seen? Returns an int.
320
+ def failed
321
+ Stat["failed:#{self}"]
322
+ end
323
+
324
+ # Tells Redis we've failed a job.
325
+ def failed!
326
+ Stat << "failed"
327
+ Stat << "failed:#{self}"
328
+ end
329
+
330
+ # What time did this worker start? Returns an instance of `Time`
331
+ def started
332
+ redis.get "worker:#{self}:started"
333
+ end
334
+
335
+ # Tell Redis we've started
336
+ def started!
337
+ redis.set("worker:#{self}:started", Time.now.to_s)
338
+ end
339
+
340
+ # Returns a hash explaining the Job we're currently processing, if any.
341
+ def job
342
+ decode(redis.get("worker:#{self}")) || {}
343
+ end
344
+ alias_method :processing, :job
345
+
346
+ # Boolean - true if working, false if not
347
+ def working?
348
+ state == :working
349
+ end
350
+
351
+ # Boolean - true if idle, false if not
352
+ def idle?
353
+ state == :idle
354
+ end
355
+
356
+ # Returns a symbol representing the current worker state,
357
+ # which can be either :working or :idle
358
+ def state
359
+ redis.exists("worker:#{self}") ? :working : :idle
360
+ end
361
+
362
+ # Is this worker the same as another worker?
363
+ def ==(other)
364
+ to_s == other.to_s
365
+ end
366
+
367
+ def inspect
368
+ "#<Worker #{to_s}>"
369
+ end
370
+
371
+ # The string representation is the same as the id for this worker
372
+ # instance. Can be used with `Worker.find`.
373
+ def to_s
374
+ @to_s ||= "#{hostname}:#{Process.pid}:#{@queues.join(',')}"
375
+ end
376
+ alias_method :id, :to_s
377
+
378
+ # chomp'd hostname of this machine
379
+ def hostname
380
+ @hostname ||= `hostname`.chomp
381
+ end
382
+
383
+ # Returns an array of string pids of all the other workers on this
384
+ # machine. Useful when pruning dead workers on startup.
385
+ def worker_pids
386
+ `ps -A -o pid,command | grep [r]esque`.split("\n").map do |line|
387
+ line.split(' ')[0]
388
+ end
389
+ end
390
+
391
+ # Log a message to STDOUT if we are verbose or very_verbose.
392
+ def log(message)
393
+ if verbose
394
+ puts "*** #{message}"
395
+ elsif very_verbose
396
+ time = Time.now.strftime('%I:%M:%S %Y-%m-%d')
397
+ puts "** [#{time}] #$$: #{message}"
398
+ end
399
+ end
400
+
401
+ # Logs a very verbose message to STDOUT.
402
+ def log!(message)
403
+ log message if very_verbose
404
+ end
405
+ end
406
+ end
data/lib/resque.rb ADDED
@@ -0,0 +1,184 @@
1
+ require 'redis/namespace'
2
+
3
+ begin
4
+ require 'yajl'
5
+ rescue LoadError
6
+ require 'json'
7
+ end
8
+
9
+ require 'resque/errors'
10
+
11
+ require 'resque/failure'
12
+ require 'resque/failure/base'
13
+
14
+ require 'resque/helpers'
15
+ require 'resque/stat'
16
+ require 'resque/job'
17
+ require 'resque/worker'
18
+
19
+ module Resque
20
+ include Helpers
21
+ extend self
22
+
23
+ # Accepts a 'hostname:port' string or a Redis server.
24
+ def redis=(server)
25
+ case server
26
+ when String
27
+ host, port = server.split(':')
28
+ redis = Redis.new(:host => host, :port => port, :thread_safe => true)
29
+ @redis = Redis::Namespace.new(:resque, :redis => redis)
30
+ when Redis
31
+ @redis = Redis::Namespace.new(:resque, :redis => server)
32
+ else
33
+ raise "I don't know what to do with #{server.inspect}"
34
+ end
35
+ end
36
+
37
+ # Returns the current Redis connection. If none has been created, will
38
+ # create a new one.
39
+ def redis
40
+ return @redis if @redis
41
+ self.redis = 'localhost:6379'
42
+ self.redis
43
+ end
44
+
45
+ def to_s
46
+ "Resque Client connected to #{redis.server}"
47
+ end
48
+
49
+
50
+ #
51
+ # queue manipulation
52
+ #
53
+
54
+ # Pushes a job onto a queue. Queue name should be a string and the
55
+ # item should be any JSON-able Ruby object.
56
+ def push(queue, item)
57
+ watch_queue(queue)
58
+ redis.rpush "queue:#{queue}", encode(item)
59
+ end
60
+
61
+ # Pops a job off a queue. Queue name should be a string.
62
+ #
63
+ # Returns a Ruby object.
64
+ def pop(queue)
65
+ decode redis.lpop("queue:#{queue}")
66
+ end
67
+
68
+ # Returns an int representing the size of a queue.
69
+ # Queue name should be a string.
70
+ def size(queue)
71
+ redis.llen("queue:#{queue}").to_i
72
+ end
73
+
74
+ # Returns an array of items currently queued. Queue name should be
75
+ # a string.
76
+ #
77
+ # start and count should be integer and can be used for pagination.
78
+ # start is the item to begin, count is how many items to return.
79
+ #
80
+ # To get the 3rd page of a 30 item, paginatied list one would use:
81
+ # Resque.peek('my_list', 59, 30)
82
+ def peek(queue, start = 0, count = 1)
83
+ list_range("queue:#{queue}", start, count)
84
+ end
85
+
86
+ # Does the dirty work of fetching a range of items from a Redis list
87
+ # and converting them into Ruby objects.
88
+ def list_range(key, start = 0, count = 1)
89
+ if count == 1
90
+ decode redis.lindex(key, start)
91
+ else
92
+ Array(redis.lrange(key, start, start+count-1)).map do |item|
93
+ decode item
94
+ end
95
+ end
96
+ end
97
+
98
+ # Returns an array of all known Resque queues as strings.
99
+ def queues
100
+ redis.smembers(:queues)
101
+ end
102
+
103
+ # Used internally to keep track of which queues we've created.
104
+ # Don't call this directly.
105
+ def watch_queue(queue)
106
+ @watched_queues ||= {}
107
+ return if @watched_queues[queue]
108
+ redis.sadd(:queues, queue.to_s)
109
+ end
110
+
111
+
112
+ #
113
+ # job shortcuts
114
+ #
115
+
116
+ # This method can be used to conveniently add a job to a queue.
117
+ # It assumes the class you're passing it is a real Ruby class (not
118
+ # a string or reference) which either:
119
+ #
120
+ # a) has a @queue ivar set
121
+ # b) responds to `queue`
122
+ #
123
+ # If either of those conditions are met, it will use the value obtained
124
+ # from performing one of the above operations to determine the queue.
125
+ #
126
+ # If no queue can be inferred this method will return a non-true value.
127
+ #
128
+ # This method is considered part of the `stable` API.
129
+ def enqueue(klass, *args)
130
+ queue = klass.instance_variable_get(:@queue)
131
+ queue ||= klass.queue if klass.respond_to?(:queue)
132
+ Job.create(queue, klass, *args)
133
+ end
134
+
135
+ # This method will return a `Resque::Job` object or a non-true value
136
+ # depending on whether a job can be obtained. You should pass it the
137
+ # precise name of a queue: case matters.
138
+ #
139
+ # This method is considered part of the `stable` API.
140
+ def reserve(queue)
141
+ Job.reserve(queue)
142
+ end
143
+
144
+
145
+ #
146
+ # worker shortcuts
147
+ #
148
+
149
+ # A shortcut to Worker.all
150
+ def workers
151
+ Worker.all
152
+ end
153
+
154
+ # A shortcut to Worker.working
155
+ def working
156
+ Worker.working
157
+ end
158
+
159
+
160
+ #
161
+ # stats
162
+ #
163
+
164
+ # Returns a hash, similar to redis-rb's #info, of interesting stats.
165
+ def info
166
+ return {
167
+ :pending => queues.inject(0) { |m,k| m + size(k) },
168
+ :processed => Stat[:processed],
169
+ :queues => queues.size,
170
+ :workers => workers.size.to_i,
171
+ :working => working.size,
172
+ :failed => Stat[:failed],
173
+ :servers => [redis.server]
174
+ }
175
+ end
176
+
177
+ # Returns an array of all known Resque keys in Redis. Redis' KEYS operation
178
+ # is O(N) for the keyspace, so be careful - this can be slow for big databases.
179
+ def keys
180
+ redis.keys("*").map do |key|
181
+ key.sub('resque:', '')
182
+ end
183
+ end
184
+ end
data/tasks/redis.rake ADDED
@@ -0,0 +1,125 @@
1
+ # Inspired by rabbitmq.rake the Redbox project at http://github.com/rick/redbox/tree/master
2
+ require 'fileutils'
3
+ require 'open-uri'
4
+
5
+ class RedisRunner
6
+
7
+ def self.redisdir
8
+ "/tmp/redis/"
9
+ end
10
+
11
+ def self.redisconfdir
12
+ '/etc/redis.conf'
13
+ end
14
+
15
+ def self.dtach_socket
16
+ '/tmp/redis.dtach'
17
+ end
18
+
19
+ # Just check for existance of dtach socket
20
+ def self.running?
21
+ File.exists? dtach_socket
22
+ end
23
+
24
+ def self.start
25
+ puts 'Detach with Ctrl+\ Re-attach with rake redis:attach'
26
+ sleep 1
27
+ exec "dtach -A #{dtach_socket} redis-server #{redisconfdir}"
28
+ end
29
+
30
+ def self.attach
31
+ exec "dtach -a #{dtach_socket}"
32
+ end
33
+
34
+ def self.stop
35
+ sh 'echo "SHUTDOWN" | nc localhost 6379'
36
+ end
37
+
38
+ end
39
+
40
+ namespace :redis do
41
+
42
+ desc 'About redis'
43
+ task :about do
44
+ puts "\nSee http://code.google.com/p/redis/ for information about redis.\n\n"
45
+ end
46
+
47
+ desc 'Start redis'
48
+ task :start do
49
+ RedisRunner.start
50
+ end
51
+
52
+ desc 'Stop redis'
53
+ task :stop do
54
+ RedisRunner.stop
55
+ end
56
+
57
+ desc 'Restart redis'
58
+ task :restart do
59
+ RedisRunner.stop
60
+ RedisRunner.start
61
+ end
62
+
63
+ desc 'Attach to redis dtach socket'
64
+ task :attach do
65
+ RedisRunner.attach
66
+ end
67
+
68
+ desc 'Install the lastest verison of Redis from Github (requires git, duh)'
69
+ task :install => [:about, :download, :make] do
70
+ %w(redis-benchmark redis-cli redis-server).each do |bin|
71
+ sh "sudo cp /tmp/redis/#{bin} /usr/bin/"
72
+ end
73
+
74
+ puts "Installed redis-benchmark, redis-cli and redis-server to /usr/bin/"
75
+
76
+ unless File.exists?('/etc/redis.conf')
77
+ sh 'sudo cp /tmp/redis/redis.conf /etc/'
78
+ puts "Installed redis.conf to /etc/ \n You should look at this file!"
79
+ end
80
+ end
81
+
82
+ task :make do
83
+ sh "cd #{RedisRunner.redisdir} && make clean"
84
+ sh "cd #{RedisRunner.redisdir} && make"
85
+ end
86
+
87
+ desc "Download package"
88
+ task :download do
89
+ sh 'rm -rf /tmp/redis/' if File.exists?("#{RedisRunner.redisdir}/.svn")
90
+ sh 'git clone git://github.com/antirez/redis.git /tmp/redis' unless File.exists?(RedisRunner.redisdir)
91
+ sh "cd #{RedisRunner.redisdir} && git pull" if File.exists?("#{RedisRunner.redisdir}/.git")
92
+ end
93
+
94
+ end
95
+
96
+ namespace :dtach do
97
+
98
+ desc 'About dtach'
99
+ task :about do
100
+ puts "\nSee http://dtach.sourceforge.net/ for information about dtach.\n\n"
101
+ end
102
+
103
+ desc 'Install dtach 0.8 from source'
104
+ task :install => [:about] do
105
+
106
+ Dir.chdir('/tmp/')
107
+ unless File.exists?('/tmp/dtach-0.8.tar.gz')
108
+ require 'net/http'
109
+
110
+ url = 'http://downloads.sourceforge.net/project/dtach/dtach/0.8/dtach-0.8.tar.gz'
111
+ open('/tmp/dtach-0.8.tar.gz', 'wb') do |file| file.write(open(url).read) end
112
+ end
113
+
114
+ unless File.directory?('/tmp/dtach-0.8')
115
+ system('tar xzf dtach-0.8.tar.gz')
116
+ end
117
+
118
+ Dir.chdir('/tmp/dtach-0.8/')
119
+ sh 'cd /tmp/dtach-0.8/ && ./configure && make'
120
+ sh 'sudo cp /tmp/dtach-0.8/dtach /usr/bin/'
121
+
122
+ puts 'Dtach successfully installed to /usr/bin.'
123
+ end
124
+ end
125
+
data/tasks/resque.rake ADDED
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'resque/tasks'