Browse Source

Making the CakeEvent -> ObjectCollection bridge more intelligent, adding tests

Jose Lorenzo Rodriguez 14 years ago
parent
commit
35ecbfebde

+ 4 - 3
lib/Cake/Test/Case/Controller/ControllerTest.php

@@ -768,7 +768,7 @@ class ControllerTest extends CakeTestCase {
 	}
 
 /**
- * test that beforeRedirect callback returnning null doesn't affect things.
+ * test that beforeRedirect callback returning null doesn't affect things.
  *
  * @return void
  */
@@ -790,7 +790,7 @@ class ControllerTest extends CakeTestCase {
 	}
 
 /**
- * test that beforeRedirect callback returnning null doesn't affect things.
+ * test that beforeRedirect callback returning null doesn't affect things.
  *
  * @return void
  */
@@ -823,7 +823,7 @@ class ControllerTest extends CakeTestCase {
 	}
 
 /**
- * test that beforeRedirect callback returnning false in controller
+ * test that beforeRedirect callback returning false in controller
  *
  * @return void
  */
@@ -833,6 +833,7 @@ class ControllerTest extends CakeTestCase {
 		$Controller->Components = $this->getMock('ComponentCollection', array('trigger'));
 
 		$Controller->expects($this->once())->method('beforeRedirect')
+			->with('http://cakephp.org')
 			->will($this->returnValue(false));
 		$Controller->response->expects($this->never())->method('header');
 		$Controller->expects($this->never())->method('_stop');

+ 56 - 0
lib/Cake/Test/Case/Utility/ObjectCollectionTest.php

@@ -18,6 +18,7 @@
  */
 
 App::uses('ObjectCollection', 'Utility');
+App::uses('CakeEvent', 'Event');
 
 /**
  * A generic object class
@@ -524,5 +525,60 @@ class ObjectCollectionTest extends CakeTestCase {
 		$result = ObjectCollection::normalizeObjectArray($components);
 		$this->assertEquals($expected, $result);
 	}
+	
+/**
+ * tests that passing an instance of CakeEvent to trigger will prepend the subject to the list of arguments
+ *
+ * @return void
+ */
+	public function testDispatchEventWithSubject() {
+		$this->_makeMockClasses();
+		$this->Objects->load('TriggerMockFirst');
+		$this->Objects->load('TriggerMockSecond');
+
+		$this->mockObjects[] = $this->Objects->TriggerMockFirst;
+		$this->mockObjects[] = $this->Objects->TriggerMockSecond;
+
+		$subjectClass = new Object();
+		$this->Objects->TriggerMockFirst->expects($this->once())
+			->method('callback')
+			->with($subjectClass, 'first argument')
+			->will($this->returnValue(true));
+		$this->Objects->TriggerMockSecond->expects($this->once())
+			->method('callback')
+			->with($subjectClass, 'first argument')
+			->will($this->returnValue(true));
+
+		$event = new CakeEvent('callback', $subjectClass, array('first argument'));
+		$this->assertTrue($this->Objects->trigger($event));
+	}
 
+/**
+ * tests that passing an instance of CakeEvent to trigger with omitSubject property
+ * will NOT prepend the subject to the list of arguments
+ *
+ * @return void
+ */
+	public function testDispatchEventNoSubject() {
+		$this->_makeMockClasses();
+		$this->Objects->load('TriggerMockFirst');
+		$this->Objects->load('TriggerMockSecond');
+
+		$this->mockObjects[] = $this->Objects->TriggerMockFirst;
+		$this->mockObjects[] = $this->Objects->TriggerMockSecond;
+
+		$subjectClass = new Object();
+		$this->Objects->TriggerMockFirst->expects($this->once())
+			->method('callback')
+			->with('first argument')
+			->will($this->returnValue(true));
+		$this->Objects->TriggerMockSecond->expects($this->once())
+			->method('callback')
+			->with('first argument')
+			->will($this->returnValue(true));
+
+		$event = new CakeEvent('callback', $subjectClass, array('first argument'));
+		$event->omitSubject = true;
+		$this->assertTrue($this->Objects->trigger($event));
+	}
 }

+ 4 - 3
lib/Cake/Utility/ObjectCollection.php

@@ -99,8 +99,9 @@ abstract class ObjectCollection {
 			$event = $callback;
 			if (is_array($event->data)) {
 				$params =& $event->data;
-			} else {
-				$params = array($event->subject());
+			}
+			if (empty($event->omitSubject)) {
+				$subject = $event->subject();
 			}
 			//TODO: Temporary BC check, while we move all the triggers system into the CakeEventManager
 			foreach (array('breakOn', 'collectReturn', 'modParams') as $opt) {
@@ -125,7 +126,7 @@ abstract class ObjectCollection {
 			throw new CakeException(__d('cake_dev', 'Cannot use modParams with indexes that do not exist.'));
 		}
 		foreach ($list as $name) {
-			$result = call_user_func_array(array($this->_loaded[$name], $callback), $params);
+			$result = call_user_func_array(array($this->_loaded[$name], $callback), compact('subject') + $params);
 			if ($options['collectReturn'] === true) {
 				$collected[] = $result;
 			}

+ 39 - 0
lib/Cake/View/HelperCollection.php

@@ -113,4 +113,43 @@ class HelperCollection extends ObjectCollection implements CakeEventListener {
 			'View.afterLayout' => 'trigger'
 		);
 	}
+
+/**
+ * Trigger a callback method on every object in the collection.
+ * Used to trigger methods on objects in the collection.  Will fire the methods in the
+ * order they were attached.
+ *
+ * ### Options
+ *
+ * - `breakOn` Set to the value or values you want the callback propagation to stop on.
+ *    Can either be a scalar value, or an array of values to break on. Defaults to `false`.
+ *
+ * - `break` Set to true to enabled breaking. When a trigger is broken, the last returned value
+ *    will be returned.  If used in combination with `collectReturn` the collected results will be returned.
+ *    Defaults to `false`.
+ *
+ * - `collectReturn` Set to true to collect the return of each object into an array.
+ *    This array of return values will be returned from the trigger() call. Defaults to `false`.
+ *
+ * - `modParams` Allows each object the callback gets called on to modify the parameters to the next object.
+ *    Setting modParams to an integer value will allow you to modify the parameter with that index.
+ *    Any non-null value will modify the parameter index indicated.
+ *    Defaults to false.
+ *
+ *
+ * @param string $callback|CakeEvent Method to fire on all the objects. Its assumed all the objects implement
+ *   the method you are calling. If an instance of CakeEvent is provided, then then Event name will parsed to
+ *   get the callback name. This is done by getting the last word after any dot in the event name
+ *   (eg. `Model.afterSave` event will trigger the `afterSave` callback)
+ * @param array $params Array of parameters for the triggered callback.
+ * @param array $options Array of options.
+ * @return mixed Either the last result or all results if collectReturn is on.
+ * @throws CakeException when modParams is used with an index that does not exist.
+ */
+	public function trigger($callback, $params = array(), $options = array()) {
+		if ($callback instanceof CakeEvent) {
+			$callback->omitSubject = true;
+		}
+		return parent::trigger($callback, $params, $options);
+	}
 }