medialize / URI.js

Javascript URL mutation library
http://medialize.github.io/URI.js/
MIT License
6.26k stars 475 forks source link

Fragments: allow fragmentPrefix to define the entire delimiter #166

Open barneycarroll opened 10 years ago

barneycarroll commented 10 years ago

Bear with my pseudo-code for the sake of example. fragmentPrefix currently behaves something like this

var _rootPrefix = '#';
var _actualPrefix = _rootPrefix;

function fragmentPrefix( value ){
  _actualPrefix = _rootPrefix + value;

  var parts = this.toString().split( _actualPrefix );
  var uriProper = parts[ 0 ];
  var fragment = parts[ 1 ];
}

The way I'd like to see this work is to eliminate what I've called _rootPrefix above and just delimit with the supplied value. This would allow my current use case of working with Mithril, whose default SPA routing mode is to split on the search ?.

rodneyrehm commented 10 years ago

To rephrase your question:

you're using the "search mode" and you would like a way to manipulate the URL contained in the query string rather than the fragment?

Basically you want the fragment URI accessor (issue #129) also available on .query(). For the time being, you could use the function in that gist and point it to this._parts.query rather than this._parts.fragment.

I wasn't going to merge that gist before version 2.0.x because it would be a BC break. But if we want to enable the same thing on query, it might make more sense to expose the feature at queryUri() and fragmentUri instead?!

barneycarroll commented 10 years ago

Ah – you're right, there are obvious implications there that would need attention if this were gonna make it in. How about I fork as you suggest then report back after a bit of real-world usage? TBH I'm very confused by the fragmentURI API at the moment, so I wouldn't go taking my advice immediately!

rodneyrehm commented 10 years ago

The fragment plugins weren't that good. they were never meant to be. They were intended to act as a hint as to how someone could extend the default behavior. That didn't work out, people started using the plugins directly.

The separation of FragmentURI and FragmentQuery didn't make too much sense, considering a more familiar API could be provided by simply treating the fragment as a URI - after all, ?foo=bar is a valid URI (relative to the current origin). That's why I tried to float that new fragment() thing shown in the gist.

It never occured to me that the same story applies to the query as well. Having thought about this for almost 3 seconds, I'm tempted to throw that function onto URI, but nut as fragment(), but fragmentUri() - that way queryUri() would be logical.

barneycarroll commented 10 years ago

Yeah, I'm in a good position to work with this because like @JamesMGreen, I'm also building an SPA working with several REST APIs (and user-facing routing – each with their own quirks). I'm going to try hacking about with it until I get something that works idiomatically, at which point I can come back with coherent suggestions :)

barneycarroll commented 9 years ago

After trying (and failing) to rationalize an elegant API for making fragment abuse more idiomatic, I realised that I could sidestep the whole issue with a minimum of fuss by passing URI().query() or URI().fragment() to URI().

Prefix-free fragment abuse with vanilla URIjs

// Whereby the SPA route overloads the search fragment
var spaRoute = URI( URI().query() );

// Assuming a location of 'http://www.example.com/?/single/page/application?query=param'
spaRoute.segment(); // [ 'single', 'page', 'application' ]
spaRoute.query( true ); // { query : 'param' }

AFAIC, the above is terse and self-evident enough to be left as is – I don't want a higher order interface to magic away what's going on with the URIjs API.

Doesn't solve the problem for people using Google's hashbang method (or other arbitrary delimiters). Like I said I really tried to come up with a generic solution that dovetailed neatly with URIjs's API, but I concluded that when authors are using such hacks they ultimately need to accept that the full power of comprehensive URI abstraction can't be magicked onto a routing format that doesn't fit with the documented standards. Given that we can't second guess the SPA developer's requirements for the abstraction of abused fragments (is the semantic distinction between query and fragment even relevant, from an API perspective, if it's being intercepted and used for arbitrary purposes?), I think it's acceptable to resort to string methods when the author demands a delimiter which doesn't align exactly with existing standardised abstractions:

I'd suggest a URI.from method, which accepts a delimiter string argument and returns a variation of URI that only parses any URI content after delimiter. This solves #129 inasmuch as you're never subclassing or modifying URIjs internals, so you can create as many of these as you want. Housekeeping falls back on the author's shoulders, leaving URIjs free of cruft and focussing on what it does best – parsing according to robust standards.

Fragment abuse from arbitrary delimiters with higher-order interface to URIjs

// Pass an arbitrary string to create a 'variation' of URIjs which only parses anything prior to that string
URI.from = function fragmentAbuseFactory( delimiter ){
  return function fragmentAbuseInterface( input ){
    return URI( 
      URI( input )
        .valueOf()
        .split( delimiter )
        .slice( 1 )
        .join( delimiter )
    );
  };
};

Here's a JSbin with a few pseudo-tests. Do you think this is a viable way forward/out of the fragment abuse problem?