From 90b007ef7564b507b5a9fb81a805487e25f6e6ab Mon Sep 17 00:00:00 2001 From: ADmad Date: Mon, 7 Nov 2011 03:30:17 +0530 Subject: [PATCH] Implemented priority based triggering of callbacks for objects in collection --- lib/Cake/Console/TaskCollection.php | 4 - lib/Cake/Controller/ComponentCollection.php | 4 +- lib/Cake/Model/BehaviorCollection.php | 9 +- .../Test/Case/Console/TaskCollectionTest.php | 14 -- .../Case/Utility/ObjectCollectionTest.php | 139 +++++++++++++++++- lib/Cake/Utility/ObjectCollection.php | 81 ++++++++-- lib/Cake/View/HelperCollection.php | 6 +- 7 files changed, 213 insertions(+), 44 deletions(-) diff --git a/lib/Cake/Console/TaskCollection.php b/lib/Cake/Console/TaskCollection.php index c82ddb560..5b2d371af 100644 --- a/lib/Cake/Console/TaskCollection.php +++ b/lib/Cake/Console/TaskCollection.php @@ -75,10 +75,6 @@ class TaskCollection extends ObjectCollection { $this->_loaded[$name] = new $taskClass( $this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin ); - $enable = isset($settings['enabled']) ? $settings['enabled'] : true; - if ($enable === true) { - $this->_enabled[] = $name; - } return $this->_loaded[$name]; } diff --git a/lib/Cake/Controller/ComponentCollection.php b/lib/Cake/Controller/ComponentCollection.php index 59b218450..92c024d48 100644 --- a/lib/Cake/Controller/ComponentCollection.php +++ b/lib/Cake/Controller/ComponentCollection.php @@ -96,8 +96,8 @@ class ComponentCollection extends ObjectCollection { } $this->_loaded[$alias] = new $componentClass($this, $settings); $enable = isset($settings['enabled']) ? $settings['enabled'] : true; - if ($enable === true) { - $this->_enabled[] = $alias; + if ($enable) { + $this->enable($alias); } return $this->_loaded[$alias]; } diff --git a/lib/Cake/Model/BehaviorCollection.php b/lib/Cake/Model/BehaviorCollection.php index 993c6a3d4..7292f44ec 100644 --- a/lib/Cake/Model/BehaviorCollection.php +++ b/lib/Cake/Model/BehaviorCollection.php @@ -165,10 +165,10 @@ class BehaviorCollection extends ObjectCollection { } } - $configDisabled = isset($config['enabled']) && $config['enabled'] === false; - if (!in_array($alias, $this->_enabled) && !$configDisabled) { + $enable = isset($config['enabled']) ? $config['enabled'] : true; + if ($enable) { $this->enable($alias); - } elseif ($configDisabled) { + } else { $this->disable($alias); } return true; @@ -184,14 +184,13 @@ class BehaviorCollection extends ObjectCollection { list($plugin, $name) = pluginSplit($name); if (isset($this->_loaded[$name])) { $this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName)); - unset($this->_loaded[$name]); + parent::unload($name); } foreach ($this->_methods as $m => $callback) { if (is_array($callback) && $callback[0] == $name) { unset($this->_methods[$m]); } } - $this->_enabled = array_values(array_diff($this->_enabled, (array)$name)); } /** diff --git a/lib/Cake/Test/Case/Console/TaskCollectionTest.php b/lib/Cake/Test/Case/Console/TaskCollectionTest.php index 3648fb2b4..f70c73f89 100644 --- a/lib/Cake/Test/Case/Console/TaskCollectionTest.php +++ b/lib/Cake/Test/Case/Console/TaskCollectionTest.php @@ -53,22 +53,8 @@ class TaskCollectionTest extends CakeTestCase { $result = $this->Tasks->attached(); $this->assertEquals(array('DbConfig'), $result, 'attached() results are wrong.'); - - $this->assertTrue($this->Tasks->enabled('DbConfig')); } -/** - * test load and enable = false - * - * @return void - */ - public function testLoadWithEnableFalse() { - $result = $this->Tasks->load('DbConfig', array('enabled' => false)); - $this->assertInstanceOf('DbConfigTask', $result); - $this->assertInstanceOf('DbConfigTask', $this->Tasks->DbConfig); - - $this->assertFalse($this->Tasks->enabled('DbConfig'), 'DbConfigTask should be disabled'); - } /** * test missingtask exception * diff --git a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php index 9a7ecdd9a..d2089d1e4 100644 --- a/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php +++ b/lib/Cake/Test/Case/Utility/ObjectCollectionTest.php @@ -23,6 +23,16 @@ App::uses('ObjectCollection', 'Utility'); * A generic object class */ class GenericObject { +/** + * Constructor + * + * @param GenericObjectCollection $collection + * @param array $settings + */ + public function __construct(GenericObjectCollection $collection, $settings = array()) { + $this->_Collection = $collection; + $this->settings = $settings; + } } /** @@ -44,6 +54,14 @@ class SecondGenericObject extends GenericObject { } } +/** + * Third Extension of Generic Object + */ +class ThirdGenericObject extends GenericObject { + public function callback() { + } +} + /** * A collection of Generic objects */ @@ -65,7 +83,7 @@ class GenericObjectCollection extends ObjectCollection { $this->_loaded[$name] = new $objectClass($this, $settings); $enable = isset($settings['enabled']) ? $settings['enabled'] : true; if ($enable === true) { - $this->_enabled[] = $name; + $this->enable($name); } return $this->_loaded[$name]; } @@ -143,10 +161,10 @@ class ObjectCollectionTest extends CakeTestCase { $result = $this->Objects->attached(); $this->assertEquals(array('First'), $result, 'loaded objects are wrong'); - $result = $this->Objects->set('First', new SecondGenericObject()); + $result = $this->Objects->set('First', new SecondGenericObject($this->Objects)); $this->assertInstanceOf('SecondGenericObject', $result['First'], 'set failed'); - $result = $this->Objects->set('Second', new SecondGenericObject()); + $result = $this->Objects->set('Second', new SecondGenericObject($this->Objects)); $this->assertInstanceOf('SecondGenericObject', $result['Second'], 'set failed'); $this->assertEquals(count($result), 2); @@ -164,6 +182,9 @@ class ObjectCollectionTest extends CakeTestCase { if (!class_exists('TriggerMockSecondGenericObject')) { $this->getMock('SecondGenericObject', array(), array(), 'TriggerMockSecondGenericObject', false); } + if (!class_exists('TriggerMockThirdGenericObject')) { + $this->getMock('ThirdGenericObject', array(), array(), 'TriggerMockThirdGenericObject', false); + } } /** @@ -358,6 +379,118 @@ class ObjectCollectionTest extends CakeTestCase { $this->assertEquals(array('new value'), $result); } +/** + * test order of callbacks trigerring based on priority. + * + * @return void + */ + public function testTriggerPriority() { + $this->_makeMockClasses(); + $this->Objects->load('TriggerMockFirst'); + $this->Objects->load('TriggerMockSecond', array('priority' => 5)); + + $this->mockObjects[] = $this->Objects->TriggerMockFirst; + $this->mockObjects[] = $this->Objects->TriggerMockSecond; + + $this->Objects->TriggerMockFirst->expects($this->any()) + ->method('callback') + ->will($this->returnValue('1st')); + $this->Objects->TriggerMockSecond->expects($this->any()) + ->method('callback') + ->will($this->returnValue('2nd')); + + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->load('TriggerMockThird', array('priority' => 7)); + $this->mockObjects[] = $this->Objects->TriggerMockThird; + $this->Objects->TriggerMockThird->expects($this->any()) + ->method('callback') + ->will($this->returnValue('3rd')); + + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '3rd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->disable('TriggerMockFirst'); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '3rd' + ); + $this->assertEquals($expected, $result); + + $this->Objects->enable('TriggerMockFirst'); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '3rd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->disable('TriggerMockThird'); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->enable('TriggerMockThird', false); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st', + '3rd' + ); + $this->assertEquals($expected, $result); + + $this->Objects->setPriority('TriggerMockThird', 1); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '3rd', + '2nd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->disable('TriggerMockThird'); + $this->Objects->setPriority('TriggerMockThird', 11); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st' + ); + $this->assertEquals($expected, $result); + + $this->Objects->enable('TriggerMockThird'); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st', + '3rd' + ); + $this->assertEquals($expected, $result); + + $this->Objects->setPriority('TriggerMockThird'); + $result = $this->Objects->trigger('callback', array(), array('collectReturn' => true)); + $expected = array( + '2nd', + '1st', + '3rd' + ); + $this->assertEquals($expected, $result); + } + /** * test normalizeObjectArray * diff --git a/lib/Cake/Utility/ObjectCollection.php b/lib/Cake/Utility/ObjectCollection.php index c3fdeed6b..0d2353edf 100644 --- a/lib/Cake/Utility/ObjectCollection.php +++ b/lib/Cake/Utility/ObjectCollection.php @@ -39,6 +39,13 @@ abstract class ObjectCollection { */ protected $_loaded = array(); +/** + * Default object priority. A non zero integer. + * + * @var int + */ + public $defaultPriority = 10; + /** * Loads a new object onto the collection. Can throw a variety of exceptions * @@ -95,7 +102,7 @@ abstract class ObjectCollection { $options ); $collected = array(); - $list = $this->_enabled; + $list = array_keys($this->_enabled); if ($options['modParams'] !== false && !isset($params[$options['modParams']])) { throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.')); } @@ -145,30 +152,77 @@ abstract class ObjectCollection { /** * Enables callbacks on an object or array of objects * - * @param mixed $name CamelCased name of the object(s) to enable (string or array) + * @param string|array $name CamelCased name of the object(s) to enable (string or array) + * @param boolean Prioritize enabled list after enabling object(s) * @return void */ - public function enable($name) { + public function enable($name, $prioritize = true) { + $enabled = false; foreach ((array)$name as $object) { - if (isset($this->_loaded[$object]) && array_search($object, $this->_enabled) === false) { - $this->_enabled[] = $object; + if (isset($this->_loaded[$object]) && !isset($this->_enabled[$object])) { + $priority = isset($this->_loaded[$object]->settings['priority']) ? $this->_loaded[$object]->settings['priority'] : $this->defaultPriority; + $this->_enabled[$object] = array($priority); + $enabled = true; } } + if ($prioritize && $enabled) { + $this->prioritize(); + } + } + +/** + * Prioritize list of enabled object + * + * @return array Prioritized list of object + */ + public function prioritize() { + $i = 1; + foreach ($this->_enabled as $name => $priority) { + $priority[1] = $i++; + $this->_enabled[$name] = $priority; + } + asort($this->_enabled); + return $this->_enabled; + } + +/** + * Set priority for an object or array of objects + * + * @param string|array $name CamelCased name of the object(s) to enable (string or array) + * If string the second param $priority is used else it should be an associative array + * with keys as object names and values as priorities to set. + * @param int|null Integer priority to set or null for default + * @return void + */ + public function setPriority($name, $priority = null) { + if (is_string($name)) { + $name = array($name => $priority); + } + foreach ($name as $obj => $prio) { + if (isset($this->_loaded[$obj])) { + if (is_null($prio)) { + $prio = $this->defaultPriority; + } + $this->_loaded[$obj]->settings['priority'] = $prio; + if (isset($this->_enabled[$obj])) { + $this->_enabled[$obj] = array($prio); + } + } + } + $this->prioritize(); } /** * Disables callbacks on a object or array of objects. Public object methods are still * callable as normal. * - * @param mixed $name CamelCased name of the objects(s) to disable (string or array) + * @param string|array $name CamelCased name of the objects(s) to disable (string or array) * @return void */ public function disable($name) { foreach ((array)$name as $object) { - $index = array_search($object, $this->_enabled); - unset($this->_enabled[$index]); + unset($this->_enabled[$object]); } - $this->_enabled = array_values($this->_enabled); } /** @@ -181,9 +235,9 @@ abstract class ObjectCollection { */ public function enabled($name = null) { if (!empty($name)) { - return in_array($name, $this->_enabled); + return isset($this->_enabled[$name]); } - return $this->_enabled; + return array_keys($this->_enabled); } /** @@ -210,7 +264,7 @@ abstract class ObjectCollection { public function unload($name) { list($plugin, $name) = pluginSplit($name); unset($this->_loaded[$name]); - $this->_enabled = array_values(array_diff($this->_enabled, (array)$name)); + unset($this->_enabled[$name]); } /** @@ -248,4 +302,5 @@ abstract class ObjectCollection { } return $normal; } -} \ No newline at end of file + +} diff --git a/lib/Cake/View/HelperCollection.php b/lib/Cake/View/HelperCollection.php index fb1b55260..4b21f22df 100644 --- a/lib/Cake/View/HelperCollection.php +++ b/lib/Cake/View/HelperCollection.php @@ -85,10 +85,10 @@ class HelperCollection extends ObjectCollection { $this->_loaded[$alias]->{$var} = $this->_View->{$var}; } $enable = isset($settings['enabled']) ? $settings['enabled'] : true; - if ($enable === true) { - $this->_enabled[] = $alias; + if ($enable) { + $this->enable($alias); } return $this->_loaded[$alias]; } -} \ No newline at end of file +}