Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added metadata to routes, returned in matches. Changed how redirects …

…and aliaseswork, simpler now.
  • Loading branch information...
commit f28fd17d8a0f2a75b6498f1f3195a0eff96b6b05 1 parent a5097e3
@lox lox authored
View
83 classes/Ergo/Decorator.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Ergo;
+
+/**
+ * A generic decorator for forwarding method calls and property access to a delegate
+ */
+class Decorator
+{
+ private $__instance;
+
+ /**
+ * Constructor.
+ * @param Object $object
+ */
+ public function __construct($object)
+ {
+ $this->__setObject($object);
+ }
+
+ /**
+ * Returns the decorated object.
+ */
+ public function __getObject()
+ {
+ return $this->__instance;
+ }
+
+ /**
+ * Replace the decorated delegate with the specified object.
+ */
+ public function __setObject($object)
+ {
+ $this->__instance = $object;
+ }
+
+ /**
+ * Forward method calls.
+ *
+ * @param String $method method name
+ * @param Array $args method arguments
+ * @return Unknown method return value
+ */
+ public function __call($method, $args)
+ {
+ $callback = array($this->__instance, $method);
+
+ if (is_callable($callback))
+ {
+ return call_user_func_array($callback, $args);
+ }
+ else
+ {
+ throw new \BadMethodCallException(sprintf(
+ '%s::%s() is not callable',
+ get_class($this->__instance),
+ $method
+ ));
+ }
+ }
+
+ /**
+ * Forward property set.
+ *
+ * @param String $name property name
+ * @param Unknown $value property value
+ */
+ public function __set($name, $value)
+ {
+ $this->__instance->$name = $value;
+ }
+
+ /**
+ * Forward property get.
+ *
+ * @param String $name, property name
+ * @return Unknown
+ */
+ public function __get($name)
+ {
+ return $this->__instance->$name;
+ }
+}
View
1  classes/Ergo/Http/Response.php
@@ -56,5 +56,4 @@ public function getBody()
{
return $this->_body;
}
-
}
View
7 classes/Ergo/Routing/ControllerDirectory.php
@@ -11,11 +11,14 @@ class ControllerDirectory implements ControllerResolver
private $_callback;
/**
- * @param DirectoryIterator
- * @param callback takes a file and class and returns a controller
+ * @param mixed either a directory path, or an iterator
+ * @param callback returns an instance of a controller, given file and controller name
*/
public function __construct($directoryIterator, $callback=null)
{
+ if(is_string($directoryIterator))
+ $directoryIterator = new \DirectoryIterator($directoryIterator);
+
$this->_iterator = $directoryIterator;
$this->_callback = $callback ?: function($file, $className) {
require_once($file);
View
19 classes/Ergo/Routing/FilteredController.php
@@ -45,7 +45,7 @@ public function addResponseFilter($filter)
* Apply a chain of filters to an object
* @return object
*/
- private function _filter($chain, $object)
+ private function _applyFilter($chain, $object)
{
foreach($chain as $filter)
{
@@ -56,14 +56,23 @@ private function _filter($chain, $object)
return $object;
}
+ /**
+ * Filter a request and response and return the response
+ * @return response
+ */
+ protected function filter($request)
+ {
+ return $this->_applyFilter($this->_responses,
+ $this->_controller->execute(
+ $this->_applyFilter($this->_requests, $request)
+ ));
+ }
+
/* (non-phpdoc)
* @see Controller::execute()
*/
public function execute($request)
{
- return $this->_filter($this->_responses,
- $this->_controller->execute(
- $this->_filter($this->_requests, $request)
- ));
+ return $this->filter($request);
}
}
View
6 classes/Ergo/Routing/Route.php
@@ -42,7 +42,7 @@ public function __construct($name, $template)
/**
* @return RouteMatch or null if no match.
*/
- public function getMatch($path)
+ public function getMatch($path, $metadata=null)
{
if (preg_match($this->_pattern, $path, $matches))
{
@@ -53,11 +53,11 @@ public function getMatch($path)
? array()
: array_combine($this->_parameters, $matches);
- return new RouteMatch($this->_name, $parameters);
+ return new RouteMatch($this->_name, $parameters, $metadata);
}
else if(strlen($path) > 1 && substr($path,-1) == '/')
{
- return $this->getMatch(rtrim($path,'/'));
+ return $this->getMatch(rtrim($path,'/'), $metadata);
}
return null;
View
31 classes/Ergo/Routing/RouteMatch.php
@@ -5,21 +5,22 @@
/**
* A match result from a lookup against a {@link Router}
*/
-class RouteMatch extends \ArrayIterator
+class RouteMatch implements \IteratorAggregate
{
private $_name;
private $_parameters;
+ private $_metadata;
/**
* @param string $name
* @param array $parameters
* @params array $tags
*/
- public function __construct($name, $parameters)
+ public function __construct($name, $parameters, $metadata=array())
{
$this->_name = $name;
$this->_parameters = $parameters;
- parent::__construct($parameters);
+ $this->_metadata = $metadata;
}
/**
@@ -39,10 +40,34 @@ public function getParameters()
}
/**
+ * @return array
+ */
+ public function getMetadata()
+ {
+ return $this->_metadata;
+ }
+
+ /**
* @return string
*/
public function parameter($key,$default=false)
{
return isset($this[$key]) ? $this[$key] : $default;
}
+
+ /*
+ * @see IteratorAggregate
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->_parameters);
+ }
+
+ /**
+ * Magic getter method
+ */
+ public function __get($key)
+ {
+ return $this->_parameters[$key];
+ }
}
View
132 classes/Ergo/Routing/Router.php
@@ -9,8 +9,10 @@ class Router implements Controller
{
private $_routes = array();
private $_controllers = array();
+ private $_defaultMetadata = array();
+ private $_metadata = array();
private $_resolver;
- private $_prefixes = array();
+ private $_baseUrl;
/**
* Constructor
@@ -19,23 +21,51 @@ class Router implements Controller
public function __construct($resolver=null)
{
$this->_resolver = $resolver;
+ }
- $this
- ->prefix('redirect', function($str){ return new RedirectController($str); })
- ->prefix('alias', function($str, $router){ return $router->controller($str); })
- ;
+ /**
+ * Sets a base url for url building
+ * @chainable
+ */
+ public function setBaseUrl($url)
+ {
+ $this->_baseUrl = $url;
+ return $this;
+ }
+
+ /**
+ * Sets the default metadata to be set into routes
+ */
+ public function setDefaultMetadata($metadata)
+ {
+ $this->_defaultMetadata = $metadata;
+ return $this;
+ }
+
+ /**
+ * Configures the routemap from a php file
+ * @chainable
+ */
+ public function configure($file)
+ {
+ require($file);
+ return $this;
}
/**
- * Connects a route template to
+ * Gives a route template a unique name and a controller to route to
* @param string a url template
* @param string a unique name for the route
+ * @param mixed a controller, either a class, a string or a callback
* @chainable
*/
- public function connect($template, $name, $controller=null)
+ public function connect($template, $name, $controller=null, $metadata=array())
{
$this->_routes[$name] = new Route($name, $template);
+ if($metadata)
+ $this->_metadata[$name] = $metadata;
+
if($controller)
$this->_controllers[$name] = $controller;
@@ -43,6 +73,26 @@ public function connect($template, $name, $controller=null)
}
/**
+ * Create a redirect from a particular route name to another
+ * @chainable
+ */
+ public function redirect($template, $name, $to)
+ {
+ return $this->connect($template, $name, new RedirectController($to));
+ }
+
+ /**
+ * Register an alias from one route name to another
+ */
+ public function alias($template, $name, $to)
+ {
+ $router = $this;
+ return $this->connect($template, $name, function($request) use($router, $to) {
+ return $router->controller($to)->execute($request);
+ });
+ }
+
+ /**
* Looks up a route match based on a url path
* @param string $path
* @return RouteMatch
@@ -51,7 +101,7 @@ public function lookup($path)
{
foreach ($this->_routes as $route)
{
- if ($match = $route->getMatch($path))
+ if ($match = $route->getMatch($path, $this->metadata($route->getName())))
return $match;
}
@@ -59,6 +109,18 @@ public function lookup($path)
}
/**
+ * Looks up metadata for a route name
+ * @return array
+ */
+ public function metadata($routeName)
+ {
+ return isset($this->_metadata[$routeName])
+ ? array_merge($this->_defaultMetadata, $this->_metadata[$routeName])
+ : $this->_defaultMetadata
+ ;
+ }
+
+ /**
* Looks up a route by name
* @return Route
*/
@@ -75,9 +137,16 @@ public function routeByName($name)
* @param string $name
* @param array $parameters
*/
- public function buildUrl($name, $parameters = array())
+ public function buildUrl($name, $parameters=array(), $baseUrl=null)
{
- return $this->routeByName($name)->interpolate($parameters);
+ $url = $this->routeByName($name)->interpolate($parameters);
+
+ if($baseUrl)
+ return (string) $baseUrl->relative($url);
+ else if(isset($this->_baseUrl))
+ return $this->_baseUrl->relative($url);
+ else
+ return $url;
}
/**
@@ -85,49 +154,20 @@ public function buildUrl($name, $parameters = array())
*/
public function controller($name)
{
- if(isset($this->_controllers[$name]))
+ if(isset($this->_controllers[$name]) && ($controller = $this->_controllers[$name]))
{
- $controller = $this->_controllers[$name];
-
- if(is_string($controller))
- return $this->_controllerFromString($controller);
- else if(is_callable($controller))
- return new CallbackController($controller);
- else if(is_object($controller))
- return $controller;
+ return is_callable($controller)
+ ? new CallbackController($controller)
+ : $controller
+ ;
}
- else if($this->_resolver)
- {
+
+ if($this->_resolver)
return $this->_resolver->resolve($name);
- }
throw new LookupException("No controller defined for route '$name'");
}
- /**
- * Register a callback for parsing a controller prefix.
- * @chainable
- */
- public function prefix($prefix, $callback)
- {
- $this->_prefixes[$prefix] = $callback;
- return $this;
- }
-
- /**
- * @return Controller
- */
- private function _controllerFromString($string)
- {
- if(preg_match('/^(.+?)\:(.+?)$/', $string, $m))
- {
- if(isset($this->_prefixes[$m[1]]))
- return call_user_func($this->_prefixes[$m[1]], $m[2], $this);
- }
-
- throw new LookupException("Unknown controller string format '$string'");
- }
-
/* (non-phpdoc)
* @see Controller::execute()
*/
View
2  runtests.php
@@ -4,7 +4,7 @@
define('BASEDIR',dirname(__FILE__));
require_once(BASEDIR.'/classes/Ergo/ClassLoader.php');
-$classloader = new Ergo_ClassLoader();
+$classloader = new \Ergo\ClassLoader();
$classloader->register()->includePaths(array(
BASEDIR."/classes",
BASEDIR."/vendor/simpletest",
View
44 tests/Routing/RouteTest.php
@@ -4,7 +4,6 @@
use Ergo\Http;
use Ergo\Routing;
-use Ergo\Routing;
\Mock::generate('\Ergo\Routing\Controller','Routing\MockController');
@@ -19,7 +18,7 @@ class RouteTest extends \UnitTestCase
public function testRouteLookup()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->_assertRoute($router,'/fruits','fruits',
array()
@@ -37,7 +36,7 @@ public function testRouteLookup()
public function testRouterTrimsTrailingSlashes()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->_assertRoute($router,'/fruits/','fruits',
array()
@@ -47,7 +46,7 @@ public function testRouterTrimsTrailingSlashes()
public function testRouteBuild()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->assertEqual(
$router->buildUrl('fruits'),
@@ -68,7 +67,7 @@ public function testRouteBuild()
public function testRouteLookupFailsOnNonExistentRouteName()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->expectException('\Ergo\Routing\LookupException');
$router->lookup('/blarg');
}
@@ -76,7 +75,7 @@ public function testRouteLookupFailsOnNonExistentRouteName()
public function testRouteLookupFailsWithEmptyTemplateVars()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->expectException('\Ergo\Routing\LookupException');
$router->lookup('/fruits//flavours/');
}
@@ -84,7 +83,7 @@ public function testRouteLookupFailsWithEmptyTemplateVars()
public function testRouteBuildFailsWithExtraParam()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
$this->expectException('\Ergo\Routing\BuildException');
$router->buildUrl('fruits', array('test' => 123));
}
@@ -92,22 +91,21 @@ public function testRouteBuildFailsWithExtraParam()
public function testRouteBuildFailsWithMissingParam()
{
$router = new Routing\Router();
- foreach($this->_exampleRoutes as $template=>$name) $router->map($template, $name);
- $this->expectException('\Ergo\Routing\BuildException');
+ foreach($this->_exampleRoutes as $template=>$name) $router->connect($template, $name);
+ $this->expectException('\Ergo\Routing\LookupException');
$router->buildUrl('flavours');
}
- public function testRouteMatchArrayInterface()
+ public function testRouteMatchGetterInterface()
{
$match = new Routing\RouteMatch('test', array('a' => 'b'));
- $this->assertEqual($match['a'], 'b');
+ $this->assertEqual($match->a, 'b');
}
-
public function testRoutesWithStringTypes()
{
$router = new Routing\Router();
- $router->map('/fruits/{fruitname:string}','fruit');
+ $router->connect('/fruits/{fruitname:string}','fruit');
$this->_assertRoute($router,'/fruits/blargh','fruit',
array('fruitname'=>'blargh')
@@ -119,7 +117,7 @@ public function testRoutesWithStringTypes()
public function testRoutesWithIntegerTypes()
{
$router = new Routing\Router();
- $router->map('/fruits/{fruitid:int}','fruit');
+ $router->connect('/fruits/{fruitid:int}','fruit');
$this->_assertRoute($router,'/fruits/123','fruit',
array('fruitid'=>'123')
@@ -132,7 +130,7 @@ public function testRoutesWithIntegerTypes()
public function testRoutesWithEnumTypes()
{
$router = new Routing\Router();
- $router->map('/fruits/{fruittype:(orange|apple)}','fruit');
+ $router->connect('/fruits/{fruittype:(orange|apple)}','fruit');
$this->_assertRoute($router,'/fruits/apple','fruit',
array('fruittype'=>'apple')
@@ -145,7 +143,7 @@ public function testRoutesWithEnumTypes()
public function testSimpleStarRoutes()
{
$router = new Routing\Router();
- $router->map('/fruits/*','fruit');
+ $router->connect('/fruits/*','fruit');
$this->_assertRoute($router,'/fruits/this/is/a/test','fruit');
$this->_assertNoRoute($router,'/blargh');
@@ -154,7 +152,7 @@ public function testSimpleStarRoutes()
public function testStarRoutesWithParameters()
{
$router = new Routing\Router();
- $router->map('/fruits/{fruitid}/*','fruit');
+ $router->connect('/fruits/{fruitid}/*','fruit');
$this->_assertRoute($router,'/fruits/5/this/is/a/test','fruit',array(
'fruitid'=>5
@@ -165,7 +163,7 @@ public function testStarRoutesWithParameters()
public function testStarRoutesMatchNothing()
{
$router = new Routing\Router();
- $router->map('/*','default');
+ $router->connect('/*','default');
$this->_assertRoute($router,'/','default');
$this->_assertRoute($router,'/this/is/a/test','default');
@@ -174,7 +172,7 @@ public function testStarRoutesMatchNothing()
public function testInterpolationFailsWithStarRoutes()
{
$router = new Routing\Router();
- $router->map('/{fruit}/*','fruit');
+ $router->connect('/{fruit}/*','fruit');
$this->expectException();
$router->buildUrl('fruit', array('fruit' =>'apple'));
@@ -182,18 +180,18 @@ public function testInterpolationFailsWithStarRoutes()
// ----------------------------------------
- private function _assertRoute($map, $template, $name, $parameters=false)
+ private function _assertRoute($connect, $template, $name, $parameters=false)
{
- $match = $map->lookup($template);
+ $match = $connect->lookup($template);
$this->assertEqual($match->getName(), $name);
if($parameters) $this->assertEqual($match->getParameters(), $parameters);
}
- private function _assertNoRoute($map, $template)
+ private function _assertNoRoute($connect, $template)
{
try
{
- $map->lookup($template);
+ $connect->lookup($template);
$this->fail("should fail route lookup for $template");
}
catch(Routing\LookupException $e)
View
20 tests/Routing/RouterTest.php
@@ -46,7 +46,7 @@ public function testConnectingARedirectRoute()
{
$router = new Router();
$router->connect('/user/{userid}', 'User.view');
- $router->connect('/user/alias/{userid}', 'Alias.view', 'redirect:User.view');
+ $router->redirect('/user/alias/{userid}', 'Alias.view', 'User.view');
$response = $router->execute(new Http\Request('GET','/user/alias/24'));
$this->assertResponse($response, NULL, 302);
@@ -63,27 +63,21 @@ public function testConnectingAnAliasRoute()
$router = new Router();
$router->connect('/user/{userid}', 'User.view', $controller);
- $router->connect('/user/alias/{userid}', 'Alias.view', 'alias:User.view');
+ $router->alias('/user/alias/{userid}', 'Alias.view', 'User.view');
$response = $router->execute(new Http\Request('GET','/user/alias/24'));
$this->assertResponse($response, 'Alias.view');
}
- public function testCustomControllerPrefix()
+ public function testRouteMetadata()
{
$router = new Router();
$router
- ->connect('/user/{userid}', 'Alias.view', 'llama:test')
- ->prefix('llama', function($string) {
- return new Routing\CallbackController(function($request, $builder) use($string) {
- return $builder
- ->setBody($string)
- ->build();
- });
- });
+ ->connect('/user/{userid}', 'User.view', 'User', array('https'=>true))
+ ;
- $response = $router->execute(new Http\Request('GET','/user/24'));
- $this->assertResponse($response, 'test');
+ $route = $router->lookup('/user/24');
+ $this->assertEqual($route->getMetadata(), array('https'=>true));
}
private function assertResponse($response, $body, $status=200)
Please sign in to comment.
Something went wrong with that request. Please try again.