From aad373c5c362eb47dabdde0ce9d62eb7d1f8c716 Mon Sep 17 00:00:00 2001 From: KrisJordan Date: Wed, 3 Dec 2008 21:33:44 -0500 Subject: [PATCH] Model Helper --- TestRunner.php | 2 +- .../controllers/PostsController.class.php | 63 ----- blueprint.html | 82 +++++-- bootstrap.php | 2 +- phpinfo.php | 3 + recess-config.php | 18 +- .../RecessToolsAppsController.class.php | 6 +- .../lib/recess/apps/tools/views/apps/app.php | 2 +- .../apps/tools/views/apps/createModel.php | 168 +++++++++++++ .../apps/tools/views/apps/newModelJQuery.php | 69 ++++++ .../recess/apps/tools/views/common/header.php | 8 +- .../apps/tools/views/common/printRoutes.php | 3 +- .../recess/apps/tools/views/routes/home.php | 2 +- recess/lib/recess/cache/Cache.class.php | 53 ++-- .../recess/framework/Application.class.php | 2 +- .../recess/framework/Coordinator.class.php | 2 +- .../recess/framework/DefaultPolicy.class.php | 2 +- .../framework/interfaces/IPolicy.class.php | 2 +- .../recess/framework/routing/Route.class.php | 2 - .../lib/recess/framework/routing/Rt.class.php | 19 ++ .../recess/framework/routing/RtNode.class.php | 227 ++++++++++++++++++ recess/lib/recess/lang/Library.class.php | 6 +- .../lib/recess/sources/db/orm/Model.class.php | 2 - recess/sqlite/default.db | Bin 31744 -> 31744 bytes ...odeTest.class.php => RtNodeTest.class.php} | 9 +- ...t.class.php => RecessObjectTest.class.php} | 0 .../recess/sources/db/orm/ModelTest.class.php | 1 - 27 files changed, 622 insertions(+), 133 deletions(-) delete mode 100644 apps/blog/controllers/PostsController.class.php create mode 100644 phpinfo.php create mode 100644 recess/lib/recess/apps/tools/views/apps/createModel.php create mode 100644 recess/lib/recess/apps/tools/views/apps/newModelJQuery.php create mode 100644 recess/lib/recess/framework/routing/Rt.class.php create mode 100644 recess/lib/recess/framework/routing/RtNode.class.php rename recess/test/lib/recess/framework/routing/{RoutingNodeTest.class.php => RtNodeTest.class.php} (91%) rename recess/test/lib/recess/lang/{RecessClassTest.class.php => RecessObjectTest.class.php} (100%) diff --git a/TestRunner.php b/TestRunner.php index 8545787..a32ccbb 100644 --- a/TestRunner.php +++ b/TestRunner.php @@ -11,7 +11,7 @@ function __construct() { $this->TestSuite('All Tests'); $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/lang/InflectorTest.class.php'); $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/lang/RecessObjectTest.class.php'); - $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/framework/routing/RoutingNodeTest.class.php'); + $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/framework/routing/RtNodeTest.class.php'); $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/sources/db/sql/SelectSqlBuilderTest.class.php'); $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/sources/db/pdo/PdoDataSetTest.class.php'); $this->addFile(dirname(__FILE__) . '/recess/test/lib/recess/sources/db/pdo/SqlitePdoDataSourceTest.class.php'); diff --git a/apps/blog/controllers/PostsController.class.php b/apps/blog/controllers/PostsController.class.php deleted file mode 100644 index 8bdf534..0000000 --- a/apps/blog/controllers/PostsController.class.php +++ /dev/null @@ -1,63 +0,0 @@ -latestPosts = Make::a('Post')->find()->orderBy('id DESC')->range(0,5); - - } - - /** !Route GET, new */ - function newPost () { } - - /** !Route POST, new */ - function createNewPost () { - - $post = new Post(); - $post->copy($this->request->post); - $post->created = $post->modified = date( 'Y-m-d H:i:s', time() ); - $post->save(); - - return $this->created('/blog/posts/' . $post->id, '/blog/'); - - } - - /** !Route GET, comments/$postId */ - function comments($postId) { - - $this->post = Make::a('Post')->equal('id',$postId)->first(); - $this->comments = $this->post->comments(); - - } - - /** !Route POST, comments/$postId */ - function newComment($postId) { - - $comment = Make::a('Comment')->copy($this->request->post); - $comment->post_id = $postId; - $comment->insert(); - - return $this->created('/blog/comment/' . $comment->id, '/blog/comments/' . $postId); - - } - - /** !Route GET, comment/$commentId/delete/ */ - function deleteComment($commentId) { - $comment = new Comment(); - $comment->id = $commentId; - $post = $comment->post(); - $comment->delete(); - Library::import('recess.http.ForwardingResponse'); - return $this->forwardOk('/blog/comments/' . $post->id); - } - -} - -?> \ No newline at end of file diff --git a/blueprint.html b/blueprint.html index 32786a1..0d7085b 100644 --- a/blueprint.html +++ b/blueprint.html @@ -14,10 +14,27 @@ window.onload = function() { dp.SyntaxHighlighter.ClipboardSwf = '/content/flash/clipboard.swf'; dp.SyntaxHighlighter.HighlightAll('code'); - } - - addField = function() { - $("modelForm").append('

Hello

'); + + function addField() { + $("#modelForm .removeField:last").unbind('blur'); + $("#rowTemplate").children().clone().appendTo("#modelForm"); + $("#modelForm .removeField:last").click(function() { $(this).parent().parent().remove(); }); + setFocus(); + } + + function setFocus() { + $("#modelForm .removeField:last").blur( addField ); + $("#modelForm .fieldName:last").focus(); + } + + $(".addField").click( addField ); + + setFocus(); + + $("#modelForm .removeField:last").click(function() { $(this).parent().parent().remove(); }); + + $("#modelForm").children().clone().appendTo("#rowTemplate"); + } @@ -56,24 +73,51 @@

Quick Links

Package: recess.lang

-
-
+ + + + + + + + + + + + + + + + + + + + + +
Property NameTypeNullable?Default ValueRemove
+
- Add Field - - + + + +
-

RecessObject extends StdClass

-
<?php 
-	class RecessObject extends StdClass {
-		public $array = array('john','jack');
-		function __construct() {
-			echo 'hello world!';
-		}
-	}
-?>
-
diff --git a/bootstrap.php b/bootstrap.php index 5f2b817..e61282c 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -18,5 +18,5 @@ Library::import('recess.framework.Coordinator'); Library::import('recess.http.Environment'); -Coordinator::main(Environment::getRawRequest(), Config::$policy, Config::$applications, Config::getRouter(), Config::$plugins); +Coordinator::main(Environment::getRawRequest(), Config::$policy, Config::$applications, Config::getRoutes(), Config::$plugins); ?> \ No newline at end of file diff --git a/phpinfo.php b/phpinfo.php new file mode 100644 index 0000000..968c8df --- /dev/null +++ b/phpinfo.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/recess-config.php b/recess-config.php index a17fa11..1161aba 100644 --- a/recess-config.php +++ b/recess-config.php @@ -37,11 +37,7 @@ 'Sqlite', ); -Config::$useTurboSpeed = true; // I wanna go FAST! (Note: Experimental feature.) - -//Config::$plugins -// = array( 'recess.framework.plugins.ContentCaching' -// ); +Config::$useTurboSpeed = false; // I wanna go FAST! (Note: Experimental feature.) /* END OF BASIC CONFIGURATION SETTINGS */ @@ -123,17 +119,19 @@ static function init() { self::$policy = new DefaultPolicy(); } - static function getRouter() { - Library::import('recess.framework.routing.RoutingNode'); + const ROUTES_CACHE_KEY = 'Recess::Routes'; + + static function getRoutes() { + Library::import('recess.framework.routing.RtNode'); Library::import('recess.framework.routing.Route'); - $router = Cache::get('router'); + $router = Cache::get(self::ROUTES_CACHE_KEY); if($router === false) { - $router = new RoutingNode(); + $router = new RtNode(); foreach(self::$applications as $app) { $app->addRoutesToRouter($router); } - Cache::set('router', $router); + Cache::set(self::ROUTES_CACHE_KEY, $router); } return $router; diff --git a/recess/lib/recess/apps/tools/controllers/RecessToolsAppsController.class.php b/recess/lib/recess/apps/tools/controllers/RecessToolsAppsController.class.php index cef2da0..0f9cb14 100644 --- a/recess/lib/recess/apps/tools/controllers/RecessToolsAppsController.class.php +++ b/recess/lib/recess/apps/tools/controllers/RecessToolsAppsController.class.php @@ -162,9 +162,7 @@ private function getNewAppStep2Form($fillValues = array()) { $form->fill($fillValues); return $form; } - - /** !Route GET, $appClass */ public function app($appClass) { $application = $this->getApplication($appClass); @@ -175,12 +173,12 @@ public function app($appClass) { $this->app = $application; } - /** !Route GET, gen/model */ + /** !Route GET, model/gen */ public function createModel() { } - /** !Route GET, gen/controller */ + /** !Route GET, controller/gen */ public function createController() { } diff --git a/recess/lib/recess/apps/tools/views/apps/app.php b/recess/lib/recess/apps/tools/views/apps/app.php index 8e90d3e..88984f6 100644 --- a/recess/lib/recess/apps/tools/views/apps/app.php +++ b/recess/lib/recess/apps/tools/views/apps/app.php @@ -50,7 +50,7 @@ function printClassesInNamespace($namespace, $codeController) {

Routes

Route Prefix: routingPrefix; ?>

addRoutesToRouter($routes); include_once($viewsDir . 'common/printRoutes.php'); printRoutes($routes, $codeController); diff --git a/recess/lib/recess/apps/tools/views/apps/createModel.php b/recess/lib/recess/apps/tools/views/apps/createModel.php new file mode 100644 index 0000000..9e7eb82 --- /dev/null +++ b/recess/lib/recess/apps/tools/views/apps/createModel.php @@ -0,0 +1,168 @@ + +

New Model Helper

+

The purpose of this helper is to help speed the process of creating Recess! Models. Please note this form is not child proof!

+
+

Step 1) Name Your Model

+ +

The name of your model must be a valid PHP class name.

+
+

Step 2) Pick Your Database Table

+
+
+

Table does not exist.

+ + + + + + + + + + + + + +
+
+
+

Table already exists.

+ + + + + + + + + +
+
+
+
+

Step 3) Properties

+ + + + + + + + + + + + + + + + + + + +
Property NameTypeNullable?Default ValueRemove
+ +

+
+

Step 4) Relationships

+ + + + + + + + + + + + + + + + + + + +
Relation NameTypeRelated ClassAdvanced OptionsRemove
+ Show + + + + + + + + + + + + + +
Foreign Key: + +
Through: + + +
On Delete: + +
+
+ +
+ + + +
+ + + +
+ \ No newline at end of file diff --git a/recess/lib/recess/apps/tools/views/apps/newModelJQuery.php b/recess/lib/recess/apps/tools/views/apps/newModelJQuery.php new file mode 100644 index 0000000..3f5293c --- /dev/null +++ b/recess/lib/recess/apps/tools/views/apps/newModelJQuery.php @@ -0,0 +1,69 @@ + + \ No newline at end of file diff --git a/recess/lib/recess/apps/tools/views/common/header.php b/recess/lib/recess/apps/tools/views/common/header.php index 7bfd538..4dec0d4 100644 --- a/recess/lib/recess/apps/tools/views/common/header.php +++ b/recess/lib/recess/apps/tools/views/common/header.php @@ -31,7 +31,13 @@ dp.SyntaxHighlighter.HighlightAll('code'); } - + <?php if(isset($title)) echo $title; else echo 'Recess! Tools!'; ?> diff --git a/recess/lib/recess/apps/tools/views/common/printRoutes.php b/recess/lib/recess/apps/tools/views/common/printRoutes.php index 0978d57..e947e9d 100644 --- a/recess/lib/recess/apps/tools/views/common/printRoutes.php +++ b/recess/lib/recess/apps/tools/views/common/printRoutes.php @@ -17,7 +17,8 @@ function printRoutesRecursive($codeController, $routingNode, $fullPath) { $parametricPaths = $routingNode->getParametricPaths(); $methods = $routingNode->getMethods(); if(!empty($methods)) { - foreach($methods as $method => $route) { + foreach($methods as $method => $rt) { + $route = $rt->toRoute(); $i++; if($i % 2 == 0) { echo ''; diff --git a/recess/lib/recess/apps/tools/views/routes/home.php b/recess/lib/recess/apps/tools/views/routes/home.php index f903fb5..cbc43c8 100644 --- a/recess/lib/recess/apps/tools/views/routes/home.php +++ b/recess/lib/recess/apps/tools/views/routes/home.php @@ -6,7 +6,7 @@

Routes

request->meta->app); diff --git a/recess/lib/recess/cache/Cache.class.php b/recess/lib/recess/cache/Cache.class.php index 9038247..553c784 100644 --- a/recess/lib/recess/cache/Cache.class.php +++ b/recess/lib/recess/cache/Cache.class.php @@ -116,11 +116,15 @@ class SqliteCacheProvider implements ICacheProvider { protected $pdo; protected $setStatement; protected $getStatement; + protected $getManyStatement; protected $deleteStatement; protected $time; + + protected $entries = array(); const VALUE = 0; const EXPIRE = 1; + const KEY = 2; function __construct() { $this->pdo = new Pdo('sqlite:' . $_ENV['dir.temp'] . 'sqlite-cache.db'); @@ -128,11 +132,13 @@ function __construct() { try { $this->setStatement = $this->pdo->prepare('INSERT OR REPLACE INTO cache (key,value,expire) values (:key,:value,:expire)'); $this->getStatement = $this->pdo->prepare('SELECT value,expire FROM cache WHERE key = :key'); + $this->getManyStatement = $this->pdo->prepare('SELECT value,expire,key FROM cache WHERE key LIKE :key'); } catch(PDOException $e) { $this->pdo->exec('CREATE TABLE "cache" ("key" TEXT PRIMARY KEY NOT NULL , "value" TEXT NOT NULL , "expire" INTEGER NOT NULL)'); $this->pdo->exec('CREATE INDEX "expiration" ON "cache" ("expire" ASC)'); $this->setStatement = $this->pdo->prepare('INSERT OR REPLACE INTO cache (key,value,expire) values (:key,:value,:expire)'); $this->getStatement = $this->pdo->prepare('SELECT value,expire FROM cache WHERE key = :key'); + $this->getManyStatement = $this->pdo->prepare('SELECT value,expire,key FROM cache WHERE key LIKE :key'); } $this->time = time(); } @@ -152,8 +158,9 @@ function reportsTo(ICacheProvider $cache) { } function set($key, $value, $duration = 0) { - $this->setStatement->execute(array(':key' => $key, ':value' => var_export($value, true), ':expire' => $duration == 0 ? 0 : time() + $duration)); + $this->setStatement->execute(array(':key' => $key, ':value' => serialize($value), ':expire' => $duration == 0 ? 0 : time() + $duration)); $this->reportsTo->set($key, $value, $duration); + $this->entries[$key] = $value; } function clearStaleEntries() { @@ -161,23 +168,42 @@ function clearStaleEntries() { } function get($key) { - $this->getStatement->execute(array(':key' => $key)); - $result = $this->getStatement->fetch(PDO::FETCH_NUM); + if(isset($this->entries[$key])) { + return $this->entries[$key]; + } + + if(($starPos = strpos($key,'*')) === false) { + // Fetch Single + $this->getStatement->execute(array(':key' => $key)); + $entries = $this->getStatement->fetchAll(PDO::FETCH_NUM); + } else { + // Prefetch With Wildcard + $this->getManyStatement->execute(array(':key' => substr($key,0,$starPos+1) . '%')); + $entries = $this->getManyStatement->fetchAll(PDO::FETCH_NUM); + } - if($result !== false) { - if($result[self::EXPIRE] == 0 || $result[self::EXPIRE] <= $this->time) { - echo $key . ' '; - echo $result[self::VALUE]; - echo '

'; - eval('$result = ' . $result[self::VALUE] . ';'); + $clearStaleEntries = false; + foreach($entries as $entry) { + if($entry[self::EXPIRE] == 0 || $entry[self::EXPIRE] <= $this->time) { + if(isset($entry[self::KEY])) { + $this->entries[$entry[self::KEY]] = unserialize($entry[self::VALUE]); + } else { + $this->entries[$key] = unserialize($entry[self::VALUE]); + } } else { - $this->clearStaleEntries(); + $clearStaleEntries = true; } - } else { - $result = $this->reportsTo->get($key); } - return $result; + if($clearStaleEntries) { + $this->clearStaleEntries(); + } + + if(isset($this->entries[$key])) { + return $this->entries[$key]; + } else{ + return $this->reportsTo->get($key); + } } function delete($key) { @@ -193,5 +219,4 @@ function clear() { $this->reportsTo->clear(); } } - ?> \ No newline at end of file diff --git a/recess/lib/recess/framework/Application.class.php b/recess/lib/recess/framework/Application.class.php index 8bb3cc5..e09e9f1 100644 --- a/recess/lib/recess/framework/Application.class.php +++ b/recess/lib/recess/framework/Application.class.php @@ -16,7 +16,7 @@ abstract class Application { public $routingPrefix = '/'; - function addRoutesToRouter(RoutingNode $router) { + function addRoutesToRouter(RtNode $router) { $classes = Library::findClassesIn($this->controllersPrefix); foreach($classes as $class) { diff --git a/recess/lib/recess/framework/Coordinator.class.php b/recess/lib/recess/framework/Coordinator.class.php index c0f4c84..f41f2e2 100644 --- a/recess/lib/recess/framework/Coordinator.class.php +++ b/recess/lib/recess/framework/Coordinator.class.php @@ -16,7 +16,7 @@ final class Coordinator { * @package recess * @static */ - public static function main(Request $request, IPolicy $policy, array $apps, RoutingNode $routes, array $plugins = array()) { + public static function main(Request $request, IPolicy $policy, array $apps, RtNode $routes, array $plugins = array()) { static $callDepth = 0; static $calls = array(); $callDepth++; diff --git a/recess/lib/recess/framework/DefaultPolicy.class.php b/recess/lib/recess/framework/DefaultPolicy.class.php index 237c83a..1eb5502 100644 --- a/recess/lib/recess/framework/DefaultPolicy.class.php +++ b/recess/lib/recess/framework/DefaultPolicy.class.php @@ -27,7 +27,7 @@ public function preprocess(Request &$request) { return $request; } - public function getControllerFor(Request &$request, array $applications, RoutingNode $routes) { + public function getControllerFor(Request &$request, array $applications, RtNode $routes) { $routeResult = $routes->findRouteFor($request); diff --git a/recess/lib/recess/framework/interfaces/IPolicy.class.php b/recess/lib/recess/framework/interfaces/IPolicy.class.php index cff2229..069950d 100644 --- a/recess/lib/recess/framework/interfaces/IPolicy.class.php +++ b/recess/lib/recess/framework/interfaces/IPolicy.class.php @@ -3,7 +3,7 @@ interface IPolicy { public function preprocess(Request &$request); - public function getControllerFor(Request &$request, array $applications, RoutingNode $routes); + public function getControllerFor(Request &$request, array $applications, RtNode $routes); public function getViewFor(Response &$response); } diff --git a/recess/lib/recess/framework/routing/Route.class.php b/recess/lib/recess/framework/routing/Route.class.php index 320db9d..01cd8e5 100644 --- a/recess/lib/recess/framework/routing/Route.class.php +++ b/recess/lib/recess/framework/routing/Route.class.php @@ -13,7 +13,6 @@ class Route { public $app; public $methods = array(); public $path; - public $args = array(); public function __construct($class, $function, $methods, $path) { $this->class = $class; @@ -27,7 +26,6 @@ public function __construct($class, $function, $methods, $path) { public static function __set_state($array) { $route = new Route($array['class'], $array['function'], $array['methods'], $array['path']); $route->app = $array['app']; - $route->args = $array['args']; return $route; } } diff --git a/recess/lib/recess/framework/routing/Rt.class.php b/recess/lib/recess/framework/routing/Rt.class.php new file mode 100644 index 0000000..d5c9146 --- /dev/null +++ b/recess/lib/recess/framework/routing/Rt.class.php @@ -0,0 +1,19 @@ +c = Library::getClassName($route->class); + $this->f = $route->function; + $this->a = $route->app; + } + + function toRoute() { + $route = new Route(Library::getFullyQualifiedClassName($this->c),$this->f,array(),''); + $route->app = $this->a; + return $route; + } +} +?> \ No newline at end of file diff --git a/recess/lib/recess/framework/routing/RtNode.class.php b/recess/lib/recess/framework/routing/RtNode.class.php new file mode 100644 index 0000000..fd8cbea --- /dev/null +++ b/recess/lib/recess/framework/routing/RtNode.class.php @@ -0,0 +1,227 @@ + matches /pages/ + * /pages/$id -> matches /pages/1 ... (id => 1) + * /pages/slug/$slug -> matches /pages/slug/some-slug-here (slug => some-slug-here) + * + * For the purposes of this class a URI path is broken into parts delimited + * with a '/'. There are two kinds of path parts: static and parametric. Static matches + * have precedence over parametric matches. For example, if you have the following routes: + * + * (1) /pages/$page_title/ + * (2) /pages/a-page/ + * (3) /pages/$page_title/$id + * + * A request of "/pages/a-page/" will match (2) and the result will not contain an argument. + * A request of "/pages/b-page/" will match (1) and the result will contain argument ("page_title" => "b_page") + * A request of "/pages/a-page/1" will match (3) with result arguments ("page_title" => "a_page", "id" => "1") + * + * Note: Because routing trees are serialized and unserialized frequently I am breaking the naming + * conventions and using short, one-letter member names. + * + * @todo Add regular expression support to the parametric parts (/pages/:id(regexp-goes-here?)/) + * + * @author Kris Jordan + * @copyright Copyright (c) 2008, Kris Jordan + * @package recess.routing + */ +class RtNode { + + protected $c = ''; // (c)ondition + protected $m; // (m)ethods + protected $s; // (s)tatic children + protected $p; // (d)ynamic children + + /** + * Used to add a route to the routing tree. + * + * @param Route The route to add to this routing tree. + */ + public function addRoute($app, Route $route, $prefix) { + if($route->path == '') return; + + $route = clone $route; + + $route->app = $app; + + if($route->path[0] != '/') { + if(substr($route->path,-1) != '/') { + $route->path = $prefix . '/' . trim($route->path); + }else{ + $route->path = $prefix . trim($route->path); + } + } + + $pathParts = $this->getRevesedPathParts($route->path); + $this->addRouteRecursively($pathParts, count($pathParts) - 1, $route); + } + + /** + * The recursive method powering addRouteFor(Request). + * + * @param array Part of a path in reverse order. + * @param int Current index of path part array - decrements with each step. + * @param Route The route being added + * + * @return FindRouteResult + */ + private function addRouteRecursively(&$pathParts, $index, $route) { + // Base Case + if($index < 0) { + foreach($route->methods as $method) { + if(isset($this->m[$method])) { + throw new RecessException('Conflicting routes, the route: "' . $route->path . '" is defined twice.', get_defined_vars()); + } + $this->m[$method] = new Rt($route); + } + return; + } + + $nextPart = $pathParts[$index]; + + if($nextPart[0] != '$') { + $childrenArray = &$this->s; + $nextKey = $nextPart; + $isParam = false; + } else { + $childrenArray = &$this->p; + $nextKey = substr($nextPart, 1); + $isParam = true; + } + + if(!isset($childrenArray[$nextKey])) { + $child = new RtNode(); + if($isParam) { + $child->c = $nextKey; + } + $childrenArray[$nextKey] = $child; + } else { + $child = $childrenArray[$nextKey]; + } + + $child->addRouteRecursively($pathParts, $index - 1, $route); + } + + /** + * Traverses children recursively to find a matching route. First looks + * to see if a static (non-parametric, i.e. /this_is_static/ vs. /$this_is_dynamic/) + * match exists. If not, we match against dynamic children. We reverse and step backwards + * through the array because $index > 0 is less costly than $index < count($parts) + * in PHP. + * + * @param Request The recess.http.Request object to find a matching route for. + * + * @return RoutingResult + */ + public function findRouteFor(Request $request) { + $pathParts = $this->getRevesedPathParts($request->resource); + return $this->findRouteRecursively($pathParts, count($pathParts) - 1, $request->method); + } + + /** + * The recursive method powering findRouteFor(Request). + * + * @param array Part of a path in reverse order. + * @param int Current index of path part array - decrements with each step. + * @param string The HTTP METHOD desired for this route. + * + * @return RoutingResult + */ + private function findRouteRecursively(&$pathParts, $index, &$method) { + // Base Case - We've gone to the end of the path. + if($index < 0) { + $result = new RoutingResult(); + if(!empty($this->m)) { // Leaf, now check HTTP Method Match + if(isset($this->m[$method])) { + $result->routeExists = true; + $result->methodIsSupported = true; + $result->route = $this->m[$method]->toRoute(); + } else { + $result->routeExists = true; + $routes = array_values($this->m); + $result->route = $routes[0]->toRoute(); + $result->route->methods = array_values($this->m); + $result->methodIsSupported = false; + $result->acceptableMethods = array_keys($this->m); + } + } else { // Non-leaf, no match + $result->routeExists = false; + } + return $result; + } + + // Find a child for the next part of the path. + $nextPart = &$pathParts[$index]; + + $result = new RoutingResult(); + + // Check for a static match + if(isset($this->s[$nextPart])) { + $child = $this->s[$nextPart]; + $result = $child->findRouteRecursively($pathParts, $index - 1, $method); + } + + if(!$result->routeExists && !empty($this->p)) { + foreach($this->p as $child) { + if($child->matches($nextPart)) { + $result = $child->findRouteRecursively($pathParts, $index - 1, $method); + if($result->routeExists) { + if($child->c != '') { + $result->arguments[$child->c] = $nextPart; + } + return $result; + } + } + } + } + + return $result; + } + + public function getStaticPaths() { + if(is_array($this->s)) return $this->s; + else return array(); + } + + public function getParametricPaths() { + if(is_array($this->p)) return $this->p; + else return array(); + } + + public function getMethods() { + if(is_array($this->m)) return $this->m; + else return array(); + } + + public function matches($path) { + // TODO: Add regexp support + return $path != ''; + } + + public static function __set_state($array) { + $node = new RtNode(); + $node->c = $array['c']; + $node->m = $array['m']; + $node->s = $array['s']; + $node->p = $array['p']; + return $node; + } + + // Helper Methods + + /** + * Explodes a string by forward slashes, removes empty first/last node + * and finally reverses the array. + * @param string Path to be split and reversed. + */ + private function getRevesedPathParts($path) { + return array_reverse(array_filter(explode('/', $path))); + } +} +?> \ No newline at end of file diff --git a/recess/lib/recess/lang/Library.class.php b/recess/lib/recess/lang/Library.class.php index a07c453..7f4d630 100644 --- a/recess/lib/recess/lang/Library.class.php +++ b/recess/lib/recess/lang/Library.class.php @@ -25,9 +25,9 @@ class Library { const dotSeparator = '.'; const pathSeparator = '/'; - const CLASSES_X_CLASS_CACHE_KEY = 'Library::$classesByClass'; - const CLASSES_X_FULL_CACHE_KEY = 'Library::$classesByFull'; - const PATHS_CACHE_KEY = 'Library::$paths'; + const CLASSES_X_CLASS_CACHE_KEY = 'Recess::*::Library::$classesByClass'; + const CLASSES_X_FULL_CACHE_KEY = 'Recess::*::Library::$classesByFull'; + const PATHS_CACHE_KEY = 'Recess::*::Library::$paths'; const NAMED_DIRS_PATH = 'namedRuns/'; const PHP_EXTENSION = '.php'; diff --git a/recess/lib/recess/sources/db/orm/Model.class.php b/recess/lib/recess/sources/db/orm/Model.class.php index 80cacd9..2067ee0 100644 --- a/recess/lib/recess/sources/db/orm/Model.class.php +++ b/recess/lib/recess/sources/db/orm/Model.class.php @@ -1,6 +1,4 @@ C_yK_U0gxL3*|8ke3jx)$@(U7Z3IhTp00V#modP7YF$5L@A9jIN2mmnw z0bOYXLTqnmNN;UrLvL<$a&K&GWpZu=laVGKlO1acla>(`9du=Fa9nO-ZDnLqb#8N9 Wa%E#>b8{|mXmCk3-QcsGc4Gyv+$byn delta 40 ycmV+@0N4M3_yK_U0gxL3*0CJb3jx%#@(U7Z2m%6~00V#mowG3nkpi=wc4GxYwGNd4 diff --git a/recess/test/lib/recess/framework/routing/RoutingNodeTest.class.php b/recess/test/lib/recess/framework/routing/RtNodeTest.class.php similarity index 91% rename from recess/test/lib/recess/framework/routing/RoutingNodeTest.class.php rename to recess/test/lib/recess/framework/routing/RtNodeTest.class.php index ab0619e..ca40d58 100644 --- a/recess/test/lib/recess/framework/routing/RoutingNodeTest.class.php +++ b/recess/test/lib/recess/framework/routing/RtNodeTest.class.php @@ -1,18 +1,18 @@ node = new RoutingNode(); + $this->node = new RtNode(); $this->routes = array( 'MethodA' => new Route('Controller','MethodA','GET','/controller/methoda/'), 'MethodB_POST' => new Route('Controller','MethodB_POST','POST','/controller/methodb/1'), @@ -37,7 +37,6 @@ function testFindOnSingleRoute() { $request->setResource('/controller/methoda/'); $routeResult = $this->node->findRouteFor($request); $this->assertTrue($routeResult->routeExists); - $this->assertEqual($routeResult->route, $this->routes['MethodA']); } function testFindFailOnSingleRoute() { diff --git a/recess/test/lib/recess/lang/RecessClassTest.class.php b/recess/test/lib/recess/lang/RecessObjectTest.class.php similarity index 100% rename from recess/test/lib/recess/lang/RecessClassTest.class.php rename to recess/test/lib/recess/lang/RecessObjectTest.class.php diff --git a/recess/test/lib/recess/sources/db/orm/ModelTest.class.php b/recess/test/lib/recess/sources/db/orm/ModelTest.class.php index eb6186b..3d6ef75 100644 --- a/recess/test/lib/recess/sources/db/orm/ModelTest.class.php +++ b/recess/test/lib/recess/sources/db/orm/ModelTest.class.php @@ -1,5 +1,4 @@