-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Item14237: Started documenting the new specs format.
- Loading branch information
Showing
2 changed files
with
262 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
%META:TOPICINFO{author="ProjectContributor" date="1494556778" format="1.1" version="1"}% | ||
---+!! Spec Files v2 | ||
%TOC% | ||
|
||
This topic is about further development of %WIKITOOLNAME% spec format. Read | ||
about the history and the reasoning behind the specs in | ||
[[Development.HowToWriteASpecFile]]. This document has more focus on specs supported | ||
by %WIKITOOLNAME% v3. | ||
|
||
%X% *NOTE* This document might not be a comprehensive description of the specs and | ||
may require further improvements. | ||
|
||
#BasicConcepts | ||
---++ Basic Concepts | ||
|
||
Specs v2 are based upon a simple Perl structure of "list of lists". The structure could further | ||
be extended to higher-level formats if considered reasonable. But for most cases Perl data should | ||
be sufficient. A typical spec file would have the following look: | ||
|
||
<verbatim> | ||
#!data | ||
-section => "Extensions" => [ | ||
-section => "DBConfigExtension" => [ | ||
-text => <<EOT, | ||
Database storage backend for LSC | ||
EOT | ||
"Extensions.DBConfigExtension.Connection" => [ | ||
Host => STRING => [ | ||
-default => 'localhost', | ||
-label => 'Database host', | ||
-text => 'Where the database server is located', | ||
], | ||
Port => NUMBER => [ | ||
-default => undef, | ||
-label => 'Database port', | ||
-text => 'Port number where database server is listening', | ||
], | ||
..., | ||
EnableSSL => [ | ||
-type => BOOLEAN, | ||
-default => 1, | ||
-label => "DSN", | ||
], | ||
], | ||
], | ||
], | ||
</verbatim> | ||
|
||
Essentially this is just a Perl array which will be enclosed into square brackets | ||
and fed to =Foswiki::Config= =spec()= method. The array contains several kinds of | ||
elements like section or key definition, etc. | ||
|
||
---++ Directories and files. | ||
|
||
Lets assume that =FOSWIKI_LIBS= shell environment variable contains the path to | ||
%WIKITOOLNAME% libraries. In this case the following directories are used to | ||
look for spec files: | ||
|
||
| *Directory* | *Description* | | ||
| =$FOSWIKI_LIBS= | To load =Foswiki.spec= | | ||
| =$FOSWIKI_LIBS/Foswiki/{Plugins|Contrib|Extension}= | To search for custom spec files. Directories are scanned recursively for files matching =Spec.*= or =*.spec= globs | | ||
| =$FOSWIKI_LIBS/.specCache= | To store specs cached data | | ||
|
||
Each file found could contain different spec formats. More about formats is in [[#SpecFormats][corresponding section]] of this topic. Here it will be | ||
explained how the format is determined for a file: | ||
|
||
1. The file is checked for shebang. Shebang format is a commonly used =#!= prefix followed with a format name defined by a single word (see =\w= character class in [[https://perldoc.perl.org/perlre.html][Perl regular expressions documentation]]). Example: | ||
<verbatim>#!legacy</verbatim> | ||
1. If file name is matching =Spec.*= glob then its extension defines the format. Example: =Spec.data= | ||
1. If the file contains TML section formatting string in a =#= comment string and a line starting with _$Foswiki::cfg{_ or _1;_ strings then it's format is guessed as legacy. | ||
1. The default format is data. | ||
|
||
#SpecFormats | ||
---++ Formats | ||
|
||
There're three Spec formats supported out of the box: =data=, =perl=, and =legacy=. =data= and =perl= are just variants of v2 specs. =legacy= | ||
is the one used in pre-%WIKITOOLNAME% v3 era. Sure enough, it is supported for compatibility matters but its use is | ||
discouraged. | ||
|
||
=data= format is the basic and the simplest one. Its use is described in the [[#BasicConcepts][Basic Concepts]] section. | ||
|
||
=perl= format is somewhat more advanced as file content is considered to be a body of a method called against a object | ||
of a class with =Foswiki::AppObject= and =Foswiki::CfgObject= roles applied. The actual class name is irrelevant and may | ||
vary depending on implementation. The method will have variable =$this= available for use. It must return a list with | ||
valid spec data. | ||
|
||
=perl= format is good for a situation if some complex processing is necessary before spec data could be formed. | ||
|
||
---++ Data Format | ||
|
||
Spec data is a list of items of different kinds: | ||
|
||
* a option is a item prefixed with dash | ||
* a string | ||
* an array ref defining a element body | ||
* a value of any type possibly following a option. | ||
|
||
Elements are the bones of spec body. Currently there are three of them: | ||
|
||
1. Sections | ||
1. Branch nodes | ||
1. Leaf nodes | ||
|
||
The order of the list defines the order of inclusion. I.e. sections may | ||
contain other sections and branch nodes; branch nodes may contain other | ||
branch nodes and leaf nodes. Leaf nodes only contain values. The example in | ||
the [[#BasicConcepts][Basic Concepts]] section defines a top-level section | ||
_Extensions_, a second level section _DBConfigExtension_, a branch node | ||
_Connection_, and a leaf node _Host_, particularly. Note that it is also | ||
implicitly defines branch nodes _Extensions_ and _DBConfigExtension_ as key | ||
_Extensions.DBConfigExtension.Connection_ gets split into subkeys. | ||
|
||
---+++ Section | ||
|
||
Sections are a way of grouping configuration settings into logically tied | ||
sets aimed at easing user perception of the configuration. In other words, | ||
a section "Extensions" is expected to contain per-extension config items; | ||
a subsection "Permissions" of "Files" section is about ownership/ACLs applied | ||
to application data files; etc. | ||
|
||
A section definition is always started with =-section= option. As a matter | ||
of fact it's the only element defined by a option. It's done this way to | ||
distinguish key definitions from sections within a section body. | ||
|
||
The option is then followed by section name string and section body represented | ||
by an array ref: | ||
|
||
<verbatim> | ||
-section => "Section Name" => [ | ||
# Section body | ||
... | ||
] | ||
</verbatim> | ||
|
||
Each section has a level associated with it. There is implicitly defined _Root_ | ||
section with level 0. The top-level section (or sections) of any given spec | ||
file correspondingly would be assigned with level 1; any of it's subsections – | ||
level 2; and so on. | ||
|
||
Two sections at the same level with same name define the same section as long | ||
as their respective parents define the same section. For example, if there is a | ||
definition: | ||
|
||
<verbatim> | ||
-section => "Sec1" => [ | ||
-expert => 1, | ||
-section => "Sec2" => [ | ||
... | ||
] | ||
] | ||
</verbatim> | ||
|
||
and the following is found in another spec file: | ||
|
||
<verbatim> | ||
-section => "Sec1" => [ | ||
-section => "Sec2" => [ | ||
... | ||
] | ||
] | ||
</verbatim> | ||
|
||
then these are the same section. Note that presence/absence of "-expert" | ||
option doesn't affect this logic. Even more: any subsequent declaration can | ||
change option values defined earlier. This may have some undesirable side | ||
effects and this behavior could change in the future. So far it is advised | ||
to avoid use of any options in a section declaration if it is known to be | ||
declared somewhere else. | ||
|
||
As an opposite example to the previous one, the following definitions are | ||
declaring different sections: | ||
|
||
<verbatim> | ||
-section => "Sec" => [ | ||
-section => "Sec2" => [ | ||
... | ||
], | ||
], | ||
-section => "Sec1" => [ | ||
-section => "Sec" => [ | ||
-section => "Sec2" => [ | ||
... | ||
] | ||
] | ||
] | ||
</verbatim> | ||
|
||
_Sec2_ are two new sections here while _Sec1_ is extending _Sec1_ from the | ||
previous two specs. | ||
|
||
Note the comma between two =-section= declaration in the latter example. | ||
Remember that this is a Perl list and declaration of _Sec1_ in nothing but | ||
another items in this list. | ||
|
||
---+++ Nodes | ||
|
||
Nodes are keys in =Foswiki::Config= data hash. Depending on their location | ||
in the hash tree they're separated into two categories: *branch* and *leaf* nodes. | ||
Branch nodes are those not storing any value but serving as a container for | ||
their sub-nodes. On the contrary, a leaf node is terminal in a path from | ||
top-level node to a value the key represents. | ||
|
||
For example, in path _From.Top.To.Bottom_ subkeys _From_, _Top_, and _To_ | ||
are branches and _Bottom_ is the leaf. | ||
|
||
A node type could be determined in a few possible ways: | ||
|
||
* If data type or default value is defined for a node – it's a leaf | ||
* If an option which is either leaf or branch only specific is used - the node is of the type for which the option is valid | ||
* If a sub-key definition detected – it's a branch | ||
|
||
Mixing incompatible attributes within single node definition raises a fatal | ||
exception. For example, if a node has type assigned to it and a sub-key | ||
definition is found. | ||
|
||
A node declaration can only be found within a section body. The following spec is illegal: | ||
|
||
<verbatim> | ||
Key => [ | ||
... | ||
] | ||
</verbatim> | ||
|
||
A declaration consist of: | ||
|
||
1. Node name of path | ||
1. Optional data type | ||
1. Node body represented by an arrayref | ||
|
||
Example of valid declarations: | ||
|
||
<verbatim> | ||
Key1 => [ | ||
SubKey => [ | ||
-type => 'STRING(30)', | ||
], | ||
], | ||
SomeNumber => 'NUMBER(10)' => [ | ||
-default => 3.1415926, | ||
... | ||
], | ||
SomeURL => STRING => [ | ||
-default => "http://trygooglethis.com", | ||
-label => "I don't know what is this for", | ||
-text => <<EOT, | ||
Let's expect a user to guess what is this value for and how the system is | ||
using it. That'd be fun! | ||
EOT | ||
], | ||
</verbatim> | ||
|
||
#ImplDetail | ||
---++ Implementation | ||
|
||
Behind the scenes specs are handled by =Foswiki::Config= class code. An object of | ||
this class can exists in two modes: _data_ or _specs_. In _data_ mode the object operates | ||
on a simple data hash of configuration keys similar to the legacy =%Foswiki::cfg=. | ||
In _specs_ mode the data hash is tied to =Foswiki::Config::DataHash= class. | ||
|
||
-- Main.AdminUser - 11 May 2017 | ||
|