8.2.8. Public instance identification
By publishing a resource, we make available to the general public a specific perspective on that resource. Concretely, we associate the resource with an identifier in the RemoteScheme (see Scheme Based Design of Resource Identifiers), which allows any PDR to retrieve it from the internet.
Note
|
We deliberately introduce an error here and that is that public resources should have an identifier in the RemoteScheme. It should not; it should be in the PublicScheme. This sections builds up to that conclusion. |
While we publish both context- and role instances, an end user will start by visiting a public context. In this context, he is given a Visitor role (not necessarily by that name) that is defined as a calculated role. The result of the calculation should be sys:Me (the indexed instance of sys:PerspectivesSystem$User).
The modeller publishes a context using the following syntactical construct:
case CouchdbServer external -- The location of the CouchdbServer_. property Url (String) property Name (String) public Visitor at extern >> Url = sys:Me perspective on extern props (Url, Name) verbs (Consult)
The keyword at
should be followed by an expression that yields a Url in runtime, as evaluated with respect to the current context.
Note
|
The result of this expression must end on a forward slash! The compiler cannot check this; only a runtime error will reveal that the forward slash is missing. |
On processing the ARC file, the PDR creates two roles with the given perspective:
-
a calculated Visitor role: user Visitor = sys:Me
-
an Enumerated role VisitorProxy, with a Calculated Property Url = extern >> Url
Actually, for public <Name> at <loc>
, it creates both <Name> and <Name>Proxy.
This does NOT mean that the user who created an instance of CouchdbServer, has specified that instances of this type are associated with the RemoteScheme, at the server located by Url. He may have stored his instance locally, in the default store, or any other place of his choosing. But it DOES mean that a version of the CouchdbServer, as defined by the published perspective, is available at that location.
It also means that any other resources that a Visitor has access to according to this perspective, are stored there. Or at least, versions that the Visitor is allowed to see.
It’s almost as if there was a real user filling an instantiated VisitorProxy role in that context, whom the creator of the CouchdbServer instance synchronised with. As if the creator’s PDR sent a Transaction with all deltas required to construct the resource versions, according to VisitorProxy’s perspective. And as if VisitorProxy had ordained that resources of this type should be identified with the RemoteScheme and thus be saved to that particular location.
Actually, that is pretty much how it is implemented. But instead of there being another PDR receiving the Transactions for Visitor, we have the creator’s PDR do it for him, as it were impersonating the VisitorProxy. Transactions for VisitorProxy are caught just prior to sending them and then interpreted locally, making sure that the resources are saved at the remote location.
8.2.8.1. Interpreting Transactions for VisitorProxy
MonadPerspectivesTransaction is defined as ReaderT (AVar Transaction) MonadPerspectives. Transaction has a member called typeSchemeMap and this contains associations between a resource type and a scheme for identifying its instances with. Almost always we have that member carry the local user’s associations, but on interpreting a Transaction for the VisitorProxy role, we replace it with a scheme that maps any resource to RemoteScheme with a url value as calculated in VisitorProxy$Url.
8.2.8.2. Switching to the Visitor perspective
A user with access to a context that has a public facing perspective, may want to switch to the Visitor role so he can see things from that perspective. We actually have the user interface to do so. We can automatically detect that a context has such a perspective (remember we have marked the context type as such). This allows us to add GUI functionality to switch to the public perspective at once.
Note that all peers in roles that have another perspective, will actually store the context and its roles privately (wherever they have ordained).
Will these two versions not conflict? No, because resources are looked up and cached using their full (i.e. schemed) identifier. So a user might have the same context in his cache twice: once in a public version, once in his (the user’s) private version. When looking at it from a public perspective, he will see different things (likely less than in his private role). But that is as it is meant.
8.2.8.3. Making known where a public context can be found
So far, we have only shown how those who have access anyway (in a non-visitor role) to a context with a public perspective can switch to the visitor role. How do we publish the existence (and location) of such a context? First, it should be really easy to obtain the url of such a context through the GUI. This allows us to use it as an ordinary link in an ordinary web page. Notice, however, that the plain url will not cause MyContexts to become active and capture it; to do so, we should add the url as an url argument to https://mycontexts.com.
How do we include a link to a public context on a page displaying a user’s perspective on another context? If the origin page is public, it is easy: the link identifier should automatically be in the RemoteScheme format. Note: we speak of 'link' but really it is a nested context role whose filler is a public context.
However if the origin page is private, by default the filler of the nested context role will be in a format that leads to the user’s local copy of it. This is a problem for enclosed contexts that should have lists of public contexts. This is rather common: think of the Repositories in MySystem.
We can create a role instance with a public filler in an (automatic) action because we can say:
bind publicrole <expression> to RoleX
where <expression> can yield an identifier in the remote scheme. We can create a perspective with a property that can be filled with the plain url of a public context and then we can have an action create a role with the value of that property as the identifier of the filler. So peers can create links to public contexts in enclosed contexts.
It can be easier than that. The author of an enclosed page might drop a role taken from a public context onto it, thereby creating a new role whose filler has the remote scheme.
8.2.8.4. No backlinks from a public role!
In order to be able to traverse a role filler relationship quickly, we not only keep a link from a role to its filler, but also to all the roles it fills. We call these backlinks. However, if a public role is used as filler, we register no backlinks on it. In most cases, the resource in the public location cannot be changed by the Visitor of that location. This would prohibit creating a private role filled with the public role.
8.2.8.5. When a Visitor adds a role to a public context
Let’s start out by noting that a Visitor should not have a perspective that allows her to modify the public facing perspective on a context. However, should there be an unlinked role in the public context, she might be able to add an instance to that. The scope of that instance would be restricted to her own umwelt. But it might be shared, too, with for example the contexts' Author. He would then keep the instance in his own umwelt (the scope of the instance would include both this single Visitor and the Author). Now think of the deltas the Visitor sends to the Author. It should include a ContextDelta. What shape does the identifier of the context take? Is it the full remote address, or just the guid? Since the Author keeps the instance in his own Umwelt, he gets to decide where to store it - so the delta should just have the guid. As a consequence, this situation is no exception to the general rule that says that deltas should be in terms of guids without their storage schemes.
8.2.8.6. Dropping a private role onto a public page
A Visitor might drop a role from a closed context onto the public context and then create an (unlinked) role instance that is filled by it. For example, he might sign up by dropping his Me role in the Volunteer role drop zone. This will create a Volunteer instance filled by a private role; but notice that this Volunteer instance is not linked to from the public context. There may be (indeed, must be for this example to be useful) another role that has a perspective on Volunteer, e.g. Organizer, but then the new Volunteers PDR will send a delta to him or her describing the new Volunteer role and its filler. The invariant is that there cannot be a role instance in a public facing perspective that is filled by a role from an enclosed context.
8.2.8.7. The case for the 'public' scheme
Above, I wrote that the author of an enclosed page might drop a role taken from a public context onto a private context, thereby creating a new role whose filler is public. If we adhere to the rule that a delta should be expressed entirely in terms of guids without their storage schemes, we run into a problem. The receiver of the delta may or may not have that filler role in his umwelt - and likely he has not, as the entire purpose of this excercise was to share a public resource.
The author’s PDR should create a binding delta whose filler slot contains an identifier in the remote scheme. But how does it distinguish this situation from that of a role filled by a private role stored in the remote scheme?
It cannot. Remember that, even though the filler role is available in a published perspective, it is not declared public itself in the model. The role is not public; it has also been published. So type reflection will not decide the issue.
Neither can it decide on grounds of the storage location. The Author may have that storage location in use to store his private copies of resources, or he may not. If he does not, he clearly intends to share a reference to a public resource. But if he has, we cannot know!
There seems but one solution and that is to encode in the filler identifier itself that the author intends to refer to the version of its referred role that is in the Visitor (public) Umwelt. We could do this with a new scheme: 'public:'.
It would be quite convenient if the role that was dropped was already identified in the public: scheme. But that seems to imply that all identifiers of resources in a public Umwelt should be in the public: scheme. And this is how we implement it. So, any resource that is to be available publicly, has an identifier in the public: scheme. All resources stored in a publicly accesible store on the internet have public: identifiers. Note, however, that for example the author of such a resource has stored it locally, for example with the default: scheme.
The public: scheme is just like the remote: scheme, but for the scheme name itself. There is only one situation in which public: identifiers are created, and that is when a Transaction for a Proxy role is executed.
The rule for deltas is then that all default:, local: and remote: schemed identifiers should be reduced to their guids, whereas the public: identifiers are not.
8.2.8.8. Overlapping public perspectives
In model://perspectives.domains#CouchdbManagement, both a Repository and a ModelManifest have a public role. In fact, the Repository$Manifests
role property NameSpace
falls in both public perspectives. Either perspective publishes to a different location. Where does the contextrole of ModelManifest end up?
Let’s step back here and reflect for a moment on the meaning of public roles. A public role is one that can be taken on by any member of the public (this is not entirely true, as the model’s author may require particular role instances to fill the public role - but the general thrust of the argument remains). As a consequence, the resources in the public perspective are available to the public - regardless of the exact url where they are published.
One approach to the problem posed above, therefore, is just to publish Repository$Manifests
and its property NameSpace
to both locations. Consider for a moment however, what would happen if a visitor of Repository opens a ModelManifest instance. Doing so he assumes the ModelManifest$Visitor
role, which gives him access to, a.o., the Versions role. He should therefore see instances of Versions. But as Repository$Visitor
does not have a perspective on that role, the representation of the ModelManifest that is stored at the particular url where Repository$Visitor
has published, it contains no Versions. No Versions will turn up! In contrast, were the user to visit ModelManifest directly - and see the version that is stored at the url where the Visitor of ModelManifest stores its resources - he would find instances of Versions. Clearly this is not what we want.
For that reason we have adopted another strategy. We define, for each context instance, an optional public url. It will be available if there is a public user role in that context. Now, for each role instance, if its context does have a public url, we will publish it there. If it has no public url of itself (and we are indeed publishing) we use the public url of the user we are actually publishing for.
Let’s consider some examples.
In case of the role Repository$Manifests
(with property NameSpace
), we find that the Repository instance does have a public url - it is the url where its Visitor role publishes to. So, even if we’re in the middle of publishing for Modelmanifest$Visitor
, we’ll publish Repository$Manifests
to the url of Repository$Visitor
rather than to ModelManifest$Visitor
's url.
In case of, say, the System$User role, things are different. Both Repository$Visitor and ModelManifest$Visitor have a perspective on User - but System doesn’t have a public role of its own. So, an instance of User ends up at both public locations.
Important
|
The takeaway is that the public location of a context instance overrides the public location of the public user role we’re publishing for, for that context’s roles. The public location of a context instance is defined as the location of its (first) public user role of its type. |