Skip to content

Instantly share code, notes, and snippets.

@mindrones
Last active March 1, 2019 22:22
Show Gist options
  • Save mindrones/2625459af28e5b5d5e3277802f70abf8 to your computer and use it in GitHub Desktop.
Save mindrones/2625459af28e5b5d5e3277802f70abf8 to your computer and use it in GitHub Desktop.

Serialising state and query in a URL

Assuming to describe a web application with a statechart, should we desire to share its state, how can we express it in the URL without relying on a server or hashing the state?

Can we make the current state of the application explicit and possibly user-friendly?

Can we also separate the current state of the app from the query and the actual data? For example: if we share a URL after having interacted with a chart, selecting people aged from 35 to 50 and living in France:

  • the state is selected (as opposed for example to unselectedor idle),
  • the query is age=[35, 50]&countries=France,
  • the data is a list of people aged between 35 and 50 (these could be 1000s).

URL reserved chars

From https://www.rfc-editor.org/rfc/rfc1738.txt:

Reserved:

   Many URL schemes reserve certain characters for a special meaning:
   their appearance in the scheme-specific part of the URL has a
   designated semantics. If the character corresponding to an octet is
   reserved in a scheme, the octet must be encoded.  The characters ";",
   "/", "?", ":", "@", "=" and "&" are the characters which may be
   reserved for special meaning within a scheme. No other characters may
   be reserved within a scheme.

   Usually a URL has the same interpretation when an octet is
   represented by a character and when it encoded. However, this is not
   true for reserved characters: encoding a character reserved for a
   particular scheme may change the semantics of a URL.

   Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
   reserved characters used for their reserved purposes may be used
   unencoded within a URL.

   On the other hand, characters that are not required to be encoded
   (including alphanumerics) may be encoded within the scheme-specific
   part of a URL, as long as they are not being used for a reserved
   purpose.

So:

  • reserved: ;/?:@=&
  • usable non-alphabethic: $-_.+!*'(),

URL components

  • Path to a page: "/" indicates a path to a page: /foo/bar/page

  • Serialised state: ! could indicate the start of the serialised state:

    • in the case of a simple machine this could be !highlighted or !selected
    • if we want to serialise a particular state of a statechart (as a compound of all the states of all its machines) we need a way to serialise the current state tree (see below)
  • query: the URL query component ?age=35-50&countries=France.

A com'lete example would be: /foo/bar/page!selected?age=35-50&countries=France

How to serialise the current state tree

Since the current statechart can contain parallel and hierarchical machines, the current state is a tree.

We need to serialise it into a string.

State name limitations

Of course, the choice of legit characters for the state serialization pose a limitation on those we can use in state names.

Parallel state

The serialisation of a parallel state must communicate that it contains multiple values and the only options I see here are "(" and ")" to avoid using [] or {} that would have to be encoded. Separators could be "," or "+":

- "," as separator: m(a,b,c)

- "+" as separator: m(a+b+c)

Hierarchical state

We could use "." to indicate nesting [1]:

/foo/bar/page!a.k.g(b(l.s,m.t),c.e.f(g.k,h.r,i.y),d)

because:

  • . is the same symbol we use to nest objects in javascript and is well readable;
  • it would be nice to use _ and - in compound state names ("off-limit" , "foo_loading"), and these make nesting not particularly readable for me;
  • * and $ don't seem to make nesting very readable again and could be used for something else, especially if we'll need wildcard symbols;
  • ' seems interesting, but:
    • I'm not sure if dirty'pending'foo'bar can be immediately interpreted as a nesting
    • can be used for parallel machines: /foo/bar/page!a.k.g(b(l.s'm.t),c.e.f(g.k'h.r'i.y)'d)
    • it would be problematic if used as the string delimiter of the URL in code.

Data

Should we serialise queries near the related state?

In this case, for example, it's very difficult to read that foois nested under loading: /my/page!authorised.loading?progress=37.foo?foo=1&bar=2

How would we describe a query (say data=2) not related to a particular state?

  • /my/page!authorised.loading?progress=37?data=2 <- should we repeat the "?" to communicate that this is a global query?
  • /my/page?data=2!authorised.loading?progress=37 <- or should we use global query before the state representation?

Could we use $ or *?

/my/page!authorised.loading?progress=37$?data=2

Here $ would mean "end of state description, global query starts here".


[1]

Here's a list of characters we could use and an example of how it would read if we used "," or "+" as parallel state separator:

  • $
/foo/bar/page!a$k$g(b(l$s+m$t)+c$e$f(g$k+h$r+i$y)+d)

/foo/bar/page!a$k$g(b(l$s,m$t),c$e$f(g$k,h$r,i$y),d)
  • -
/foo/bar/page!a-k-g(b(l-s+m-t)+c-e-f(g-k+h-r+i-y)+d)

/foo/bar/page!a-k-g(b(l-s,m-t),c-e-f(g-k,h-r,i-y),d)
  • _
/foo/bar/page!a_k_g(b(l_s+m_t)+c_e_f(g_k+h_r+i_y)+d)

/foo/bar/page!a_k_g(b(l_s,m_t),c_e_f(g_k,h_r,i_y),d)
  • .
/foo/bar/page!a.k.g(b(l.s+m.t)+c.e.f(g.k+h.r+i.y)+d)

/foo/bar/page!a.k.g(b(l.s,m.t),c.e.f(g.k,h.r,i.y),d)
  • *
/foo/bar/page!a*k*g(b(l*s+m*t)+c*e*f(g*k+h*r+i*y)+d)

/foo/bar/page!a*k*g(b(l*s,m*t),c*e*f(g*k,h*r,i*y),d)
  • '
/foo/bar/page!a'k'g(b(l's+m't)+c'e'f(g'k+h'r+i'y)+d)

/foo/bar/page!a'k'g(b(l's,m't),c'e'f(g'k,h'r,i'y),d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment