In order to ensure that writing modules is easy and doesn't result in spaghetti sauce spilling everywhere, Radiance has rather strict encapsulation rules in place. However, since modules also need to be able to interact in some form, be it due to extending or sharing functionality, we need to offer some kind of interface to exchange data between them. If possible in a well-specified manner.
Such a mechanism has only existed for two cases so far. The first being the interfaces themselves, which abstract away an entire implementation of a component. The second is the hooks and triggers system, which allows reactionary interaction between modules (source to clients). The last thing that is left is the opposite of the hooks, allowing modules to retrieve data from others, or telling them to do something (clients to source).
Of course, this can be “fixed” in any case by simply demanding that modules expose their functions. This is fine when you want to interact with a very specific module. It is not fine when you need to interact with an arbitrary module or interface implementation. The case comes up when you need to execute common operations to get some kind of action or data across, without knowing what the target in specific is.
The scenario is most easily imaginable when it comes to interfaces. An interface completely hides its implementing module. Modules that use an interface should not have to know anything about the implementation. Of course, here the solution would be to add more functions to the interface, but that breaks down if you factor in common operations. Defining an extra function for each interface would be a waste, when instead you could generalise the same for not only all interfaces, but every module.
What I've worked out now is a
resource
system. Resources are supposed to be things that are accessible from any module, for any module. Interfaces will delegate to their current implementation. Each resource type has a resource-locator function, which receives a module and a number of arguments and returns the resource according to its arguments. That sounds extremely general, and it is very general.It is so general in order to allow extensions to the system. Currently I have planned to standardise four resource types, but there could be plenty more. The three standards are
domain
,api
,static
, andpage
. The first does what you might expect, it simply returns the domain the module occupies. The latter three are all about getting the path to the resources associated. Radiance reserves exactly two paths on all domains for itself, in order to ensure that static files and the API are always reachable the same way. The last one is a locator that translates a page name to the actually defined path.So, this resources system solves the problem of common, agnostic data exchange. While you certainly can define resources that only work for one specific module, it probably isn't the best candidate. Instead, for cases like it's probably still best to exhibit a module-specific set of functions that others can use directly. However, there is one incentive that could make you want to implement things as a resource, rather than a standard function.
Accompanying the resources extension to Radiance is a pattern extension. This changes the semantics of the already existing URI to fit for use in templates and other situations where URIs need to be resolved, rather than compared. Patterns are a subclass to the standard URI and implement their own parsing scheme. The parsing scheme incorporates both allowing placeholders to be filled in later and resource references.
This difference in intent and the extension to its syntax that are necessary to support patterns makes them fundamentally incompatible with standard URIs, so I could not fuse the two together and save on another segmentation. However, patterns can be used just like any other URIs, with the minor difference that they may need to be resolved down to a URI in order to be comparable.
When a pattern is resolved it replaces all of its delegated parts, such as placeholders and resources, with their actual values and then uses that as an information to build a standard URI. Patterns solve the conundrum of making it easy to reference other things in templates. Using patterns you can now provide a link to the login page of the
auth
implementation doing something like<auth>/<auth:page;login>
. Angled brackets are resource references. The syntax specifies the module, resource type (defaulting todomain
), and finally a list of arguments. I tried to lean this onto the way logical pathnames are represented, in order to retain at least some manner of familiarity. Using parenthesis did not seem like a good way to go about it, as it looked too confusing amidst the already very un-lispy URI syntax. Placeholders are specified using wavy brackets and a name. Resolving a pattern with placeholders therefore requires a plist of arguments to supply. Usually when I write URIs with placeholders in my templates I uppercase the replaced parts, but I felt that that was too much of a breach, since people might very well want to use uppercase letters without it automatically generating a placeholder.In order to further ease the use of patterns in templates, the
r-clip
extension will define a few additional attributes, namely@href
,@src
, and@link
, which will take a pattern (and optionally arguments) to automatically parse and include in the generated output. That way substituting the plain HTML+lQuery way should be straightforward and easy to do.I'm really glad that I finally found a seemingly workable solution for this problem. I'm still working on the details now, as some things seem much more shaky to me than I'm comfortable with. Hopefully I'll have the proper system hacked together soon though. After that, the only critical issue remaining in Radiance is the reverse routing. I'll probably talk about that whenever I've found the beginning of a solution for it.
Written by shinmera