From 5fda3f2fa83e84bcb38f7438a2a4034d13094f50 Mon Sep 17 00:00:00 2001 From: Valerii Kovalchuk Date: Fri, 11 Mar 2016 16:27:36 +0200 Subject: [PATCH] Document MuranoPL Metadata Change-Id: Iadec4116f9045eef9f861a6060f91dae5b805338 --- doc/source/draft/appdev-guide/murano_pl.rst | 2 + .../draft/appdev-guide/murano_pl/metadata.rst | 316 ++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 doc/source/draft/appdev-guide/murano_pl/metadata.rst diff --git a/doc/source/draft/appdev-guide/murano_pl.rst b/doc/source/draft/appdev-guide/murano_pl.rst index 209b83d7c..e65718be7 100644 --- a/doc/source/draft/appdev-guide/murano_pl.rst +++ b/doc/source/draft/appdev-guide/murano_pl.rst @@ -17,4 +17,6 @@ YAQL languages. The sections below describe these languages. .. include:: murano_pl/core_lib.rst .. include:: murano_pl/reflection.rst .. include:: murano_pl/statics.rst +.. include:: murano_pl/metadata.rst .. include:: murano_pl/actions.rst + diff --git a/doc/source/draft/appdev-guide/murano_pl/metadata.rst b/doc/source/draft/appdev-guide/murano_pl/metadata.rst new file mode 100644 index 000000000..b60f8e9c4 --- /dev/null +++ b/doc/source/draft/appdev-guide/murano_pl/metadata.rst @@ -0,0 +1,316 @@ +.. _metadata: + +MuranoPL Metadata +~~~~~~~~~~~~~~~~~ + +MuranoPL metadata is a way to attach additional information to various MuranoPL +entities such as classes, packages, properties, methods, and method arguments. +That information can be used by both applications (to implement dynamic +programming techniques) or by the external callers (API consumers like UI or +even by the Murano Engine itself to impose some runtime behavior based on +well known meta values). Thus, metadata is a flexible alternative to adding new +keyword for every new feature. + +Work with metadata includes the following cases: + +* Defining your own meta-classes +* Attaching metadata to various parts of MuranoPL code +* Obtaining metadata and its usage + +Define meta-classes +------------------- + +Define meta-class with the description of arbitrary metadata. Meta-class and +regular class have two differences. The ``Usage`` attribute of meta-class +equals to ``Meta``, while the ``Usage`` attribute of regular class equals to +``Class``. The default value of the ``Usage`` attribute is ``Class``. +Meta-class also has additional attributes (``Cardinality``, ``Applies`` and +``Inherited``) to control how and where instances of that meta-class can +be attached. + +Cardinality ++++++++++++ + +The ``Cardinality`` attribute can be set to either ``One`` or ``Many`` and +indicates the possibility to attach two or more instances of meta-class to a +single language entity. The default value is ``One``. + +Applies ++++++++ + +The ``Applies`` attribute can be set to one of ``Package``, ``Type``, +``Method``, ``Property``, ``Argument`` or ``All`` and controls the possible +language entities which instances of meta-class can be attached to. It is +possible to specify several values using YAML list notation. The default value +is ``All``. + +Inherited ++++++++++ + +The ``Inherited`` attribute can be set to ``true`` or ``false`` and specifies +if there is metadata retained for child classes, overridden methods and +properties. The default value is ``false``. + +Using of ``Inherited: true`` has the following consequences. + +If some class inherits from two classes with the same metadata attached and +this metadata has ``Cardinality: One``, it will lead to emerging of two +metadata objects with ``Cardinality: One`` within a single entity and will +throw an exception. However, if the child class has this metadata attached +explicitly, it will override the inherited metas and there is no conflict. + +If the child class has the same meta as its parent (attached explicitly), +then in case of ``Cardinatity: One`` the meta of the child overrides the +meta of the parent as it is mentioned above. And in case of +``Cardinatity: Many`` meta of the parent is added to the list of the child's +metas. + +Example ++++++++ + +The following example shows a simple meta-class implementation: + +.. code-block:: yaml + + Name: MetaClassOne + Usage: Meta + Cardinality: One + Applies: All + + Properties: + description: + Contract: $.string() + Default: null + + count: + Contract: $.int().check($ >= 0) + Default: 0 + +``MetaClassOne`` is defined as a meta-class by setting the ``Usage`` +attribute to ``Meta``. The ``Cardinality`` and ``Applies`` attributes determine +that only one instance of ``MetaClassOne`` can be attached to object of any +type. The ``Inherited`` attribute is omitted so there is no metadata +retained for child classes, overridden methods and properties. In the +example above, ``Cardinality`` and ``Applies`` can be omitted as well, as +their values are set to default but in this case the author wants to be +explicit. + +The following example shows meta-class with different values of attributes: + +.. code-block:: yaml + + Name: MetaClassMany + Usage: Meta + Cardinality: Many + Applies: [Property, Method] + Inherited: true + + Properties: + description: + Contract: $.string() + Default: null + + count: + Contract: $.int().check($ >= 0) + Default: 0 + +An instance (or several instances) of ``MetaClassMany`` can be attached to +either property or method. Overridden methods and properties inherit +metadata from its parents. + +Attach metadata to a MuranoPL entity +------------------------------------ + +To attach metadata to MuranoPL class, package, property, method or method +argument, add the ``Meta`` keyword to its description. Under the +description, specify a list of meta-class instances which you want to attach +to the entity. To attach only one meta-class instance, use a single scalar +instead of a list. + +Consider the example of attaching previously defined metadata to different +entities in a class definition: + +.. code-block:: yaml + + Namespaces: + =: io.murano.bar + std: io.murano + res: io.murano.resources + sys: io.murano.system + + + Name: Bar + + Extends: std:Application + + Meta: + MetaClassOne: + description: "Just an empty application class with some metadata" + count: 1 + + Properties: + name: + Contract: $.string().notNull() + Meta: + - MetaClassOne: + description: "Name of the app" + count: 1 + - MetaClassMany: + count: 2 + - MetaClassMany: + count: 3 + + Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + Meta: + MetaClassOne: + description: "Method for initializing app" + count: 1 + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Deploy started') + - $._environment.reporter.report($this, 'Deploy finished') + - $.setAttr(deployed, true) + +The ``Bar`` class has an instance of meta-class ``MetaClassOne`` attached. For +this, the ``Meta`` keyword is added to the ``Bar`` class description and +the instance of the ``MetaClassOne`` class is specified under it. This +instance's properties are ``description`` and ``count``. + +There are three meta-objects attached to the ``name`` property of the ``Bar`` +class. One of it is a ``MetaclassOne`` object and the other two are +``MetaClassMany`` objects. There can be more than one instance of +``MetaClassMany`` attached to a single entity since the ``Cardinality`` +attribute of ``MetaClassMany`` is set to ``Many``. + +The ``initialize`` method of ``Bar`` also has its metadata. + +To attach metadata to the package, add the ``Meta`` keyword to +``manifest.yaml`` file. + +Example: + +.. code-block:: yaml + + Format: 1.0 + Type: Application + FullName: io.murano.bar.Bar + Name: Bar + Description: | + Empty Description + Author: author + Tags: [bar] + Classes: + io.murano.bar.Bar: Bar.yaml + io.murano.bar.MetaClassOne: MetaClassOne.yaml + io.murano.bar.MetaClassMany: MetaClassMany.yaml + Supplier: + Name: Name + Description: Description + Summary: Summary + Meta: + io.murano.bar.MetaClassOne: + description: "Just an empty application with some metadata" + count: 1 + +Obtain metadata in runtime +-------------------------- + +Metadata can be accessed from MuranoPL using reflection capabilities and +from Python code using existing YAQL mechanism. + +The following example shows how applications can access attached metadata: + +.. code-block:: yaml + + Namespaces: + =: io.murano.bar + std: io.murano + res: io.murano.resources + sys: io.murano.system + + Name: Bar + + Extends: std:Application + + Meta: + MetaClassOne: + description: "Just an empty application class with some metadata" + + Methods: + sampleAction: + Usage: Action + Body: + - $._environment.reporter.report($this, typeinfo($).meta. + where($ is MetaClassOne).single().description) + +The ``sampleAction`` method is added to the ``Bar`` class definition. This +makes use of metadata attached to the ``Bar`` class. + +The information about the ``Bar`` class is received by calling the +``typeinfo`` function. Then metadata is accessed through the ``meta`` +property which returns the collection of all meta attached to the property. +Then it is checked that the meta is a ``MetaClassOne`` object to ensure that +it has ``description``. While executing the action, the phrase "Just an +empty application class with some metadata" is reported to a log. Some +advanced usages of MuranoPL reflection capabilities can be found in the +corresponding section of this reference. + +By using metadata, an application can get information of any type attached +to any object and use this information to change its own behavior. The most +valuable use-cases of metadata can be: + +* Providing information about capabilities of application and its parts +* Setting application requirements + +Capabilities can include version of software, information for use in UI or +CLI, permissions, and any other. Metadata can also be used in requirements as +a part of the contract. + +The following example demonstrates the possible use cases for the +meta-classes: + +.. code-block:: yaml + + Name: BlogApp + + Meta: + m:SomeFeatureSupport: + support: true + + Properties: + volumeName: + Contract: $.string().notNull() + Meta: + m:Deprecated: + text: "volumeName property is deprecated" + server: + Contract: $.class(srv:CoolServer).notNull().check(typeinfo($).meta. + where($ is m:SomeFeatureSupport and $.support = true).any()) + + Methods: + importantAction: + Usage: Action + Meta: + m:CallerMustBeAdmin + +Note, that the classes in the example do not exist as of Murano Mitaka, and +therefore the example is not a real working code. + +The ``SomeFeatureSupport`` meta-class with ``support: true`` says that the +``BlogApp`` application supports some feature. The ``Deprecated`` meta-class +attached to the ``volumeName`` property informs that this +property has a better alternative and it will not be used in the future +versions anymore. The ``CallerMustBeAdmin`` meta-class attached to the +``importantAction`` method sets permission to execute this method to the +admin users only. + +In the contract of the ``server`` property it is specified that the server +application must be of the ``srv:CoolServer`` class and must have the +attached meta-object of the ``m:SomeFeatureSupport`` meta-class with the +``support`` property set to ``true``.