Permalink
Browse files

enabling a callback to be used in addition to String::insert-style st…

…rings for Dispatcher rules. a callback can be used to define the rules as well
  • Loading branch information...
rmarscher committed Apr 26, 2012
1 parent 3e58f83 commit dc3e17de251ad36d939bb7418b2fe51d130253c6
Showing with 92 additions and 7 deletions.
  1. +47 −7 action/Dispatcher.php
  2. +45 −0 tests/cases/action/DispatcherTest.php
View
@@ -50,15 +50,43 @@ class Dispatcher extends \lithium\core\StaticObject {
* Each key in the array represents a 'rule'; if a key that matches the rule is present (and
* not empty) in a route, (i.e. the result of `lithium\net\http\Router::parse()`) then the
* rule's value will be applied to the route before it is dispatched. When applying a rule, any
- * array elements array elements of the flag which are present in the route will be modified
- * using a `lithium\util\String::insert()`-formatted string.
+ * array elements of the flag which are present in the route will be modified
+ * using a `lithium\util\String::insert()`-formatted string. Alternatively,
+ * a callback can be used to do custom transformations other than the
+ * default `lithium\util\String::insert()`.
*
* For example, to implement action prefixes (i.e. `admin_index()`), set a rule named 'admin',
* with a value array containing a modifier key for the `action` element of a route, i.e.:
* `array('action' => 'admin_{:action}')`. Now, if the `'admin'` key is present and not empty
* in the parameters returned from routing, the value of `'action'` will be rewritten per the
* settings in the rule.
*
+ * Here's another example. To support normalizing actions,
+ * set a rule named 'action' with a value array containing a callback that uses
+ * `lithium\util\Inflector` to camelize the action:
+ *
+ * {{{
+ * use lithium\action\Dispatcher;
+ * use lithium\util\Inflector;
+ *
+ * Dispatcher::config(array('rules' => array(
+ * 'action' => array('action' => function($params) {
+ * return Inflector::camelize(strtolower($params['action']), false);
+ * })
+ * )));
+ * }}}
+ *
+ * The rules can be a callback as well:
+ *
+ * {{{
+ * Dispatcher::config(array('rules' => function($params) {
+ * if (isset($params['admin'])) {
+ * return array('special' => array('action' => 'special_{:action}'));
+ * }
+ * return array();
+ * }));
+ * }}}
+ *
* @see lithium\action\Dispatcher::config()
* @see lithium\util\String::insert()
*/
@@ -82,6 +110,10 @@ public static function config(array $config = array()) {
foreach ($config as $key => $val) {
$key = "_{$key}";
+ if (!is_array($val)) {
+ static::${$key} = $val;
+ continue;
+ }
if (isset(static::${$key})) {
static::${$key} = $val + static::${$key};
}
@@ -135,6 +167,7 @@ public static function run($request, array $options = array()) {
public static function applyRules(&$params) {
$result = array();
$values = array();
+ $rules = static::$_rules;
if (!$params) {
return false;
@@ -158,17 +191,24 @@ public static function applyRules(&$params) {
}
$values += $params;
- foreach (static::$_rules as $rule => $value) {
+ if (is_callable($rules)) {
+ $rules = $rules($params);
+ }
+ foreach ($rules as $rule => $value) {
+ if (!isset($values[$rule])) {
+ continue;
+ }
foreach ($value as $k => $v) {
- if (isset($values[$rule])) {
- $result[$k] = String::insert($v, $values);
+ if (is_callable($v)) {
+ $result[$k] = $v($values);
+ continue;
}
$match = preg_replace('/\{:\w+\}/', '@', $v);
$match = preg_replace('/@/', '.+', preg_quote($match, '/'));
-
if (preg_match('/' . $match . '/i', $values[$k])) {
- return false;
+ continue;
}
+ $result[$k] = String::insert($v, $values);
}
}
return $result + $values;
@@ -13,6 +13,7 @@
use lithium\net\http\Router;
use lithium\action\Dispatcher;
use lithium\tests\mocks\action\MockDispatcher;
+use lithium\util\Inflector;
class DispatcherTest extends \lithium\test\Unit {
@@ -130,6 +131,50 @@ public function testConfigManipulation() {
$result = end(MockDispatcher::$dispatched);
$expected = array('action' => 'admin_test', 'controller' => 'Test', 'admin' => true);
$this->assertEqual($expected, $result->params);
+
+ MockDispatcher::config(array('rules' => array(
+ 'action' => array('action' => function($params) {
+ return Inflector::camelize(strtolower($params['action']), false);
+ })
+ )));
+
+ MockDispatcher::$dispatched = array();
+ Router::reset();
+ Router::connect('/', array('controller' => 'test', 'action' => 'TeST-camelize'));
+ MockDispatcher::run(new Request(array('url' => '/')));
+
+ $result = end(MockDispatcher::$dispatched);
+ $expected = array('action' => 'testCamelize', 'controller' => 'Test');
+ $this->assertEqual($expected, $result->params);
+
+ MockDispatcher::config(array('rules' => function($params) {
+ if (isset($params['admin'])) {
+ return array('special' => array('action' => 'special_{:action}'));
+ }
+ return array();
+ }));
+
+ MockDispatcher::$dispatched = array();
+ Router::reset();
+ Router::connect('/', array('controller' => 'test', 'action' => 'test', 'admin' => true));
+ Router::connect('/special', array(
+ 'controller' => 'test', 'action' => 'test',
+ 'admin' => true, 'special' => true
+ ));
+ MockDispatcher::run(new Request(array('url' => '/')));
+
+ $result = end(MockDispatcher::$dispatched);
+ $expected = array('action' => 'test', 'controller' => 'Test', 'admin' => true);
+ $this->assertEqual($expected, $result->params);
+
+ MockDispatcher::run(new Request(array('url' => '/special')));
+
+ $result = end(MockDispatcher::$dispatched);
+ $expected = array(
+ 'action' => 'special_test', 'controller' => 'Test',
+ 'admin' => true, 'special' => true
+ );
+ $this->assertEqual($expected, $result->params);
}
public function testControllerLookupFail() {

0 comments on commit dc3e17d

Please sign in to comment.