Created
March 16, 2013 09:44
-
-
Save fritz-gerneth/5175698 to your computer and use it in GitHub Desktop.
Suggested router API/Interface
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MVCRoute | |
{ | |
public function getController(); | |
} | |
class MVCDispatcher | |
{ | |
var template = router.match(request).getController(); | |
// render template into view, or pass it to view-package, ... | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The request and the router are decoupled. For the router it doesn't matter from where the current URI to match is actually from. This enables server-side routing for the same setup as well. (Using a different dispatcher of course). On a long-term the request probably would be a package itself abstracting client/server differences in a request. | |
The dispatcher is depending on the `doesMatch` method, and trigger recalculation of the view uppon invalidation. As we receive a callback we can invalidate controllers (being destructors for those, to unsubscribe/....). | |
The implementation of the routes doesn't matter for the dispatchers, why should they care about how the data was gathered. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Common interface for any kind of routes | |
*/ | |
interface RouteInterface | |
{ | |
/** | |
* Does this route match the request | |
* | |
* @reactive | |
*/ | |
public function doesMatch(request); | |
/** | |
* Match the route for the request | |
* | |
* @return RouteMatch | |
*/ | |
public function match(request); | |
/** | |
* Assemble a URI according matching this route (similiar to "to") | |
*/ | |
public function assemble(params = array()); | |
} | |
interface RouteStackInterface | |
{ | |
public function addRoute(RouteInterface); | |
public function doesMatch(request); | |
public function match(request); | |
} | |
/** | |
* Data container for information about the current route match. | |
* Changes in here do not trigger rebuild of the actual URI. | |
* This object is not reactive (as the layer above should be). | |
*/ | |
class RouteMatch | |
{ | |
/** | |
* Constructor | |
*/ | |
public RouteMatch(params); | |
/** | |
* Set the matching route | |
*/ | |
public function setMatchingRoute(RouteInterface route); | |
/** | |
* Get the matching route | |
*/ | |
public function getMatchingRoute(); | |
/** | |
* Params are those matched, i.e :id, :controller, ... | |
* routeMatch->getParam("id", 5); | |
*/ | |
public function setParam(name, value); | |
public function getParams(); | |
public function getParam(name, default = null); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For pagejs to direct navigation this might be: | |
class PageJSRouter | |
{ | |
// implementation | |
} | |
var router = RouteStack(); | |
router.addRoute(...); | |
// another package | |
class SimpleDispatcher | |
{ | |
var matchObj = router.match(request); | |
// render matchObj.getParam('template'); | |
} |
Hi @fritz-gerneth (did that make you get an email?).
Sorry again for my slow response!
I think I'm understanding what you are saying, but I'm confused about two things.
- I'm not sure what a Front Controller is, but I think you are saying it makes a certain function run every time a certain URL matches?
- It's not clear in all this how the actual active window (or request)'s URL (which has to be a singleton) is involved.
I've started to write up a "split" API based on your ideas here: https://github.com/tmeasday/meteor-router/wiki/Split-API
What do you think? The concepts match across pretty cleanly to what you are saying here if I've understood you correctly.
var FrontController = function (routeStack, dispatcher) {
"use strict";
Meteor.autorun(function () {
var currentRoute = routeStack.match();
// pull information on what to call from where-ever, e.g. a dispatcher or for simplicity, from the route itself
currentRoute.controller();
})
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Let's assume there are the following components:
RouteStackInterface
The router is an instance of
RouteStackInterface
. It's not singletone (remember, singletone is bad). In theory there can be multiple instances of the same router or different routers at the same time. Other packages just access the variable containing the router instance they want to use. How they gain access to it doesn't matter for the router, that's up to the developer.With the your approach (and your new API) there's the problem that the router actually has multiple responsibilities (matching routes, callbacks, templating,.. ). With the suggested API the router only does match routes and makes the current matching route available. Other components can access it and decide on what to do on their own way (e.g templating, layout).
Looking at my suggested API: the
RouteStack
is what currently the router is, but in a more general way. In therory (later on) we could build up tree-like routes, sub-routes and others. Other packages can add their own routes to it if they have access to the instance (remember, it's not singletone, so it actively has to be passed).Other components can rely on the current matching route by calling
match()
(returning the route and further route-related information). As this function is reactive it's easy to be notified when the routing result changes.Templating and views would be build on top of the router. They'd call the
match
function of the Router(RouterStack
probably), look at the route that matched and decide which layout / template to render. This way the router isn't aware of the templating stuff at all. A bonus: it can be used on the server as well. More sophisticated applications might decide not to the templating directly as described but have a component that maps the current matching route to a function. That'd be a real FrontController then. Effectively it splits up your router into several smaller packages with only one responsibility.Now I know Meteor wants to be beginners friendly. Making thinks simple for beginners is important, sure. However the architecture should be flexible enough for more complex use-cases. Therefore I'd suggest several packages built on top of each other:
That'd look like