Joose 3 internals – Part II. Meta-model
This post will be very technical and contain a gory details about the implementation of the meta-model in Joose. It will be useful only for extension authors, if you are not planning to write one – nothing interesting ahead.
Well, you were warned.
Everything is a Property.
Property is the basement of the meta-model. It is an abstract name/value pair.
Everything in Joose is a property, i.e. attribute is a property, method is a property, role’s requirement is a property as well.
As it’s been mentioned in the previous post, property is implemented as a class from the 1st meta-level: Joose.Managed.Property (it has a Joose.Proto.Class as the meta-class).
Property can be shared among several classes (via composition relationships) so its totally self-contained and don’t contain any links to its “current” container, only to container it was defined in.
Property’s life stages.
By itself, property is just a description. To receive a concrete implementation (one can say “materialize” property) it should be applied to target. It can be un-applied as well (remember the mutability feature). In general, property has the following life stages: `preApply/apply` and `unApply/postUnApply`. More on this below.
Everything is a PropertySet.
Joose.Managed.PropertySet inherits from Joose.Managed.Property. It is an abstract, unordered collection of properties, indexed by name:
On the set of property sets (sorry for tautology) we’ll define the following operations:
- A.cleanClone – create a “clean” clone of A (without any properties)
- A.clone – create a clone of A, including properties (only properties container will be cloned, not properties itself)
- A.alias(what) – add aliases for properties, listed in what
- A.exclude(what) – delete properties, listed in what
- A.flattenTo(B) – for each property in A,
- check if the property with the same name in B exists
- if there is one, and its a special conflict marker property or the property of A itself, then just skip it
- if there is one and its different from the above – then replace it in B with the conflict marker
- otherwise, copy that property to B
- check if the property with the same name in B exists
- A.composeTo(B) – for each property a in A,
- check if B has own property with the same name
- if not – then copy that property to B
- A.composeFrom(B, C, …)
- create a clean clone of A
- flatten each argument into the clean clone (possible aliasing & excluding some properties first)
- compose the result to A
Listed operations will implement the traits spec. All subclasses of PropertySet will keep the semantic of these operations.
PropertySet’s life stages.
PropertySet simply propagate the `preApply/apply/unApply/postUnApply` stages to its elements. Note, that current implementation is still written in the imperative spirit and rewriting it in more functional way will possible allow us to remove the `unApply/postUnApply` stages at all.
Mutable PropertySet.
Joose.Managed.PropertySet.Mutable inherits from Joose.Managed.PropertySet. Its a bit more specialized property set. It track the depended sets (derivatives) and propagate the changes through them. Mutable set B said to be the derivative of A, if it has been composed from A (possibly along with other sets).
Mutable PropertySet can be in 2 states: opened (this.opened > 0) & closed (this.opened == 0). Mutation is only allowed when property set is opened.
During “open” operation, when switching from closed state to opened the set is de-composed – cleaned from all properties which weren’t defined in this set. Before that all derivatives are also opened. Note, that the set can be opened several times – subsequent “open”s will be no-ops. Each “close” operation should have matching “open”. Initially the set is opened (this.opened == 1)
During “close” operation, when switching from opened state to closed, the set is re-composed using the data from “composedFrom” attribute and inherited “composeFrom” method.
Higher-order PropertySet.
Joose.Managed.PropertySet.Composition inherits from Joose.Managed.PropertySet.Mutable. Its an abstract higher-order mutable property set – a property set, which has other property sets as the elements:
Its no longer unordered and defines the order of properties processing (`processOrder`), also defines the reverse processing order, which is important for unapplying things.
Composition’s behavior.
Composition translates the higher-level operations to individual properties. That is, when composition A is asked to be flattened to composition B for example, it flat it’s individual properties:
Other operations are defined in the same way. Note, that composition is a mutable property set.
Stem
Joose.Managed.Stem inherits from Joose.Managed.PropertySet.Composition. Its a concrete implementation of composition:
Stem contains the following properties (in the processing order): [ ‘attributes’, ‘methods’, ‘requirements’, ‘methodsModifiers’ ]. Each of those properties is a property set of individual properties – collection of attributes, methods, etc.
Stem is the first property, which is aware of the real JavaScript class it belongs to (targetMeta attribute).
During re-composition stem initiates the `preApply/apply` actions of the whole properties subtree. In the same way, during de-composition, it initiates the `unApply/postUnApply` stages.
Note, that during `preApply` stage the stem is still opened, and can mutate. During `apply` stage, the class definition is “final” and should only be somehow “materialized” in the prototype of the class, but not changed. The class in which the property should “materialize” itself will be passed as the 1st argument for “apply” method.
Conclusion
This post contains the details about the meta-model implementation in Joose3. In the next post of this series we’ll briefly describe the anatomy of class from the 2nd meta-level – Joose.Managed.Class.