Hi all,

I've been working on the Dojo Router component for 1.8, and I think it's
pretty much ready to go. I've discussed the API with Colin, Ken, and Revin,
and I think it's pretty solid.

If you aren't familiar with the concept behind a router, it's typically a
way for people to conveniently listen to hash changes and respond to them
with a callback, which may be automatically parsing values out of the hash
(which typically looks like a URL). It's a component that other toolkits
and libraries have had, and one that anyone could roll their own with
dojo/hash, but it's nothing that we really have a more direct analog for.
It's not the hardest code, but it's definitely something worth Dojo having.

The module returns a single object, which is the router itself -- not a
constructor for a router or anything like that, just a singleton. It
provides a few major methods:

- go: router.go("/foo/bar");

go is simply a shorthand to fire off a hash change, but it also kicks off
any matching routes synchronously.

- startup: router.startup();

Simply just starts the router listening to the hash changes.

- register: router.register(route, callback, isBefore)

Register is really the larger piece of API here. It sets up a callback to
be fired based on the route provided. The route can be a string
("/foo/bar") or a regular expression. The callback will be provided a
single object, which is either an object with keys in the case of a string,
or an array of capture groups in the regex.

* If a string is used, it's parsed for some special strings - anything
prefixed with a colon gets turned into a capture group and anything
prefixed with a star gets turned into a more aggressive capture group. As
an example, the route "/foo/:bar/*baz" would match against the hash
"/foo/bar/baz/qux", and the object provided to the callback would look like
`{ bar: "bar", baz: "baz/qux" }`.

* If a regex is used, nothing special happens, except that any capture
groups are passed through to the callback in a single array parameter. As
an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of
"/foo/abc123/456" and the callback would receive an array like `[ "abc123",
"456" ]`.

The third parameter of `isBefore` allows for binding the callback to happen
before any previously registered callbacks. Internally it is using
dojo/aspect, and setting `isBefore` to true kicks it into using
aspect.before rather than aspect.after.

The register call returns an object with two methods:

- remove: Does what you would expect. It unbinds the route/callback
combination specified, while leaving any other callbacks using that route
in tact.

- add: Allows you to add a new callback with the same route. It will also
return a new object that matches this specification, meaning that you could
easily chain `add` calls, or save the result of an `add` in order to remove
just that one callback and not the rest.

With `add`, it doesn't cumulatively remember or hang onto any collections
for group removal, so if you create a route, save its returned handle
object, and use it to add five more routes, using `remove` will only get
rid of the original.

Additionally, it uses `has("config-isDebug")` to expose a few properties on
the router object itself for inspection / debugging.

I've posted it on GitHub in my Dojo fork at
https://github.com/brianarn/dojo/blob/master/router.js along with a set of
tests at https://github.com/brianarn/dojo/blob/master/tests/router.js that
should help demonstrate its use better. I've tried to put in some detailed
inline documentation as well, so if this email doesn't make it clear,
perhaps that will. :)

I've always liked the idea of a router and I'm glad I got to build it.
Here's hoping you all like it too. Any feedback you can provide would be
more than welcomed. Thanks. :)

--
Brian Arnold
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120424/23cd2d7c/attachment.htm

Search Discussions

  • James Thomas at Apr 25, 2012 at 8:22 am
    Brian,

    This looks great! Have you any thoughts about allowing users to enable
    support for the HTML5 History API, failing back to hash events where they
    aren't supported?

    2012/4/25 Brian Arnold <brianarn at gmail.com>
    Hi all,

    I've been working on the Dojo Router component for 1.8, and I think it's
    pretty much ready to go. I've discussed the API with Colin, Ken, and Revin,
    and I think it's pretty solid.

    If you aren't familiar with the concept behind a router, it's typically a
    way for people to conveniently listen to hash changes and respond to them
    with a callback, which may be automatically parsing values out of the hash
    (which typically looks like a URL). It's a component that other toolkits
    and libraries have had, and one that anyone could roll their own with
    dojo/hash, but it's nothing that we really have a more direct analog for.
    It's not the hardest code, but it's definitely something worth Dojo having.

    The module returns a single object, which is the router itself -- not a
    constructor for a router or anything like that, just a singleton. It
    provides a few major methods:

    - go: router.go("/foo/bar");

    go is simply a shorthand to fire off a hash change, but it also kicks off
    any matching routes synchronously.

    - startup: router.startup();

    Simply just starts the router listening to the hash changes.

    - register: router.register(route, callback, isBefore)

    Register is really the larger piece of API here. It sets up a callback to
    be fired based on the route provided. The route can be a string
    ("/foo/bar") or a regular expression. The callback will be provided a
    single object, which is either an object with keys in the case of a string,
    or an array of capture groups in the regex.

    * If a string is used, it's parsed for some special strings - anything
    prefixed with a colon gets turned into a capture group and anything
    prefixed with a star gets turned into a more aggressive capture group. As
    an example, the route "/foo/:bar/*baz" would match against the hash
    "/foo/bar/baz/qux", and the object provided to the callback would look like
    `{ bar: "bar", baz: "baz/qux" }`.

    * If a regex is used, nothing special happens, except that any capture
    groups are passed through to the callback in a single array parameter. As
    an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of
    "/foo/abc123/456" and the callback would receive an array like `[ "abc123",
    "456" ]`.

    The third parameter of `isBefore` allows for binding the callback to
    happen before any previously registered callbacks. Internally it is using
    dojo/aspect, and setting `isBefore` to true kicks it into using
    aspect.before rather than aspect.after.

    The register call returns an object with two methods:

    - remove: Does what you would expect. It unbinds the route/callback
    combination specified, while leaving any other callbacks using that route
    in tact.

    - add: Allows you to add a new callback with the same route. It will also
    return a new object that matches this specification, meaning that you could
    easily chain `add` calls, or save the result of an `add` in order to remove
    just that one callback and not the rest.

    With `add`, it doesn't cumulatively remember or hang onto any collections
    for group removal, so if you create a route, save its returned handle
    object, and use it to add five more routes, using `remove` will only get
    rid of the original.

    Additionally, it uses `has("config-isDebug")` to expose a few properties
    on the router object itself for inspection / debugging.

    I've posted it on GitHub in my Dojo fork at
    https://github.com/brianarn/dojo/blob/master/router.js along with a set
    of tests at https://github.com/brianarn/dojo/blob/master/tests/router.jsthat should help demonstrate its use better. I've tried to put in some
    detailed inline documentation as well, so if this email doesn't make it
    clear, perhaps that will. :)

    I've always liked the idea of a router and I'm glad I got to build it.
    Here's hoping you all like it too. Any feedback you can provide would be
    more than welcomed. Thanks. :)

    --
    Brian Arnold

    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors

    --
    Regards,
    James Thomas
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120425/b967f011/attachment.htm
  • Mike Wilcox at Apr 25, 2012 at 10:32 am
    Hi Brian, I recently implemented a router in an app at work. I like your RegExp option, I think if I'd have used that it may have saved a few cycles. I do have a few suggestions...

    Having aop.before is a great option - but it would be better if it passed an event that could stop the hash change. In my app at least, there are several cases where the requested page is not valid, like it doesn't exist, or the person does not have permission. It's important to stop the hash from changing to that bad page so that it doesn't get stuck in the back button history.

    There's no way to get the current hash. Of course I could do document.location.hash and parse it, but that seems contrary to the whole idea. Maybe router.get() would return the same event as passed by the callbacks. I'd also (or otherwise) like to see the current hash event in the handle. This would be very handy when a module registers after startup has been called. More data!

    It's not clear exactly what properties the callback provides. I think your RegExp and special characters should handle most cases. But you don't have any examples where you have properties in the URL, like: foo/bar/baz&id«c&value3. It looks like you only return what *doesn't* match my request string (I think), and that sounds a bit brittle. Perhaps return that match along with an object and or array of the whole hash parsed. More data!

    But still great stuff!

    Mike Wilcox
    http://clubajax.org
    mike at mikewilcox.net


    On Apr 24, 2012, at 11:20 PM, Brian Arnold wrote:

    Hi all,

    I've been working on the Dojo Router component for 1.8, and I think it's pretty much ready to go. I've discussed the API with Colin, Ken, and Revin, and I think it's pretty solid.

    If you aren't familiar with the concept behind a router, it's typically a way for people to conveniently listen to hash changes and respond to them with a callback, which may be automatically parsing values out of the hash (which typically looks like a URL). It's a component that other toolkits and libraries have had, and one that anyone could roll their own with dojo/hash, but it's nothing that we really have a more direct analog for. It's not the hardest code, but it's definitely something worth Dojo having.

    The module returns a single object, which is the router itself -- not a constructor for a router or anything like that, just a singleton. It provides a few major methods:

    - go: router.go("/foo/bar");

    go is simply a shorthand to fire off a hash change, but it also kicks off any matching routes synchronously.

    - startup: router.startup();

    Simply just starts the router listening to the hash changes.

    - register: router.register(route, callback, isBefore)

    Register is really the larger piece of API here. It sets up a callback to be fired based on the route provided. The route can be a string ("/foo/bar") or a regular expression. The callback will be provided a single object, which is either an object with keys in the case of a string, or an array of capture groups in the regex.

    * If a string is used, it's parsed for some special strings - anything prefixed with a colon gets turned into a capture group and anything prefixed with a star gets turned into a more aggressive capture group. As an example, the route "/foo/:bar/*baz" would match against the hash "/foo/bar/baz/qux", and the object provided to the callback would look like `{ bar: "bar", baz: "baz/qux" }`.

    * If a regex is used, nothing special happens, except that any capture groups are passed through to the callback in a single array parameter. As an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of "/foo/abc123/456" and the callback would receive an array like `[ "abc123", "456" ]`.

    The third parameter of `isBefore` allows for binding the callback to happen before any previously registered callbacks. Internally it is using dojo/aspect, and setting `isBefore` to true kicks it into using aspect.before rather than aspect.after.

    The register call returns an object with two methods:

    - remove: Does what you would expect. It unbinds the route/callback combination specified, while leaving any other callbacks using that route in tact.

    - add: Allows you to add a new callback with the same route. It will also return a new object that matches this specification, meaning that you could easily chain `add` calls, or save the result of an `add` in order to remove just that one callback and not the rest.

    With `add`, it doesn't cumulatively remember or hang onto any collections for group removal, so if you create a route, save its returned handle object, and use it to add five more routes, using `remove` will only get rid of the original.

    Additionally, it uses `has("config-isDebug")` to expose a few properties on the router object itself for inspection / debugging.

    I've posted it on GitHub in my Dojo fork at https://github.com/brianarn/dojo/blob/master/router.js along with a set of tests at https://github.com/brianarn/dojo/blob/master/tests/router.js that should help demonstrate its use better. I've tried to put in some detailed inline documentation as well, so if this email doesn't make it clear, perhaps that will. :)

    I've always liked the idea of a router and I'm glad I got to build it. Here's hoping you all like it too. Any feedback you can provide would be more than welcomed. Thanks. :)

    --
    Brian Arnold
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120425/154550c0/attachment.htm
  • Bill Keese at Apr 25, 2012 at 6:01 pm
    You might want to consider AdapterRegistry for keeping the list of routes.
    Not sure if it makes sense or not.

    Oh, also, you have a lot of spaces in your patch that should be removed.
    if ( foo ) { should be if(foo){ according to our coding standards.

    2012/4/25 Brian Arnold <brianarn at gmail.com>
    Hi all,

    I've been working on the Dojo Router component for 1.8, and I think it's
    pretty much ready to go. I've discussed the API with Colin, Ken, and Revin,
    and I think it's pretty solid.

    If you aren't familiar with the concept behind a router, it's typically a
    way for people to conveniently listen to hash changes and respond to them
    with a callback, which may be automatically parsing values out of the hash
    (which typically looks like a URL). It's a component that other toolkits
    and libraries have had, and one that anyone could roll their own with
    dojo/hash, but it's nothing that we really have a more direct analog for.
    It's not the hardest code, but it's definitely something worth Dojo having.

    The module returns a single object, which is the router itself -- not a
    constructor for a router or anything like that, just a singleton. It
    provides a few major methods:

    - go: router.go("/foo/bar");

    go is simply a shorthand to fire off a hash change, but it also kicks off
    any matching routes synchronously.

    - startup: router.startup();

    Simply just starts the router listening to the hash changes.

    - register: router.register(route, callback, isBefore)

    Register is really the larger piece of API here. It sets up a callback to
    be fired based on the route provided. The route can be a string
    ("/foo/bar") or a regular expression. The callback will be provided a
    single object, which is either an object with keys in the case of a string,
    or an array of capture groups in the regex.

    * If a string is used, it's parsed for some special strings - anything
    prefixed with a colon gets turned into a capture group and anything
    prefixed with a star gets turned into a more aggressive capture group. As
    an example, the route "/foo/:bar/*baz" would match against the hash
    "/foo/bar/baz/qux", and the object provided to the callback would look like
    `{ bar: "bar", baz: "baz/qux" }`.

    * If a regex is used, nothing special happens, except that any capture
    groups are passed through to the callback in a single array parameter. As
    an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of
    "/foo/abc123/456" and the callback would receive an array like `[ "abc123",
    "456" ]`.

    The third parameter of `isBefore` allows for binding the callback to
    happen before any previously registered callbacks. Internally it is using
    dojo/aspect, and setting `isBefore` to true kicks it into using
    aspect.before rather than aspect.after.

    The register call returns an object with two methods:

    - remove: Does what you would expect. It unbinds the route/callback
    combination specified, while leaving any other callbacks using that route
    in tact.

    - add: Allows you to add a new callback with the same route. It will also
    return a new object that matches this specification, meaning that you could
    easily chain `add` calls, or save the result of an `add` in order to remove
    just that one callback and not the rest.

    With `add`, it doesn't cumulatively remember or hang onto any collections
    for group removal, so if you create a route, save its returned handle
    object, and use it to add five more routes, using `remove` will only get
    rid of the original.

    Additionally, it uses `has("config-isDebug")` to expose a few properties
    on the router object itself for inspection / debugging.

    I've posted it on GitHub in my Dojo fork at
    https://github.com/brianarn/dojo/blob/master/router.js along with a set
    of tests at https://github.com/brianarn/dojo/blob/master/tests/router.jsthat should help demonstrate its use better. I've tried to put in some
    detailed inline documentation as well, so if this email doesn't make it
    clear, perhaps that will. :)

    I've always liked the idea of a router and I'm glad I got to build it.
    Here's hoping you all like it too. Any feedback you can provide would be
    more than welcomed. Thanks. :)

    --
    Brian Arnold

    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120426/ba6bb960/attachment-0001.htm
  • Brian Arnold at May 8, 2012 at 10:15 pm
    Hello everyone,

    Thank you for the great feedback. I really appreciate it. Sorry it's taken
    awhile to get back to things -- the past few weeks have been a bit crazy
    (and I expect they'll continue to be, new kid woo!). To respond to feedback:

    I received some off-list feedback commenting that the third parameter to
    `register` of `isBefore` was not the most intuitive, and that perhaps it
    should be a string like "before" or "after", which may help make things a
    bit more clear and feel a bit more like the dom-const `create` and `place`
    methods. Additionally, it was suggested that instead of being a parameter,
    that perhaps it should be a function that hangs off of register, like
    `router.register.before(route, callback)`. After some discussion,
    router.register.before wins and is what's implemented now. It definitely
    feels cleaner too.

    James: The idea of using HTML5 History is one that I quite like, and other
    routers do support. It's not too hard to implement, and just wasn't in this
    first pass because I was working more on getting the API itself nailed down
    a bit. It's something in the queue to implement. I've been talking to Colin
    and there was suggestion that perhaps we should consider a shim around that
    API, akin to how dojo/hash is really just a shim around the hashchange
    event. Open to thoughts here.

    Mike: I do like the idea of having an event object that exposes some way to
    cancel forward movement. The original implementation was basically a
    wrapper around some aspect calls, and since that didn't provide any form of
    cancel or preventDefault or whatever, there wasn't anything to expose.
    Thinking about it, passing an event object with the following
    properties/methods does seem useful:

    * url: The hash or history state, depending on the API used (assuming HTML5
    History API is in place)
    * params: An object that is either an array of matches (in the case of pure
    RE) or object with keys/values (in the case of string route) -- basically
    the object that's being passed through now
    * preventDefault(): A method that, when called, keeps subsequent callbacks
    from firing

    On the other hand, the API as-is simply just exposes that params object and
    is going to keep things simpler because there won't be as many lookups to
    get to the values that you want. I wouldn't mind getting some further
    feedback here. Do people think it should have more of an event-like
    interface? Seems more powerful at the potential (very slight) expense of
    performance, and would mean I could likely eliminate the dependency on
    aspect, which makes it more standalone.

    There had also been some talk at one point about allowing a callback to
    return a promise, and only continuing the chain when the promise
    successfully resolved. However, we decided not to go that route in order to
    keep things relatively simple and independent. If we did alter things to
    allow for cancelation in the sequence, putting in promise support seems
    like it wouldn't be that much more work, but it seems better to keep it
    simple for now.

    As for the more complex URL structure shown by Mike, the common router
    paradigm is RESTful-like URLs and I haven't seen any other routers that
    would support the kind of mix-and-match style shown with ampersands, making
    it more like a mix of querystring and URL structures. That being said, it
    would be easy enough with the regex style to grab whatever you're looking
    for. I'm inclined to not change the string syntaxes supported, and to
    suggest regex for more complex URLs, for the sake of keeping a clean and
    simple implementation.

    Bill: I looked at AdapterRegistry and it wasn't really quite the right
    thing here. It's an interesting piece of API but I can't see a clean way to
    use it to replace the current registration system without making the code
    feel a lot more complex. Right now it's fairly simple and light, and I
    would be concerned that the AdapterRegistry is going to add some
    unnecessary overhead. Also, Revin lent a hand and cleaned up the style
    guide discrepancies.

    I'd love to hear more thoughts and feedback, especially with regards to
    Mike's suggestion about having it be a more event-style API for callbacks.

    Thanks all! :)


    --
    Brian Arnold

    On Wed, Apr 25, 2012 at 4:01 PM, Bill Keese wrote:

    You might want to consider AdapterRegistry for keeping the list of routes.
    Not sure if it makes sense or not.

    Oh, also, you have a lot of spaces in your patch that should be removed.
    if ( foo ) { should be if(foo){ according to our coding standards.

    2012/4/25 Brian Arnold <brianarn at gmail.com>
    Hi all,

    I've been working on the Dojo Router component for 1.8, and I think it's
    pretty much ready to go. I've discussed the API with Colin, Ken, and Revin,
    and I think it's pretty solid.

    If you aren't familiar with the concept behind a router, it's typically a
    way for people to conveniently listen to hash changes and respond to them
    with a callback, which may be automatically parsing values out of the hash
    (which typically looks like a URL). It's a component that other toolkits
    and libraries have had, and one that anyone could roll their own with
    dojo/hash, but it's nothing that we really have a more direct analog for.
    It's not the hardest code, but it's definitely something worth Dojo having.

    The module returns a single object, which is the router itself -- not a
    constructor for a router or anything like that, just a singleton. It
    provides a few major methods:

    - go: router.go("/foo/bar");

    go is simply a shorthand to fire off a hash change, but it also kicks off
    any matching routes synchronously.

    - startup: router.startup();

    Simply just starts the router listening to the hash changes.

    - register: router.register(route, callback, isBefore)

    Register is really the larger piece of API here. It sets up a callback to
    be fired based on the route provided. The route can be a string
    ("/foo/bar") or a regular expression. The callback will be provided a
    single object, which is either an object with keys in the case of a string,
    or an array of capture groups in the regex.

    * If a string is used, it's parsed for some special strings - anything
    prefixed with a colon gets turned into a capture group and anything
    prefixed with a star gets turned into a more aggressive capture group. As
    an example, the route "/foo/:bar/*baz" would match against the hash
    "/foo/bar/baz/qux", and the object provided to the callback would look like
    `{ bar: "bar", baz: "baz/qux" }`.

    * If a regex is used, nothing special happens, except that any capture
    groups are passed through to the callback in a single array parameter. As
    an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of
    "/foo/abc123/456" and the callback would receive an array like `[ "abc123",
    "456" ]`.

    The third parameter of `isBefore` allows for binding the callback to
    happen before any previously registered callbacks. Internally it is using
    dojo/aspect, and setting `isBefore` to true kicks it into using
    aspect.before rather than aspect.after.

    The register call returns an object with two methods:

    - remove: Does what you would expect. It unbinds the route/callback
    combination specified, while leaving any other callbacks using that route
    in tact.

    - add: Allows you to add a new callback with the same route. It will also
    return a new object that matches this specification, meaning that you could
    easily chain `add` calls, or save the result of an `add` in order to remove
    just that one callback and not the rest.

    With `add`, it doesn't cumulatively remember or hang onto any collections
    for group removal, so if you create a route, save its returned handle
    object, and use it to add five more routes, using `remove` will only get
    rid of the original.

    Additionally, it uses `has("config-isDebug")` to expose a few properties
    on the router object itself for inspection / debugging.

    I've posted it on GitHub in my Dojo fork at
    https://github.com/brianarn/dojo/blob/master/router.js along with a set
    of tests at https://github.com/brianarn/dojo/blob/master/tests/router.jsthat should help demonstrate its use better. I've tried to put in some
    detailed inline documentation as well, so if this email doesn't make it
    clear, perhaps that will. :)

    I've always liked the idea of a router and I'm glad I got to build it.
    Here's hoping you all like it too. Any feedback you can provide would be
    more than welcomed. Thanks. :)

    --
    Brian Arnold

    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120508/6866e758/attachment-0001.htm
  • Christophe Jolif at May 9, 2012 at 3:40 am
    Birian,

    On Wed, May 9, 2012 at 4:15 AM, Brian Arnold wrote:

    James: The idea of using HTML5 History is one that I quite like, and other
    routers do support. It's not too hard to implement, and just wasn't in this
    first pass because I was working more on getting the API itself nailed down
    a bit. It's something in the queue to implement. I've been talking to Colin
    and there was suggestion that perhaps we should consider a shim around that
    API, akin to how dojo/hash is really just a shim around the hashchange
    event. Open to thoughts here.
    There is a ticket open to enhance dojo/hash to support HTML5 history as
    well:

    http://trac.dojotoolkit.org/ticket/13958

    Maybe that would be the way to go?


    --
    Brian Arnold


    On Wed, Apr 25, 2012 at 4:01 PM, Bill Keese wrote:

    You might want to consider AdapterRegistry for keeping the list of
    routes. Not sure if it makes sense or not.

    Oh, also, you have a lot of spaces in your patch that should be removed.
    if ( foo ) { should be if(foo){ according to our coding standards.

    2012/4/25 Brian Arnold <brianarn at gmail.com>
    Hi all,

    I've been working on the Dojo Router component for 1.8, and I think it's
    pretty much ready to go. I've discussed the API with Colin, Ken, and Revin,
    and I think it's pretty solid.

    If you aren't familiar with the concept behind a router, it's typically
    a way for people to conveniently listen to hash changes and respond to them
    with a callback, which may be automatically parsing values out of the hash
    (which typically looks like a URL). It's a component that other toolkits
    and libraries have had, and one that anyone could roll their own with
    dojo/hash, but it's nothing that we really have a more direct analog for.
    It's not the hardest code, but it's definitely something worth Dojo having.

    The module returns a single object, which is the router itself -- not a
    constructor for a router or anything like that, just a singleton. It
    provides a few major methods:

    - go: router.go("/foo/bar");

    go is simply a shorthand to fire off a hash change, but it also kicks
    off any matching routes synchronously.

    - startup: router.startup();

    Simply just starts the router listening to the hash changes.

    - register: router.register(route, callback, isBefore)

    Register is really the larger piece of API here. It sets up a callback
    to be fired based on the route provided. The route can be a string
    ("/foo/bar") or a regular expression. The callback will be provided a
    single object, which is either an object with keys in the case of a string,
    or an array of capture groups in the regex.

    * If a string is used, it's parsed for some special strings - anything
    prefixed with a colon gets turned into a capture group and anything
    prefixed with a star gets turned into a more aggressive capture group. As
    an example, the route "/foo/:bar/*baz" would match against the hash
    "/foo/bar/baz/qux", and the object provided to the callback would look like
    `{ bar: "bar", baz: "baz/qux" }`.

    * If a regex is used, nothing special happens, except that any capture
    groups are passed through to the callback in a single array parameter. As
    an example, the route /^\/foo\/(\w+)\/(\d+)/ would match against a hash of
    "/foo/abc123/456" and the callback would receive an array like `[ "abc123",
    "456" ]`.

    The third parameter of `isBefore` allows for binding the callback to
    happen before any previously registered callbacks. Internally it is using
    dojo/aspect, and setting `isBefore` to true kicks it into using
    aspect.before rather than aspect.after.

    The register call returns an object with two methods:

    - remove: Does what you would expect. It unbinds the route/callback
    combination specified, while leaving any other callbacks using that route
    in tact.

    - add: Allows you to add a new callback with the same route. It will
    also return a new object that matches this specification, meaning that you
    could easily chain `add` calls, or save the result of an `add` in order to
    remove just that one callback and not the rest.

    With `add`, it doesn't cumulatively remember or hang onto any
    collections for group removal, so if you create a route, save its returned
    handle object, and use it to add five more routes, using `remove` will only
    get rid of the original.

    Additionally, it uses `has("config-isDebug")` to expose a few properties
    on the router object itself for inspection / debugging.

    I've posted it on GitHub in my Dojo fork at
    https://github.com/brianarn/dojo/blob/master/router.js along with a set
    of tests at https://github.com/brianarn/dojo/blob/master/tests/router.jsthat should help demonstrate its use better. I've tried to put in some
    detailed inline documentation as well, so if this email doesn't make it
    clear, perhaps that will. :)

    I've always liked the idea of a router and I'm glad I got to build it.
    Here's hoping you all like it too. Any feedback you can provide would be
    more than welcomed. Thanks. :)

    --
    Brian Arnold

    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors

    --
    Christophe
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120509/a503b249/attachment.htm
  • Sam foster at May 9, 2012 at 11:52 am
    This has been one of those critical missing APIs in dojo for a long
    time, props to you and Sitepen for getting it done.

    What's the use case for the isBefore option? Also, why a singleton,
    why not export a Router class? (speaking of uses cases, I don't know
    why you'd need more than one router, but I get bit *every* time I use
    a singleton like this)

    /sam
  • Colin Snover at May 9, 2012 at 3:08 pm
    Hi,
    On 09/05/2012 10:52, sam foster wrote:
    This has been one of those critical missing APIs in dojo for a long
    time, props to you and Sitepen for getting it done.
    SP is not sponsoring the router component as far as I am aware, it?s an
    evenings-and-weekends project for Brian. :) dgrid is the major sponsored
    project at the moment, though I think there are some others coming soon.
    What's the use case for the isBefore option?
    It?s analogous to Array.prototype.unshift, so people can register
    additional callbacks to be executed before earlier registered callbacks.
    Also, why a singleton,
    why not export a Router class? (speaking of uses cases, I don't know
    why you'd need more than one router, but I get bit *every* time I use
    a singleton like this)
    There is only one window.location per page, so I don?t know how multiple
    router components would ever make sense. Interested to hear how you
    think this could ever be used differently, though.

    Regards,

    --
    Colin Snover
    http://zetafleet.com
  • Ben Hockey at May 9, 2012 at 3:35 pm

    On May 9, 2012, at 3:08 PM, Colin Snover wrote:

    There is only one window.location per page, so I don?t know how multiple
    router components would ever make sense. Interested to hear how you
    think this could ever be used differently, though.

    changes in window.location is not the only way to "drive" routes - think outside the box just a little bit. i honestly haven't looked at brian's code yet so i'm not speaking to it specifically but a pluggable way to set the routes and allowing multiple routers per page (i.e. not a singleton) are important features imo. just because only one router could control window.location shouldn't mean that we should only ever have one router. a pluggable interface for setting routes would at least allow a simple swap from hash based routing to history based routing but would also allow for any other controller to set routes.

    for a use case, think of an embedded widget in a page. that widget shouldn't take control of window.location but it might use a router (changing routes via something like pub/sub) for indicating changes in application state. maybe some of the core code in that widget is also used in a full page version and in that case it uses location or history (if available) to trigger the changes in routes. in this use case, the widget needs to be able to plugin various controllers for setting the route - hash, history, pubsub.

    in my opinion there should be a loose coupling between the router and the mechanism used to control/set routes.

    thanks,

    ben...
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120509/08d15560/attachment.htm
  • Dustin Machi at May 9, 2012 at 3:44 pm
    I like the way you think and would like to subscribe to your newsletter.

    I agree that what drives the routes should be loosely coupled for the reasons described by Ben. Having one router accept a route and pass it along to another router is fairly useful and I think it would be beneficial here. This is a common capability for the equivalent functionality on the server side (django's router for example), and it seems like it would be equally beneficial here when putting a dynamic app together.

    In dojox app these functions are specifically kept separate for the same reasons and it would be great to use Brian's router rather than reimplementing something similar.

    Dustin

    On May 9, 2012, at 3:35 PM, Ben Hockey wrote:

    On May 9, 2012, at 3:08 PM, Colin Snover wrote:

    There is only one window.location per page, so I don?t know how multiple
    router components would ever make sense. Interested to hear how you
    think this could ever be used differently, though.

    changes in window.location is not the only way to "drive" routes - think outside the box just a little bit. i honestly haven't looked at brian's code yet so i'm not speaking to it specifically but a pluggable way to set the routes and allowing multiple routers per page (i.e. not a singleton) are important features imo. just because only one router could control window.location shouldn't mean that we should only ever have one router. a pluggable interface for setting routes would at least allow a simple swap from hash based routing to history based routing but would also allow for any other controller to set routes.

    for a use case, think of an embedded widget in a page. that widget shouldn't take control of window.location but it might use a router (changing routes via something like pub/sub) for indicating changes in application state. maybe some of the core code in that widget is also used in a full page version and in that case it uses location or history (if available) to trigger the changes in routes. in this use case, the widget needs to be able to plugin various controllers for setting the route - hash, history, pubsub.

    in my opinion there should be a loose coupling between the router and the mechanism used to control/set routes.

    thanks,

    ben...
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
  • Eugene Lazutkin at May 9, 2012 at 3:59 pm

    On 05/09/2012 02:08 PM, Colin Snover wrote:
    There is only one window.location per page, so I don?t know how multiple
    router components would ever make sense. Interested to hear how you
    think this could ever be used differently, though.
    There is one window per frame and potentially many frames per page.

    Cheers,

    Eugene
  • Brian Arnold at May 11, 2012 at 1:51 am
    Based on the meeting from Wednesday, here's an update:

    * I've made solid progress on implementing the callback queue per route
    work without use of dojo/aspect. The main reason for this is to be able to
    insert more event-like functionality in a way that aspect wouldn't really
    allow.

    * Originally, the callbacks would just receive a parameters object. They're
    now getting an event-like object with the following properties/methods:

    - params: An object (from string routes) or array (regex routes) of the
    matches (basically the object they used to receive)
    - oldPath: What the hash was previously in its entirety (named Path so that
    when HTML5 History APIs show, it won't be just hash)
    - newPath: What the hash is now shifting to in its entirety
    - preventDefault(): A method that, when called, will stop the new hash from
    applying. That being said, this only works if the hash change was initiated
    by the router itself. If an external source (someone calling hash directly
    or modifying location.hash) has made the change, it's too late to undo
    that. Theoretically we could issue a history.go(-1) but that feels horribly
    ugly, and if people are consistent about using the router, it should be a
    non-issue.
    - stopImmediatePropagation(): A method that, when called, will stop any
    additional bound callbacks to that route from firing. If there are two
    different routes that match the current hash, stopping propagation in one
    route's callback chain will not stop the other callback chain from firing
    -- it only stops the current callback chain.

    * I have not started on the move from singleton -> declare'd constructor
    just yet. That will happen next. I still feel like a singleton is going to
    be the right solution, but there were quite a few voices against a
    singleton pattern, so the approach moving forward will be as follows:

    - require("dojo/router") and you get a singleton, much like the current
    implementation.
    - require("dojo/router/RouterBase") and you get a constructor for the
    router. The singleton in "dojo/require" is effectively just going to be an
    unstarted instance of RouterBase.

    I will still admit to some frustration with this particular approach, as
    the singleton is very likely going to be the 99.5% use case, and shifting
    to a declare'd constructor means loading the full weight of
    dojo/_base/declare rather than just dojo/has, dojo/hash, and dojo/topic,
    but I understand why people are so vociferous about having a proper
    constructor available. It does open some potential doors in the future --
    it's just hard to say at this point whether or not those doors matter.
    Better to stay more flexible now while laying the API, I suppose. =)

    The current WIP is on my `eventAPI` branch on GitHub and can be viewed at
    https://github.com/brianarn/dojo/blob/eventAPI/router.js

    Once the refactor into an declare'd constructor is complete, I think it's
    probably ready to find its way into trunk, but while I do technically have
    that right now, I'd definitely want a little sign-off before actually doing
    that. =)

    --
    Brian Arnold

    On Wed, May 9, 2012 at 1:59 PM, Eugene Lazutkin wrote:

    On 05/09/2012 02:08 PM, Colin Snover wrote:

    There is only one window.location per page, so I don?t know how multiple
    router components would ever make sense. Interested to hear how you
    think this could ever be used differently, though.
    There is one window per frame and potentially many frames per page.

    Cheers,

    Eugene
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120510/13753227/attachment.htm
  • Mike Wilcox at May 11, 2012 at 9:37 am
    I agree that a singleton is all that is necessary. I hadn't heard any compelling arguments on why you would need to spawn multiple routers. Dustin's suggestion that Node could make use of it is compelling but still theoretical. I also don't buy that you are locked into the signature and can't upgrade it. If it was determined that a constructor was necessary, the router.register would simply change from its current functionality to returning new RouterBase(route, callback);

    Also, for that .05%, I'm guessing that anyone who needs functionality that specific, they are going to need something custom and write it themeselves.

    Mike


    On May 11, 2012, at 12:51 AM, Brian Arnold wrote:

    * I have not started on the move from singleton -> declare'd constructor just yet. That will happen next. I still feel like a singleton is going to be the right solution, but there were quite a few voices against a singleton pattern, so the approach moving forward will be as follows:

    - require("dojo/router") and you get a singleton, much like the current implementation.
    - require("dojo/router/RouterBase") and you get a constructor for the router. The singleton in "dojo/require" is effectively just going to be an unstarted instance of RouterBase.

    I will still admit to some frustration with this particular approach, as the singleton is very likely going to be the 99.5% use case
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120511/778ae286/attachment.htm
  • Sasha Firsov at May 11, 2012 at 11:14 am

    Also, for that .05%, I'm guessing that anyone who needs functionality
    that specific, they are going to need something custom and write it
    themeselves.
    Mike
    Singleton vs multiple instances is not that difficult from beginning.
    But for those .05% would be reverse. To insulate 2 instances out of
    singleton would require quite of tricks.
    There are patterns which where accepted for sake of code
    standardization. Ability to create router has not been explored yet.
    Just from top of the head I could pick few. Branched sub-history,
    navigation map, sub-container-scope history, etc.

    When basic constructs are made they desperately need to be as generic as
    possible.
    Obviously in reasonable labor scope. Only in that case we would expect
    extended use. Otherwise when another feature which could be based on
    current, would be cloned. Or even worse done on its own which mean
    duplication of functionality for sake of initial limitations.

    -1 to singleton as implementation. It is OK to have dual use: singleton
    as instantiated object.

    Sasha

    PS. Those .05% actually are the evolution engine of DTK.
  • Sam foster at May 14, 2012 at 5:50 am
    I went ahead and forked to make dojo/router export a Router class:
    https://github.com/sfoster/dojo/blob/master/router.js

    The tests pass, but I didn't attempt to resolve any of the questions
    this opens, like:

    * how to configure instances (and what should be configurable)
    * if they don't observe and manipulate location.hash (via dojo/hash) then what?

    I did have instances subscribe to this.changeTopic, but they still
    call hash(path, replace) directly. That might work, or not depending
    on what you expect. I think actually that location.hash is one
    possible driver for the router and configuring a router probably
    involves telling it where to get change events from (.e.g.
    dojo/hashchange), how to effect a change (e.g. setting hash(path,
    replace)) and dojo/hash is just one possible source.

    The HTML5 history api (historypush/pop state) is already out there,
    and mainstream for projects targeting modern browsers. So, for me,
    location.hash/onhashchange is the legacy API and this all should be
    refactored to deal with states, with fallback behavior for hashchange.
    That might be outside the scope of this effort though, in which case
    i'll take my fork elsewhere.

    /Sam
  • Brian Arnold at May 14, 2012 at 2:08 pm
    Hey there Sam,

    I appreciate the effort, but it isn't quite what was agreed upon in the
    meeting. "dojo/router" as a MID is still going to provide a singleton, and
    there'll be a base class that defines something more like what your fork is
    looking like, probably something like "dojo/router/RouterBase" or something
    else if people have a better naming convention there.

    Additionally, your branch doesn't seem to have any of the work I put out
    into my eventAPI branch, which includes some significant changes to how the
    router actually fires its events (dojo/aspect isn't used anymore, there's
    also support for stopping the hash change if it's triggered by a router
    object). Not sure how easy it'll be to apply changes, but I'll take a look
    and see. :)

    As for configuration of instances, right now there isn't too much to
    configure that I can think of or see until HTML5 history comes into play
    (more below on that). My original thinking was that the constructor could
    be used, or that the `startup` method could take a config object that'll
    allow for some initial configuration that doesn't matter until things
    actually start rolling.

    With regards to how the hash is changing, the idea is that you'll use your
    router's `go` method, which makes it cleaner to actively stop the hash
    change from flowing out unless all the callbacks are successful. That being
    said, any changes to the hash should be picked up since the router will
    load up dojo/hash no matter what.

    I agree that the HTML5 history API is out there and worth pursuing, but I
    think the idea was that we need to get something that's stable and ready to
    land first, and depending on effort it'll take, to put in HTML5 history
    once it's stable with dojo/hash. There was also talk of maybe coming up
    with a history "shim" of sorts to be using instead, or to put history into
    dojo/hash, and since that future hasn't been clear, it hasn't been pursued
    yet. This would be the only configuration point I would think of, where you
    could specify which API to use at startup.

    --
    Brian Arnold

    On Mon, May 14, 2012 at 3:50 AM, sam foster wrote:

    I went ahead and forked to make dojo/router export a Router class:
    https://github.com/sfoster/dojo/blob/master/router.js

    The tests pass, but I didn't attempt to resolve any of the questions
    this opens, like:

    * how to configure instances (and what should be configurable)
    * if they don't observe and manipulate location.hash (via dojo/hash) then
    what?

    I did have instances subscribe to this.changeTopic, but they still
    call hash(path, replace) directly. That might work, or not depending
    on what you expect. I think actually that location.hash is one
    possible driver for the router and configuring a router probably
    involves telling it where to get change events from (.e.g.
    dojo/hashchange), how to effect a change (e.g. setting hash(path,
    replace)) and dojo/hash is just one possible source.

    The HTML5 history api (historypush/pop state) is already out there,
    and mainstream for projects targeting modern browsers. So, for me,
    location.hash/onhashchange is the legacy API and this all should be
    refactored to deal with states, with fallback behavior for hashchange.
    That might be outside the scope of this effort though, in which case
    i'll take my fork elsewhere.

    /Sam
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120514/5120494e/attachment.htm
  • Brian Arnold at May 15, 2012 at 12:36 am
    Okay, I've gone ahead and completed the refactoring. The baseline declare'd
    module is available at
    https://github.com/brianarn/dojo/blob/master/router/RouterBase.js and the
    singleton version is at
    https://github.com/brianarn/dojo/blob/master/router.js (which is simply
    just a single instance of the RouterBase). There's also a series of tests
    at https://github.com/brianarn/dojo/blob/master/tests/router.js that cover
    the singleton instance, but that exercises the RouterBase, so I would say
    it's safe to assume passing tests here are indications that the refactor
    worked.

    I still think determining HTML5 History API support is kind of up in the
    air, but everything else is still solid. For 1.8 purposes, it's probably
    where it should be, since the future of HTML5 History within Dojo is still
    not exactly clear (could be a good 1.9/2.0 shim or some such, might be
    worth some discussion/thought on its own).

    --
    Brian Arnold

    On Mon, May 14, 2012 at 12:08 PM, Brian Arnold wrote:

    Hey there Sam,

    I appreciate the effort, but it isn't quite what was agreed upon in the
    meeting. "dojo/router" as a MID is still going to provide a singleton, and
    there'll be a base class that defines something more like what your fork is
    looking like, probably something like "dojo/router/RouterBase" or something
    else if people have a better naming convention there.

    Additionally, your branch doesn't seem to have any of the work I put out
    into my eventAPI branch, which includes some significant changes to how the
    router actually fires its events (dojo/aspect isn't used anymore, there's
    also support for stopping the hash change if it's triggered by a router
    object). Not sure how easy it'll be to apply changes, but I'll take a look
    and see. :)

    As for configuration of instances, right now there isn't too much to
    configure that I can think of or see until HTML5 history comes into play
    (more below on that). My original thinking was that the constructor could
    be used, or that the `startup` method could take a config object that'll
    allow for some initial configuration that doesn't matter until things
    actually start rolling.

    With regards to how the hash is changing, the idea is that you'll use your
    router's `go` method, which makes it cleaner to actively stop the hash
    change from flowing out unless all the callbacks are successful. That being
    said, any changes to the hash should be picked up since the router will
    load up dojo/hash no matter what.

    I agree that the HTML5 history API is out there and worth pursuing, but I
    think the idea was that we need to get something that's stable and ready to
    land first, and depending on effort it'll take, to put in HTML5 history
    once it's stable with dojo/hash. There was also talk of maybe coming up
    with a history "shim" of sorts to be using instead, or to put history into
    dojo/hash, and since that future hasn't been clear, it hasn't been pursued
    yet. This would be the only configuration point I would think of, where you
    could specify which API to use at startup.

    --
    Brian Arnold


    On Mon, May 14, 2012 at 3:50 AM, sam foster wrote:

    I went ahead and forked to make dojo/router export a Router class:
    https://github.com/sfoster/dojo/blob/master/router.js

    The tests pass, but I didn't attempt to resolve any of the questions
    this opens, like:

    * how to configure instances (and what should be configurable)
    * if they don't observe and manipulate location.hash (via dojo/hash) then
    what?

    I did have instances subscribe to this.changeTopic, but they still
    call hash(path, replace) directly. That might work, or not depending
    on what you expect. I think actually that location.hash is one
    possible driver for the router and configuring a router probably
    involves telling it where to get change events from (.e.g.
    dojo/hashchange), how to effect a change (e.g. setting hash(path,
    replace)) and dojo/hash is just one possible source.

    The HTML5 history api (historypush/pop state) is already out there,
    and mainstream for projects targeting modern browsers. So, for me,
    location.hash/onhashchange is the legacy API and this all should be
    refactored to deal with states, with fallback behavior for hashchange.
    That might be outside the scope of this effort though, in which case
    i'll take my fork elsewhere.

    /Sam
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120514/6ddc14a6/attachment.htm
  • Tom Trenka at May 15, 2012 at 9:43 am
    Looks good to me. Two notes/small nits:

    1) Are you going to write an actual router.js module that returns an
    instance of RouterBase?
    2) In the RouterBase constructor, you have this:

    // Simple constructor-style "Decorate myself all over" for now
    for(var i in kwArgs){
    if(kwArgs.hasOwnProperty(i)){
    this[i] = kwArgs[i];
    }
    }
    (https://github.com/brianarn/dojo/blob/master/router/RouterBase.js#L79)

    It'd be easier to just do this instead:

    declare.safeMixin(this, kwArgs);

    Otherwise it looks good to me!

    Cheers--
    Tom

    On Mon, May 14, 2012 at 11:36 PM, Brian Arnold wrote:

    Okay, I've gone ahead and completed the refactoring. The baseline
    declare'd module is available at
    https://github.com/brianarn/dojo/blob/master/router/RouterBase.js and the
    singleton version is at
    https://github.com/brianarn/dojo/blob/master/router.js (which is simply
    just a single instance of the RouterBase). There's also a series of tests
    at https://github.com/brianarn/dojo/blob/master/tests/router.js that
    cover the singleton instance, but that exercises the RouterBase, so I would
    say it's safe to assume passing tests here are indications that the
    refactor worked.

    I still think determining HTML5 History API support is kind of up in the
    air, but everything else is still solid. For 1.8 purposes, it's probably
    where it should be, since the future of HTML5 History within Dojo is still
    not exactly clear (could be a good 1.9/2.0 shim or some such, might be
    worth some discussion/thought on its own).

    --
    Brian Arnold


    On Mon, May 14, 2012 at 12:08 PM, Brian Arnold wrote:

    Hey there Sam,

    I appreciate the effort, but it isn't quite what was agreed upon in the
    meeting. "dojo/router" as a MID is still going to provide a singleton, and
    there'll be a base class that defines something more like what your fork is
    looking like, probably something like "dojo/router/RouterBase" or something
    else if people have a better naming convention there.

    Additionally, your branch doesn't seem to have any of the work I put out
    into my eventAPI branch, which includes some significant changes to how the
    router actually fires its events (dojo/aspect isn't used anymore, there's
    also support for stopping the hash change if it's triggered by a router
    object). Not sure how easy it'll be to apply changes, but I'll take a look
    and see. :)

    As for configuration of instances, right now there isn't too much to
    configure that I can think of or see until HTML5 history comes into play
    (more below on that). My original thinking was that the constructor could
    be used, or that the `startup` method could take a config object that'll
    allow for some initial configuration that doesn't matter until things
    actually start rolling.

    With regards to how the hash is changing, the idea is that you'll use
    your router's `go` method, which makes it cleaner to actively stop the hash
    change from flowing out unless all the callbacks are successful. That being
    said, any changes to the hash should be picked up since the router will
    load up dojo/hash no matter what.

    I agree that the HTML5 history API is out there and worth pursuing, but I
    think the idea was that we need to get something that's stable and ready to
    land first, and depending on effort it'll take, to put in HTML5 history
    once it's stable with dojo/hash. There was also talk of maybe coming up
    with a history "shim" of sorts to be using instead, or to put history into
    dojo/hash, and since that future hasn't been clear, it hasn't been pursued
    yet. This would be the only configuration point I would think of, where you
    could specify which API to use at startup.

    --
    Brian Arnold


    On Mon, May 14, 2012 at 3:50 AM, sam foster wrote:

    I went ahead and forked to make dojo/router export a Router class:
    https://github.com/sfoster/dojo/blob/master/router.js

    The tests pass, but I didn't attempt to resolve any of the questions
    this opens, like:

    * how to configure instances (and what should be configurable)
    * if they don't observe and manipulate location.hash (via dojo/hash)
    then what?

    I did have instances subscribe to this.changeTopic, but they still
    call hash(path, replace) directly. That might work, or not depending
    on what you expect. I think actually that location.hash is one
    possible driver for the router and configuring a router probably
    involves telling it where to get change events from (.e.g.
    dojo/hashchange), how to effect a change (e.g. setting hash(path,
    replace)) and dojo/hash is just one possible source.

    The HTML5 history api (historypush/pop state) is already out there,
    and mainstream for projects targeting modern browsers. So, for me,
    location.hash/onhashchange is the legacy API and this all should be
    refactored to deal with states, with fallback behavior for hashchange.
    That might be outside the scope of this effort though, in which case
    i'll take my fork elsewhere.

    /Sam
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    _______________________________________________
    dojo-contributors mailing list
    dojo-contributors at mail.dojotoolkit.org
    http://mail.dojotoolkit.org/mailman/listinfo/dojo-contributors
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120515/605d84e2/attachment-0001.htm
  • Sam foster at May 15, 2012 at 3:53 am
    Hi Brian,
    Dont worry about attempting applying any of these changes; it was just
    an hour or so of work to explore the idea on my part. I didn't pull
    request because I knew I wasn't really taking part in the discussion
    and was probably off-base.

    Re: History API, it should probably be the other way around, with a
    new dojo/history and dojo/hash being a fallback or shim to give
    history support to older browsers; this was the intention of the
    history api, and is how Backbone's router and others work. I do think
    forward-compatible support for HTML5 APIs is important today. I know
    when we went shopping for a framework, a router and good history
    support were among a few gating factors we used to help produce a
    shortlist. Dojo didn't make the list for this (and a few other)
    reasons.

    /Sam
    On Mon, May 14, 2012 at 7:08 PM, Brian Arnold wrote:
    Hey there Sam,

    I appreciate the effort, but it isn't quite what was agreed upon in the
    meeting. "dojo/router" as a MID is still going to provide a singleton, and
    there'll be a base class that defines something more like what your fork is
    looking like, probably something like "dojo/router/RouterBase" or something
    else if people have a better naming convention there.
  • Bill Keese at May 9, 2012 at 3:34 pm

    On Wed, May 9, 2012 at 11:15 AM, Brian Arnold wrote:

    I received some off-list feedback commenting that the third parameter to
    `register` of `isBefore` was not the most intuitive, and that perhaps it
    should be a string like "before" or "after", which may help make things a
    bit more clear and feel a bit more like the dom-const `create` and `place`
    methods. Additionally, it was suggested that instead of being a parameter,
    that perhaps it should be a function that hangs off of register, like
    `router.register.before(route, callback)`. After some discussion,
    router.register.before wins and is what's implemented now. It definitely
    feels cleaner too.
    Hmm, although you can document this in the reference guide, it sounds like
    it won't show up in the API documentation. We don't have a plan to
    document nested functions.

    I can't think of anywhere else in dojo where we have nested functions (ie,
    twice removed from the module return value).
    -------------- next part --------------
    An HTML attachment was scrubbed...
    URL: http://mail.dojotoolkit.org/pipermail/dojo-contributors/attachments/20120510/ffb4c53d/attachment.htm

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupdojo-contributors @
categoriesdojo
postedApr 25, '12 at 12:20a
activeMay 15, '12 at 9:43a
posts20
users12
websitedojotoolkit.org

People

Translate

site design / logo © 2021 Grokbase