Merge pull request #212 from chrisboulton/beforeEnqueue

Add beforeEnqueue hook
This commit is contained in:
Chris Boulton 2015-02-04 10:56:54 -08:00
commit a8322cd4e7
6 changed files with 110 additions and 30 deletions

View File

@ -33,6 +33,7 @@ Changes by iskandar introduce improved support for using DSNs to connect to Redi
* Fix an issue where a lost connection to Redis could cause an infinite loop (atorres757) * Fix an issue where a lost connection to Redis could cause an infinite loop (atorres757)
* Add a helper method to `Resque_Redis` to remove the namespace applied to Redis keys (tonypiper) * Add a helper method to `Resque_Redis` to remove the namespace applied to Redis keys (tonypiper)
* Call beforePerform hook before retrieivng an instance of the job class (allows beforePerform to cancel a job with DontPerform before initialising your application) * Call beforePerform hook before retrieivng an instance of the job class (allows beforePerform to cancel a job with DontPerform before initialising your application)
* Add `beforeEnqueue` hook, called before a job is placed on a queue
## 1.2 (2012-10-13) ## ## 1.2 (2012-10-13) ##

View File

@ -413,6 +413,18 @@ Called whenever a job fails. Arguments passed (in this order) include:
* Exception - The exception that was thrown when the job failed * Exception - The exception that was thrown when the job failed
* Resque_Job - The job that failed * Resque_Job - The job that failed
#### beforeEnqueue ####
Called immediately before a job is enqueued using the `Resque::enqueue` method.
Arguments passed (in this order) include:
* Class - string containing the name of the job to be enqueued
* Arguments - array of arguments for the job
* Queue - string containing the name of the queue the job is to be enqueued in
* ID - string containing the token of the job to be enqueued
You can prevent enqueing of the job by throwing an exception of `Resque_Job_DontCreate`.
#### afterEnqueue #### #### afterEnqueue ####
Called after a job has been queued using the `Resque::enqueue` method. Arguments passed Called after a job has been queued using the `Resque::enqueue` method. Arguments passed

View File

@ -197,21 +197,28 @@ class Resque
* @param array $args Any optional arguments that should be passed when the job is executed. * @param array $args Any optional arguments that should be passed when the job is executed.
* @param boolean $trackStatus Set to true to be able to monitor the status of a job. * @param boolean $trackStatus Set to true to be able to monitor the status of a job.
* *
* @return string * @return string|boolean Job ID when the job was created, false if creation was cancelled due to beforeEnqueue
*/ */
public static function enqueue($queue, $class, $args = null, $trackStatus = false) public static function enqueue($queue, $class, $args = null, $trackStatus = false)
{ {
$result = Resque_Job::create($queue, $class, $args, $trackStatus); $id = Resque::generateJobId();
if ($result) { $hookParams = array(
Resque_Event::trigger('afterEnqueue', array( 'class' => $class,
'class' => $class, 'args' => $args,
'args' => $args, 'queue' => $queue,
'queue' => $queue, 'id' => $id,
'id' => $result, );
)); try {
Resque_Event::trigger('beforeEnqueue', $hookParams);
}
catch(Resque_Job_DontCreate $e) {
return false;
} }
return $result; Resque_Job::create($queue, $class, $args, $trackStatus, $id);
Resque_Event::trigger('afterEnqueue', $hookParams);
return $id;
} }
/** /**
@ -342,5 +349,15 @@ class Resque
$result = self::redis()->del('queue:' . $queue); $result = self::redis()->del('queue:' . $queue);
return ($result == 1) ? $counter : 0; return ($result == 1) ? $counter : 0;
} }
/*
* Generate an identifier to attach to a job for status tracking.
*
* @return string
*/
public static function generateJobId()
{
return md5(uniqid('', true));
}
} }

View File

@ -47,17 +47,21 @@ class Resque_Job
* @param string $class The name of the class that contains the code to execute the job. * @param string $class The name of the class that contains the code to execute the job.
* @param array $args Any optional arguments that should be passed when the job is executed. * @param array $args Any optional arguments that should be passed when the job is executed.
* @param boolean $monitor Set to true to be able to monitor the status of a job. * @param boolean $monitor Set to true to be able to monitor the status of a job.
* @param string $id Unique identifier for tracking the job. Generated if not supplied.
* *
* @return string * @return string
*/ */
public static function create($queue, $class, $args = null, $monitor = false) public static function create($queue, $class, $args = null, $monitor = false, $id = null)
{ {
if (is_null($id)) {
$id = Resque::generateJobId();
}
if($args !== null && !is_array($args)) { if($args !== null && !is_array($args)) {
throw new InvalidArgumentException( throw new InvalidArgumentException(
'Supplied $args must be an array.' 'Supplied $args must be an array.'
); );
} }
$id = md5(uniqid('', true));
Resque::push($queue, array( Resque::push($queue, array(
'class' => $class, 'class' => $class,
'args' => array($args), 'args' => array($args),

View File

@ -0,0 +1,12 @@
<?php
/**
* Exception to be thrown if while enqueuing a job it should not be created.
*
* @package Resque/Job
* @author Chris Boulton <chris@bigcommerce.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
class Resque_Job_DontCreate extends Exception
{
}

View File

@ -76,6 +76,18 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase
$this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called'); $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called');
} }
public function testBeforeEnqueueEventCallbackFires()
{
$event = 'beforeEnqueue';
$callback = 'beforeEnqueueEventCallback';
Resque_Event::listen($event, array($this, $callback));
Resque::enqueue('jobs', 'Test_Job', array(
'somevar'
));
$this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback .') was not called');
}
public function testBeforePerformEventCanStopWork() public function testBeforePerformEventCanStopWork()
{ {
$callback = 'beforePerformEventDontPerformCallback'; $callback = 'beforePerformEventDontPerformCallback';
@ -88,10 +100,22 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase
$this->assertFalse(Test_Job::$called, 'Job was still performed though Resque_Job_DontPerform was thrown'); $this->assertFalse(Test_Job::$called, 'Job was still performed though Resque_Job_DontPerform was thrown');
} }
public function testBeforeEnqueueEventStopsJobCreation()
{
$callback = 'beforeEnqueueEventDontCreateCallback';
Resque_Event::listen('beforeEnqueue', array($this, $callback));
Resque_Event::listen('afterEnqueue', array($this, 'afterEnqueueEventCallback'));
$result = Resque::enqueue('test_job', 'TestClass');
$this->assertContains($callback, $this->callbacksHit, $callback . ' callback was not called');
$this->assertNotContains('afterEnqueueEventCallback', $this->callbacksHit, 'afterEnqueue was still called, even though it should not have been');
$this->assertFalse($result);
}
public function testAfterEnqueueEventCallbackFires() public function testAfterEnqueueEventCallbackFires()
{ {
$callback = 'afterEnqueueEventCallback'; $callback = 'afterEnqueueEventCallback';
$event = 'afterEnqueue'; $event = 'afterEnqueue';
Resque_Event::listen($event, array($this, $callback)); Resque_Event::listen($event, array($this, $callback));
Resque::enqueue('jobs', 'Test_Job', array( Resque::enqueue('jobs', 'Test_Job', array(
@ -103,7 +127,7 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase
public function testStopListeningRemovesListener() public function testStopListeningRemovesListener()
{ {
$callback = 'beforePerformEventCallback'; $callback = 'beforePerformEventCallback';
$event = 'beforePerform'; $event = 'beforePerform';
Resque_Event::listen($event, array($this, $callback)); Resque_Event::listen($event, array($this, $callback));
Resque_Event::stopListening($event, array($this, $callback)); Resque_Event::stopListening($event, array($this, $callback));
@ -117,13 +141,18 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase
); );
} }
public function beforePerformEventDontPerformCallback($instance) public function beforePerformEventDontPerformCallback($instance)
{ {
$this->callbacksHit[] = __FUNCTION__; $this->callbacksHit[] = __FUNCTION__;
throw new Resque_Job_DontPerform; throw new Resque_Job_DontPerform;
} }
public function beforeEnqueueEventDontCreateCallback($queue, $class, $args, $track = false)
{
$this->callbacksHit[] = __FUNCTION__;
throw new Resque_Job_DontCreate;
}
public function assertValidEventCallback($function, $job) public function assertValidEventCallback($function, $job)
{ {
$this->callbacksHit[] = $function; $this->callbacksHit[] = $function;
@ -143,6 +172,11 @@ class Resque_Tests_EventTest extends Resque_Tests_TestCase
), $args); ), $args);
} }
public function beforeEnqueueEventCallback($job)
{
$this->callbacksHit[] = __FUNCTION__;
}
public function beforePerformEventCallback($job) public function beforePerformEventCallback($job)
{ {
$this->assertValidEventCallback(__FUNCTION__, $job); $this->assertValidEventCallback(__FUNCTION__, $job);