Feature Proposal: Fine-granular control over supported features

Motivation

To have a flexible way to provide and check information about features supported by the core or other components.

Description and Documentation

Usually software compatibility is checked by testing against versions. If we speak of extensions it means that we usually know that an extension was designed for a core version X.Y and wont work on lower versions because they lack of a required feature. It doesn't really matter what exactly the term 'feature' means in this context: either it's an API, certain behaviour, or anything else of the kind. All we need to know is if the feature is there to provide the extension with required functionality or data.

But knowing the minimal required version is not sufficient too. Consider an abandoned extension which would work as long as certain core functionality is in place. How do we know that the extension's life cycle is over? Remember that we may speak of tens or even hundreds of them out there. Closely following each one wouldn't be possible. Neither is possible to know what exact functionality is required unless the author has documented it.

How it works.

Let's start with an example. The example assumes that feature set support was implemented for Foswiki 2.x (hypothetically, of course).

package Foswiki;

...

use Foswiki::FeatureSet;

...

# Declare FEATURE => [ versionStarted, versionDeprecated, versionDropped, %options ] pairs.
features_provided
    TWIKI_COMPATIBILITY => [ undef, 2.99, 3.0 ],
    PARA_INDENT => [ undef, undef, undef, 
                        -desc => "Paragraph indentation", ],
    MOO => [ 2.99, undef, undef,
                        -desc => "OO with Moo", 
                        -proposal => 'Development.ImproveOOModel', 
                        -doc => 'Documentation.Foswiki3CodingStyle', ],
    EXTENSIONS_1 => [ undef, undef, 2.99, 
                        -desc => "Old-style plguins and contribs", ],
    EXTENSIONS_1_3 => [ 2.99, 2.99, 4.0, 
                        -desc => "Old-style plguins and contribs adapted for Foswiki 3.0", ],
    EXTENSIONS_3 => [ 2.99, undef, undef, 
                        -desc => "New and powerful OO extensions", 
                        -doc => 'Foswiki::Extensions' ],
;

In this example we see that TWIKI_COMPATIBILITY feature (TWikiCompatibilityPlugin) was there since the beginning of time (true, isn't it?). But is deprecated since 2.99 and won't exist in 3.0.

For old-style extensions (EXTENSIONS_1) the situation is a bit more complicated because they won't work in their old format (with $Foswiki::Plugins::SESSION variable and $object->{attribute} style of accessing object's data). Thus this feature is just dropped in 2.99 but EXTENSIONS_1_3 is introduced instead which is deprecated since it's introduced in 2.99 and will be dropped in 4.0. (I hope nobody would take this seriously as I'm alone do not define lifespan of a feature.)

For the feature set defined above an extension could use the following statement to define what it needs:

our @requiredFeatures = qw(MOO EXTENSIONS_1_3);

The core would match these features against its version and in this way determine if the extension could be used or shall be disabled. The compatibility table then would look like this:

Core ver Compatible? Reason
2.1
ALERT!
MOO absent
2.99.2
DONE
 
3.1
DONE
 
4.0
ALERT!
EXTENSIONS_1_3 is not supported any more

Context

Features active for the current core version would be included into the application context. To be distinguished from other contexts their names will be completed with SUPPORTS_ prefix. Note the PARA_IDENT feature above – its context name would then become SUPPORTS_PARA_INDENT which currently exists and is being used in Foswiki code.

NOTE At some point access to the contexts would be provided to the front end JavaScript. I expect it to open a couple of new possibilities for future user experience.

Namespaces

Not only the core may need the feature sets. Other extensions or frameworks could use it to improve their interoperability with other Foswiki components. To make this kind of interaction easier and to avoid same feature names from different components to clash with each other the concept of namespaces is provided.

Consider the example:

features_provided -namespace => 'Foswiki::Extension::EmptyExtenion',
    MY_FEATURE1 => [ undef, 0.8, 1.0 ],
    MY_FEATURE2 => [ 0.3, undef, undef ],
;

In this code an extension declares features it provides in terms of the extension's module version, not core's.

A namespaced feature does appear in the application context too but its name has <namespace>:: prefix. In the example above the context name will be Foswiki::Extension::EmptyExtenion::SUPPORTS_MY_FEATURE1. Yes, it does look terribly long. But namespaces are not limited to any particular format and it may be agreed that:

  • Foswiki:: part could be avoided whatsoever as practically every component has it.
  • Extensions can use only their plain name skipping the Extension:: part too as long the name ends in 'Extension' suffix. Otherwise the full Extension:: form could be replaced with shorter Ext:: variant. For example: Ext::YAMLConfig for Foswiki::Extension::YAMLConfig.

NOTE The core features are in fact registered in the CORE namespace.

Naming conventions.

Though all feature names here are in all-caps format this is not mandatory. Actually, a feature could be any sequence of symbols except for the following rather clear rules:

  1. Throughout this proposal options are defined using -option notation. This is to distinguish them from feature names and to simplify some API calls allowing to mix up options with features. Of course this imposes a ban on dash-prefixed feature names. But I guess this won't be a big deal.
  2. Similar implication has use of double colon for namespaces in context names. Though this time it also implies that there will be no contexts containing a m/[\w]::SUPPORTS_/ sequence. Despite very low possibility of such context would ever exists I would suggest prohibiting use of the double colons for context names whatsoever.

A component lifespan effect

Due to better granularity of defining compatibility comparing to simple version match certain future components could survive major overhauls of core code simply because they would depend on few rather stable features. Without it the risk of declaring them non-compatible is rather high.

On the other hand, scanning for incompatible components will be as simple as possible as long as features declared by core are kept tidy and actual.

Impact

%WHATDOESITAFFECT%
edit

Implementation

This functionality is currently in development in the Item13897 branch. The first commit is expected in 2-3 days from publishing this proposal.

Could be backported to 2.x branch.

-- Contributors: VadimBelman - 16 Nov 2016

Discussion

This is my last proposal (Promise! I really do!) before dedicating myself to completing and polishing the future 3.0. Together with the new extensions this would be something we can provide for developers to do really fantastic things to Foswiki. Unfortunately, my other vision of redesigned Foswiki::Meta left outboard and won't be part of the (hopefully) forthcoming release. On the other hand a curious developer can take care of it by implementing a corresponding extension.

As it is clear from the text the main focus is kept on handling the new extensions. But the use could span beyond this particular case and everything depends on developers' fantasy. I currently would expect skins utilize features to turn on/off certain blocks depending on whether or not there is support for these block on core's or extensions' side. Think of it as of a possibility to have version independent skin which could provide extended functionality without the penalty of incompatibility with other Foswiki or extension versions.

I'm all eyes and ears now. smile

-- VadimBelman - 16 Nov 2016
 
Topic revision: r2 - 25 Nov 2016, VadimBelman
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy