Permalink
Browse files

Merge pull request #117 from yaauie/schedule-hooks

add support for before_schedule and after_schedule hooks
  • Loading branch information...
2 parents 581edf1 + 44fe11d commit 8d0176f3bb4103a2c050bb610e8e682006a3ee79 @bvandenbos bvandenbos committed Nov 2, 2011
Showing with 76 additions and 1 deletion.
  1. +6 −0 README.markdown
  2. +18 −1 lib/resque_scheduler.rb
  3. +52 −0 test/scheduler_hooks_test.rb
View
@@ -128,6 +128,12 @@ down when a particular job is supposed to be queue, they will simply "catch up"
once they are started again. Jobs are guaranteed to run (provided they make it
into the delayed queue) after their given queue_at time has passed.
+Similar to `before_enqueue` and `after_enqueue` hooks provided in Resque
+(>= 1.19.1), your jobs can specify one or more `before_schedule` and
+`after_schedule` hooks, to be run before or after scheduling. If *any* of your
+`before_schedule` hooks returns `false`, the job will *not* be scheduled and
+your `after_schedule` hooks will *not* be run.
+
One other thing to note is that insertion into the delayed queue is O(log(n))
since the jobs are stored in a redis sorted set (zset). I can't imagine this
being an issue for someone since redis is stupidly fast even at log(n), but full
View
@@ -102,14 +102,23 @@ def remove_schedule(name)
# sit in the schedule list.
def enqueue_at(timestamp, klass, *args)
validate_job!(klass)
- delayed_push(timestamp, job_to_hash(klass, args))
+ enqueue_at_with_queue( queue_from_class(klass), timestamp, klass, *args)
end
# Identical to +enqueue_at+, except you can also specify
# a queue in which the job will be placed after the
# timestamp has passed.
def enqueue_at_with_queue(queue, timestamp, klass, *args)
+ before_hooks = before_schedule_hooks(klass).collect do |hook|
+ klass.send(hook,*args)
+ end
+ return false if before_hooks.any? { |result| result == false }
+
delayed_push(timestamp, job_to_hash_with_queue(queue, klass, args))
+
+ after_schedule_hooks(klass).collect do |hook|
+ klass.send(hook,*args)
+ end
end
# Identical to enqueue_at but takes number_of_seconds_from_now
@@ -240,6 +249,14 @@ def validate_job!(klass)
end
end
+ def before_schedule_hooks(klass)
+ klass.methods.grep(/^before_schedule/).sort
+ end
+
+ def after_schedule_hooks(klass)
+ klass.methods.grep(/^after_schedule/).sort
+ end
+
end
Resque.extend ResqueScheduler
@@ -0,0 +1,52 @@
+require File.dirname(__FILE__) + '/test_helper'
+
+context "scheduling jobs with hooks" do
+ class JobThatCannotBeScheduledWithoutArguments < Resque::Job
+ @queue = :job_that_cannot_be_scheduled_without_arguments
+ def self.perform(*x);end
+ def self.before_schedule_return_nil_if_arguments_not_supplied(*args)
+ counters[:before_schedule] += 1
+ return false if args.empty?
+ end
+
+ def self.after_schedule_do_something(*args)
+ counters[:after_schedule] += 1
+ end
+
+ class << self
+ def counters
+ @counters ||= Hash.new{|h,k| h[k]=0}
+ end
+ def clean
+ counters.clear
+ self
+ end
+ end
+ end
+
+ setup do
+ Resque::Scheduler.dynamic = false
+ Resque.redis.del(:schedules)
+ Resque.redis.del(:schedules_changed)
+ Resque::Scheduler.mute = true
+ Resque::Scheduler.clear_schedule!
+ Resque::Scheduler.send(:class_variable_set, :@@scheduled_jobs, {})
+ end
+
+ test "before_schedule hook that does not return false should not block" do
+ enqueue_time = Time.now + 12
+ Resque.enqueue_at(enqueue_time, JobThatCannotBeScheduledWithoutArguments.clean, :foo)
+ assert_equal(1, Resque.delayed_timestamp_size(enqueue_time.to_i), "delayed queue should have one entry now")
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:before_schedule], 'before_schedule was not run')
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:after_schedule], 'after_schedule was not run')
+ end
+
+ test "before_schedule hook that returns false should block" do
+ enqueue_time = Time.now + 60
+ assert_equal(0, JobThatCannotBeScheduledWithoutArguments.clean.counters[:before_schedule], 'before_schedule should be zero')
+ Resque.enqueue_at(enqueue_time, JobThatCannotBeScheduledWithoutArguments.clean)
+ assert_equal(0, Resque.delayed_timestamp_size(enqueue_time.to_i), "job should not have been put in queue")
+ assert_equal(1, JobThatCannotBeScheduledWithoutArguments.counters[:before_schedule], 'before_schedule was not run')
+ assert_equal(0, JobThatCannotBeScheduledWithoutArguments.counters[:after_schedule], 'after_schedule was run')
+ end
+end

0 comments on commit 8d0176f

Please sign in to comment.