Skip to content

Instantly share code, notes, and snippets.

@simonwelsh
Created April 30, 2014 01:11
Show Gist options
  • Save simonwelsh/b926dc1e8929529c9724 to your computer and use it in GitHub Desktop.
Save simonwelsh/b926dc1e8929529c9724 to your computer and use it in GitHub Desktop.
<?hh // strict
namespace beatbox;
type Path = ImmVector<string>;
type Extension = ?string;
type Metadata = Map<string, mixed>;
type FragmentHandler = (function(Path, Extension, Metadata): mixed);
type CheckerCallback = (function(string, Metadata): bool);
type FragmentTable = Map<string, FragmentHandler>;
type CheckerTable = Map<string, CheckerCallback>;
type PathTable = Pair<FragmentTable, Metadata>;
type RouteTable = Map<string, PathTable>;
type SimpleRouteTable = Map<string, FragmentTable>;
newtype RequestStackItem = (Path, Extension, FragmentTable, Metadata);
newtype RequestStack = Vector<RequestStackItem>;
class Router {
protected static RouteTable $_straight_routes = Map {};
protected static RouteTable $_regex_routes = Map {};
protected static CheckerTable $_checkers = Map {};
protected static RequestStack $_stack = Vector {};
/**
* Route the url, generating the fragments
*/
public static function route(string $url, \Traversable<string> $fragments = Vector {}) : mixed {
// Get extension
if (strpos($url, '.') === false) {
$ext = '';
} else {
list($url, $ext) = explode($url, '.', 2);
}
// Get URL parts
$url = trim($url, '/');
$parts = explode($url, '/') ?: ['/'];
$parts = Vector::fromArray($parts);
// Get fragments
$fragments = new ImmVector($fragments);
if ($fragments->count() == 0) {
$fragments = ImmVector {'page'};
}
// Get potential routes and MD
$url = $parts->toImmVector();
$allPaths = Vector {};
do {
$path = implode('/', $parts);
$allPaths[] = $path;
$parts->pop();
} while(!$parts->isEmpty());
$allPaths[] = '';
$allPaths->reverse();
$paths = Map {};
$metadata = Map {};
$allPaths->map((string $path) ==> {
$p = static::get_routes_for_path($path);
if ($p) {
$paths->setAll($p[0]);
$metadata->setAll($p[1]);
}
});
self::$_stack->add(tuple($url, $ext, $paths, $metadata));
}
/**
* Add routes with metadata
*/
public static function add_routes(RouteTable $routes, bool $regex = false) : void {
if ($regex) {
$map = self::$_regex_routes;
} else {
$map = self::$_straight_routes;
}
foreach ($routes as $path => $table) {
$path = self::sanitise_path($path);
if (!$map->contains($path)) {
$map[$path] = Pair { Map {}, Map {} };
}
// Add the fragment handlers
$map[$path][0]->setAll($table[0]);
// Add the metadata
$map[$path][1]->setAll($table[1]);
}
}
/**
* Add routes without metadata
*/
public static function add_simple_routes(SimpleRouteTable $routes, bool $regex = false): void {
static::add_routes($routes->map((FragmentTable $t) ==> Pair {$t, Map {} }), $regex);
}
/**
* Gets the routes available for a given path
*/
public static function get_routes_for_path(string $path): ?PathTable {
$path = self::sanitise_path($path);
if (self::$_straight_routes->contains($path)) {
return self::$_straight_routes[$path];
}
if ($path) {
foreach (self::$_regex_routes as $p => $table) {
if (preg_match('#^' . $p . '$#', $path)) {
return $table;
}
}
}
return null;
}
/**
* Add a checker for the given metadata key
*/
public static function add_checker(string $key, CheckerCallback $callback) : void {
self::$_checkers[strtolower($key)] = $callback;
}
/**
* Return the checker for the given metadata key
*/
public static function get_checker(string $key) : ?CheckerCallback {
return self::$_checkers->get(strtolower($key));
}
public static function response_for_fragment(string $frag) : mixed {
}
public static function current_path() : ?ImmVector<string> {
if (!self::$_stack->isEmpty()) {
$index = self::$_stack->count() - 1;
return self::$_stack[$index][0];
}
return null;
}
protected static function reset(): void {
self::$_straight_routes = Map {};
self::$_regex_routes = Map {};
self::$_checkers = Map {};
self::$_stack = Vector {};
}
private static function sanitise_path(string $path): string {
if (strlen($path)) {
$path = trim($path, '/') ?: '/';
}
return $path;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment