Dromeo: A simple, generic router for PHP, Python, Node/XPCOM/JS, ActionScript
JavaScript PHP Python
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
src
test
README.md
dromeo.jpg

README.md

Dromeo

A simple and flexible pattern routing framework for PHP, Python, Node/XPCOM/JS, ActionScript (TODO)

Dromeo

Etymology of "dromos" (path) Etymology pf "path"

Dromeo.js

see also:

  • Tao A simple, tiny, isomorphic, precise and fast template engine for handling both string and live dom based templates
  • ModelView light-weight, isomorphic & flexible MVVM framework for JavaScript/HTML5
  • ModelView MVC jQueryUI Widgets plug-n-play, state-full, full-MVC widgets for jQueryUI using modelview.js (e.g calendars, datepickers, colorpickers, tables/grids, etc..) (in progress)
  • Contemplate a light-weight template engine for Node/XPCOM/JS, PHP, Python, ActionScript
  • HtmlWidget html widgets used as (template) plugins and/or standalone for PHP, Node/XPCOM/JS, Python both client and server-side
  • Importer simple class & dependency manager and loader for PHP, Node/XPCOM/JS, Python
  • PublishSubscribe a simple and flexible publish-subscribe pattern implementation for Node/XPCOM/JS, PHP, Python, ActionScript
  • Regex Analyzer/Composer Regular Expression Analyzer and Composer for Node/XPCOM/JS, PHP, Python, ActionScript
  • DateX eXtended & localised Date parsing, diffing, formatting and validation for Node/XPCOM/JS, Python, PHP
  • GrammarTemplate versatile and intuitive grammar-based templating for PHP, Python, Node/XPCOM/JS, ActionScript
  • Xpresion eXpression parser engine (with custom functions & variables support) for PHP, Python, Node/XPCOM/JS, ActionScript
  • Dialect cross-platform SQL construction for PHP, Python, Node/XPCOM/JS
  • Simulacra a simulation, algebraic, probability and combinatorics PHP package for scientific computations
  • RT client-side real-time communication for Node/XPCOM/JS with support for Poll/BOSH/WebSockets
  • Asynchronous a simple manager for async, linearised, parallelised, interleaved & sequential tasks for JavaScript

Example:

var path = require('path'), 
    Dromeo = require(path.join(__dirname, '../src/js/Dromeo.js')),
    echo = console.log
;

function routeHandler( params )
{
    echo('Route Handler Called');
    echo('Route: ' + params['route']);
    echo('Params: ');
    echo( params['data'] );
}

function fallbackHandler( params )
{
    echo('Fallback Handler Called');
    echo('Route: ' + params['route']);
    echo('Params: ');
    echo( params['data'] );
}

echo( 'Dromeo.VERSION = ' + Dromeo.VERSION );
echo( );

var dromeo = new Dromeo( );

dromeo
    
    .on(
      
      {
      // route pattern
      route: 'http://abc.org/{%ALPHA%:group}/{%ALNUM%:user}/{%NUMBR%:id}{/%moo|soo|too%:?foo(1)}{%ALL%:?rest}', 
      // method, default is '*', any
      //method: '*',
      // route handler
      handler: routeHandler, 
      // default params (if any)
      defaults: {'foo':'moo','extra-flag','extra'},
      // optional type-casting for certain matches
      types: {'id': Dromeo.TYPE('INTEGER')}
      }
    
    )
    
    .fallback( fallbackHandler )
;

dromeo.route( 'http://abc.org/users/abcd12/23/soo' );

var uri = 'http://abc.org/path/to/page/?abcd%5B0%5D=1&abcd%5B1%5D=2&foo=1&moo%5Bsoo%5D=1&moo%5Btoo%5D=2#def%5B0%5D=1&def%5B1%5D=2&foo%5Bsoo%5D=1'
echo( );
echo( 'Parse URI: ' + uri );
echo( dromeo.parse( uri ) );

uri = 'http::/abc.org/path/to/page/';
echo( );
echo( 'Build URI' );
echo( dromeo.build(uri, {
    'abcd': [1, 2],
    'foo': 1,
    'moo': {'soo':1, 'too':2}
}, {
    'def': [1, 2],
    'foo': {'soo':1}
}) );

output:

Dromeo.VERSION = 0.6.4

Route Handler Called
Route: http://abc.org/users/abcd12/23/soo
Params: 
{ foo: 'soo',
  extra: 'extra',
  group: 'users',
  user: 'abcd12',
  id: 23,
  rest: null }

Parse URI: http://abc.org/path/to/page/?abcd%5B0%5D=1&abcd%5B1%5D=2&foo=1&moo%5Bsoo%5D=1&moo%5Btoo%5D=2#def%5B0%5D=1&def%5B1%5D=2&foo%5Bsoo%5D=1
{ fragment: 'def%5B0%5D=1&def%5B1%5D=2&foo%5Bsoo%5D=1',
  query: 'abcd%5B0%5D=1&abcd%5B1%5D=2&foo=1&moo%5Bsoo%5D=1&moo%5Btoo%5D=2',
  path: '/path/to/page/',
  host: 'abc.org',
  scheme: 'http',
  query_params: 
   { abcd: { '0': '1', '1': '2' },
     foo: '1',
     moo: { soo: '1', too: '2' } },
  fragment_params: { def: { '0': '1', '1': '2' }, foo: { soo: '1' } } }

Build URI
http::/abc.org/path/to/page/?abcd%5B0%5D=1&abcd%5B1%5D=2&foo=1&moo%5Bsoo%5D=1&moo%5Btoo%5D=2#def%5B0%5D=1&def%5B1%5D=2&foo%5Bsoo%5D=1

Route Patterns:

// Examples:
//

// match literal route
'http::/abc.org/'

// match route and capture the last numeric part into 'id' param
'http::/abc.org/{%NUMBR%:id}'

// same as previous, numeric 'id' part is optional
'http::/abc.org/{%NUMBR%:?id}'

// numeric part is optional but not captured (no param name given)
'http::/abc.org/{%NUMBR%:?}'

// numeric part is required but not captured (no param name given)
'http::/abc.org/{%NUMBR%:}'

// optional captured 'id' part is now the numeric pattern plus the leading '/'
'http::/abc.org{/%NUMBR%:?id}'

// optional captured 'id' part is only the numeric pattern without the leading '/', i.e group 1
'http::/abc.org{/%NUMBR%:?id(1)}'

/* etc.. */

Methods:

  • Dromeo is also a XPCOM JavaScript Component (Firefox) (e.g to be used in firefox browser addons/plugins)
// -- instance methods --
// --------------------------------------------------------

// optional route_prefix to be used in case all routes have a common prefix
// so can define routes using only the part that differs (easier/shorter code)
var router = new Dromeo( route_prefix='' );

// set/define delimiters used in route-patterns, see examples
router.defineDelimiters( ['{', '}', '%', '%', ':'] );

// define a (new) sub-pattern identified with className
// sub-patterns are used in route-patterns, 
// e.g "http://abc.org/{%ALNUM%:user}", "ALNUM" is an alpha-numeric sub-pattern, i.e "[a-zA-Z0-9\\-_]+"
// default sub-patterns:
// ALPHA =>   "[a-zA-Z\\-_]+"            alphabetic only
// NUMBR =>   "[0-9]+"                   numeric only
// INT   =>   "[0-9]+"                   integer with associated optional typecaster
// ALNUM =>   "[a-zA-Z0-9\\-_]+"         alpha-numeric only
// QUERY =>   "\\?[^?#]+"                query part with leading '?'
// FRAGMENT =>"#[^?#]+"                  hash/fragment part with leading '#'
// PART  =>   "[^\\/?#]+"                arbitrary path part (between /../)
// ALL   =>   ".+"                       arbitrary sequence
router.definePattern( className, subPattern [,typecaster=null] );

// unset/remove the sub-pattern "clasName"
router.dropPattern( className );

// define a custom type, to be used as (optional) typecaster for matching parts
router.defineType( type, typecaster );

// reset/remove routes and fallback handler
router.reset( );

// set/unset fallback handler
router.fallback( [handlerFunc | false | null] );

// set a handler for routePattern, with optional defaults object (oneOff if "one" used)
router.[on|one]( routeObj | routeObjs | routePattern, handler );
// route object configuration
//
//{
//    route: '..', // the route pattern matched, needed
//    method: 'post', // the method (case-insensitive), default is '*', i.e any
//    handler: function(params){/*..*/}, // the route handler to be called, needed
//    defaults: {/*..*/}, // any default and/or extra parameters to be used, if missing, and passed to handler, default is {}
//    types: {/*..*/} // optional typecasters for specific matches, i.e INTEGER, STRING, ARRAY, PARAMS or custom, default null
//}
//

// this also works:
router.[on|one]( routePattern, function(params){/*..*/} );

// set handler(s) for multiple routePattern(s) (oneOff if "one" used)

// using array of objects
router.[on|one]([ 
    routeObj1,
    routeObj2
    /* etc .. */
]);

// using variable arguments
router.[on|one]( 
    routeObj1,
    routeObj2
    /* etc .. */
);

// remove the routePattern (optionally if handlerFunc matches as well)
router.off( routePattern | routeObj [, handlerFunc=null] );

// redirect to given url (with optional statusCode and statusMsg)
// in Node, the **response object** from node.http should be passed as well
router.redirect( url, response [, statusCode=302, statusMsg=true] );

// parse and extract uri components and optional query/fragment params as objects (using RFC3986)
var components = router.parse( url [, query_p='query_params', fragment_p='fragment_params'] );

// parse/unglue a uri component into a params object (using RFC3986)
var params = router.unglue( uriComponent );

// build (url-encoded) url from baseUrl and query and/or hash objects (using RFC3986)
var url = router.build( baseUrl, query=null, hash=null );

// build/glue together a uri component from a params object (using RFC3986)
var component = router.glue( params );

// match and route a given url 
// (with optional method, only routes which match the method will be used), 
// returns true if matched any routePattern else false
var matched = router.route( url, method="*", breakOnFirstMatch=true );

TODO

  • add support for (http/request) method [DONE]
  • add support for extra passed defaults [DONE]
  • add support for (optional) type-casting of matched parameters [DONE]
  • add support for RFC 6570 URI Template specification