Dromeo: A simple, generic router for PHP, Python, Node/XPCOM/JS, ActionScript
JavaScript PHP Python
Latest commit 8156c87 Dec 10, 2016 @foo123 c,java versions (todo)
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