Updates to Router

- Add Router::add method
- Refactor Router to contain one RouteTable
This commit is contained in:
PJ Dietz 2015-02-21 14:11:53 -05:00
parent b350693aca
commit 5dacb232ec
3 changed files with 98 additions and 96 deletions

View File

@ -15,6 +15,7 @@ use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface; use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface; use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Routes\PrefixRoute; use pjdietz\WellRESTed\Routes\PrefixRoute;
use pjdietz\WellRESTed\Routes\RouteFactory;
use pjdietz\WellRESTed\Routes\StaticRoute; use pjdietz\WellRESTed\Routes\StaticRoute;
/** /**
@ -26,26 +27,64 @@ class Router implements HandlerInterface
{ {
/** @var array Hash array of status code => error handler */ /** @var array Hash array of status code => error handler */
private $errorHandlers; private $errorHandlers;
/** @var array Hash array HTTP verb => RouteTable */ /** @var RouteTable */
private $routeTables; private $routeTable;
/** Create a new Router. */ /** Create a new Router. */
public function __construct() public function __construct()
{ {
$this->errorHandlers = array(); $this->errorHandlers = array();
$this->routeTables = array(); $this->routeTable = new RouteTable();
}
/**
* Add a route or series of routes to the Router.
*
* When adding a single route, the first argument should be the path, path prefix, URI template, or regex pattern.
* The method will attempt to find the best type of route based on this argument and send the remainding arguments
* to that routes constructor. @see {RouteFactory::createRoute}
*
* To add multiple routes, pass arrays to add where each array contains an argument list.
*/
public function add()
{
$factory = new RouteFactory();
$args = func_get_args();
if (count($args) > 1 && is_array($args[0])) {
foreach ($args as $argumentList) {
$route = call_user_func_array(array($factory, "createRoute"), $argumentList);
$this->addRoute($route);
}
return;
}
$route = call_user_func_array(array($factory, "createRoute"), $args);
$this->addRoute($route);
}
/**
* Append a series of routes.
*
* @param array $routes List array of routes
*/
public function addRoutes(array $routes)
{
foreach ($routes as $route) {
if ($route instanceof HandlerInterface) {
$this->addRoute($route);
}
}
} }
/** /**
* Append a new route to the route table. * Append a new route to the route table.
* *
* @param HandlerInterface $route * @param HandlerInterface $route
* @param string $method HTTP Method; * for any
*/ */
public function addRoute(HandlerInterface $route, $method = "*") public function addRoute(HandlerInterface $route)
{ {
$table = $this->getRouteTable($method); $this->routeTable->addRoute($route);
$table->addRoute($route);
} }
/** /**
@ -96,7 +135,7 @@ class Router implements HandlerInterface
*/ */
public function getResponse(RequestInterface $request, array $args = null) public function getResponse(RequestInterface $request, array $args = null)
{ {
$response = $this->getResponseFromRouteTables($request, $args); $response = $this->tryResponse($this->routeTable, $request, $args);
if ($response) { if ($response) {
// Check if the router has an error handler for this status code. // Check if the router has an error handler for this status code.
$status = $response->getStatusCode(); $status = $response->getStatusCode();
@ -126,30 +165,6 @@ class Router implements HandlerInterface
return $response; return $response;
} }
private function getRouteTable($method = "*")
{
if (!isset($this->routeTables[$method])) {
$this->routeTables[$method] = new RouteTable();
}
return $this->routeTables[$method];
}
private function getResponseFromRouteTables(RequestInterface $request, array $args = null)
{
$method = $request->getMethod();
if (isset($this->routeTables[$method])) {
$table = $this->routeTables[$method];
return $this->tryResponse($table, $request, $args);
}
if (isset($this->routeTables["*"])) {
$table = $this->routeTables["*"];
return $this->tryResponse($table, $request, $args);
}
return null;
}
private function getErrorResponse($status, $request, $args = null, $response = null) private function getErrorResponse($status, $request, $args = null, $response = null)
{ {
if (isset($this->errorHandlers[$status])) { if (isset($this->errorHandlers[$status])) {

View File

@ -10,6 +10,7 @@
namespace pjdietz\WellRESTed\Routes; namespace pjdietz\WellRESTed\Routes;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use ReflectionClass; use ReflectionClass;
/** /**
@ -17,6 +18,9 @@ use ReflectionClass;
*/ */
class RouteFactory class RouteFactory
{ {
/**
* @return HandlerInterface
*/
public function createRoute() public function createRoute()
{ {
$args = func_get_args(); $args = func_get_args();
@ -51,6 +55,5 @@ class RouteFactory
// Regex // Regex
$reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\RegexRoute"); $reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\RegexRoute");
return $reflector->newInstanceArgs($args); return $reflector->newInstanceArgs($args);
} }
} }

View File

@ -4,6 +4,7 @@ namespace pjdietz\WellRESTed\Test;
use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException; use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
use pjdietz\WellRESTed\Router; use pjdietz\WellRESTed\Router;
use pjdietz\WellRESTed\Routes\TemplateRoute;
use Prophecy\Argument; use Prophecy\Argument;
/** /**
@ -26,80 +27,63 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface"); $this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
} }
/** public function testAddsSingleRoute()
* @dataProvider httpMethodProvider
*/
public function testAddsRouteToDefaultRouteTable($method)
{ {
$this->request->getPath()->willReturn("/"); $this->request->getPath()->willReturn("/cats/");
$this->request->getMethod()->willReturn($method); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$router = new Router(); $router = new Router();
$router->addRoute($this->route->reveal()); $router->add("/cats/", $this->handler->reveal());
$response = $router->getResponse($this->request->reveal()); $response = $router->getResponse($this->request->reveal());
$this->assertNotNull($response); $this->assertNotNull($response);
} }
public function httpMethodProvider()
{
return [
["GET"],
["POST"],
["PUT"],
["DELETE"],
["HEAD"],
["PATCH"],
["OPTIONS"],
["CUSTOM"]
];
}
/** /**
* @dataProvider httpMethodListProvider * @dataProvider pathProvider
*/ */
public function testAddsRouteToSpecificRouteTable($registerMethod, $requestMethod, $expectedResponse) public function testAddsMultpleRoutes($path, $exptectedSuccess)
{ {
$this->request->getPath()->willReturn("/"); $this->request->getPath()->willReturn($path);
$this->request->getMethod()->willReturn($requestMethod);
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$router = new Router();
$router->addRoute($this->route->reveal(), $registerMethod);
$response = $router->getResponse($this->request->reveal());
$this->assertEquals($expectedResponse, !is_null($response));
}
public function httpMethodListProvider()
{
return [
["GET", "GET", true],
["POST", "GET", false]
];
}
/**
* @dataProvider httpMethodProvider
*/
public function testMatchSpecificTableBeforeDefaultTable($method)
{
$this->request->getMethod()->willReturn("POST");
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal()); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
$genericRoute = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$genericRoute->getResponse()->willReturn(null);
$specificRoute = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
$specificRoute->getResponse(Argument::cetera())->willReturn(null);
$router = new Router(); $router = new Router();
$router->addRoute($genericRoute->reveal()); $router->add(
$router->addRoute($specificRoute->reveal(), "POST"); ["/cats/", $this->handler->reveal()],
$router->getResponse($this->request->reveal()); ["/cats/*", $this->handler->reveal()],
["/dogs/{name}", $this->handler->reveal(), TemplateRoute::RE_ALPHA],
["~/hamsters/[a-z]+~", $this->handler->reveal()]
);
$response = $router->getResponse($this->request->reveal());
$this->assertEquals($exptectedSuccess, !is_null($response));
}
$genericRoute->getResponse(Argument::cetera())->shouldNotHaveBeenCalled(); public function pathProvider()
$specificRoute->getResponse(Argument::cetera())->shouldHaveBeenCalled(); {
return [
["/cats/", true],
["/cats/molly", true],
["/dogs/bear", true],
["/hamsters/fizzgig", true],
["/dogs/", false],
["/birds/", false],
["/hamsters/23", false]
];
}
public function testAddsSingleRouteInstance()
{
$router = new Router();
$router->addRoute($this->route->reveal());
$router->getResponse($this->request->reveal());
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
}
public function testAddsMultipleRouteInstances()
{
$router = new Router();
$router->addRoutes([$this->route->reveal()]);
$router->getResponse($this->request->reveal());
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
} }
public function testRespondsWithErrorResponseForHttpException() public function testRespondsWithErrorResponseForHttpException()