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 tounselected
oridle
), - the query is
age=[35, 50]&countries=France
, - the data is a list of people aged between 35 and 50 (these could be 1000s).
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:
$-_.+!*'(),
-
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)
- in the case of a simple machine this could be
-
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
Since the current statechart can contain parallel and hierarchical machines, the current state is a tree.
We need to serialise it into a string.
Of course, the choice of legit characters for the state serialization pose a limitation on those we can use in state names.
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)
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.
- I'm not sure if
Should we serialise queries near the related state?
In this case, for example, it's very difficult to read that foo
is 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)