Skip to content

Commit

Permalink
Item14152: Extension deregistration implemented.
Browse files Browse the repository at this point in the history
An extension object chops off its ties to the application upon
destruction.

- RENAMED Foswiki::Extensions to Foswiki::ExtManager

- RENAMED Foswiki::App extensions attribute to extMgr

- Fixed a problem with Foswiki::Aux::_ExtensibleRole BUILD not being
called after commit 6258d85
  • Loading branch information
vrurg committed May 20, 2017
1 parent 9b15e61 commit 542ac43
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 92 deletions.
16 changes: 8 additions & 8 deletions EmptyExtension/lib/Foswiki/Extension/Empty.pm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extends qw(Foswiki::Extension);
---++ The Ecosystem
Extensions exists as a list of objects managed by =Foswiki::App= =extensions=
attribute which is actually an object of =Foswiki::Extensions= class. The latter
attribute which is actually an object of =Foswiki::ExtManager= class. The latter
provides API for extension manipulation routines like loading and registering an
extension; registering extension's components like overriding methods or
classes; find an extenion object by name; etc.
Expand All @@ -46,7 +46,7 @@ is a registered =Sample= extension then whenever we a ask for the extension's
object then we can be sure that there is no more than signle active one exists.
This is an important rule for some of [[#ExportedSubs][exported subroutines]].
=Foswiki::Extensions= module has its own =$VERSION= global var. It represents
=Foswiki::ExtManager= module has its own =$VERSION= global var. It represents
%WIKITOOLNAME% API version and is used to check an extension compatibility.
---++ Extensions loading
Expand All @@ -71,7 +71,7 @@ use version 0.77; our $VERSION = version->declare(0.0.1);
our $API_VERSION = version->declare("2.99.0");
</verbatim>
=$API_VERSION= declares the minimal version of =Foswiki::Extensions= module
=$API_VERSION= declares the minimal version of =Foswiki::ExtManager= module
required.
=cut
Expand All @@ -93,7 +93,7 @@ functionality. As such, their use is similar to =CPAN:Moo=
---+++ Extension dependencies
An extension can claim to be located before or after another one in the list of
extension objects (=extensions= attribute of =Foswiki::Extensions= class). This defines
extension objects (=extensions= attribute of =Foswiki::ExtManager= class). This defines
inheritance and callback execution order. I.e., if =Ext2= goes after =Ext1= and
both register a callback handler for =Foswiki::App::postConfig= then =Ext1= handler
will be called first.
Expand All @@ -109,7 +109,7 @@ The following subs implement this functionality:
What these do is define a directed graph of extensions. When all extensions are
loaded and registered the graph gets sorted using topoligical sort. The resulting
order is stored in =Foswiki::Extensions= =orderedList= attribute.
order is stored in =Foswiki::ExtManager= =orderedList= attribute.
The final order of extensions is not guaranteed. For example, =Ext2= could
require to be placed before =Ext1= but it doesn't mean that it will directly
Expand Down Expand Up @@ -167,7 +167,7 @@ tagHandler MYMACRO => 'Foswiki::Extension::Macro::MYMACRO';
it is expected that the class would does =Foswiki::Macro= role. An object of
this class will be created on demand by =Foswiki::Macros=. It won't get any
reference to the extension object. Would the object be needed to expand the
macro then =Foswiki::Extensions= =extObject()= method *must* be used to obtain
macro then =Foswiki::ExtManager= =extObject()= method *must* be used to obtain
the reference.
=cut
Expand Down Expand Up @@ -283,7 +283,7 @@ will be explained later in this documentation.
When a pluggable method is called the extensions framework first executes all
_before_ methods; then _around_ ones; then _after_. Within each group methods
are called using the order defined by =Foswiki::Extensions= =orderedList=
are called using the order defined by =Foswiki::ExtManager= =orderedList=
attribute (see the [[#ExtDeps][dependecies section]]).
__NOTE:__ It is commonplace for _after_ methods to be called in reverse order.
Expand Down Expand Up @@ -419,7 +419,7 @@ suite.
---++ SEE ALSO
=Foswiki::Extensions=, =Foswiki::Extension=, =Foswiki::Class=, and
=Foswiki::ExtManager=, =Foswiki::Extension=, =Foswiki::Class=, and
=ExtensionsTests= test suite.
Check out [[Foswiki:Development.OONewPluginModel][Foswiki topic]] where all this
Expand Down
57 changes: 27 additions & 30 deletions UnitTestContrib/test/unit/ExtensionsTests.pm
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ extends qw(Foswiki::Extension);
use version 0.77; our \$VERSION = version->declare(0.0.1);
our \$API_VERSION = version->declare("2.99.0");
Foswiki::Extensions::registerExtModule('$extName');
Foswiki::ExtManager::registerExtModule('$extName');
$code
Expand All @@ -123,15 +123,15 @@ sub _setExtDependencies {

foreach my $extName ( keys %deps ) {
my $dep = $deps{$extName};
Foswiki::Extensions::registerDeps( $extName,
Foswiki::ExtManager::registerDeps( $extName,
ref($dep) ? @{$dep} : $dep );
}
}

sub _disableAllCurrentExtensions {
my $this = shift;
$this->app->env->{FOSWIKI_DISABLED_EXTENSIONS} =
[@Foswiki::Extensions::extModules];
[@Foswiki::ExtManager::extModules];
}

sub test_orderedList {
Expand All @@ -152,7 +152,7 @@ sub test_orderedList {

$this->assert_deep_equals(
$expected,
$this->app->extensions->orderedList,
$this->app->extMgr->orderedList,
"Wrong order of extensions"
);
}
Expand All @@ -169,14 +169,14 @@ sub test_manual_disable {
$this->reCreateFoswikiApp;

$this->assert_not_null(
$this->app->extensions->extensions->{ $ext[0] },
$this->app->extMgr->extensions->{ $ext[0] },
"First extension is expected to be initialized"
);
$this->assert( !$this->app->extensions->extEnabled( $ext[1] ),
$this->assert( !$this->app->extMgr->extEnabled( $ext[1] ),
"Second extensions is expected to be disabled but it is not" );
$this->assert_str_equals(
"Disabled by FOSWIKI_DISABLED_EXTENSIONS environment variable.",
$this->app->extensions->disabledExtensions->{ $ext[1] }
$this->app->extMgr->disabledExtensions->{ $ext[1] }
);
}

Expand All @@ -196,27 +196,25 @@ sub test_depend_on_manual_disable {
$this->reCreateFoswikiApp;

$this->assert_not_null(
$this->app->extensions->extensions->{ $ext[0] },
$this->app->extMgr->extensions->{ $ext[0] },
"First extension is expected to be initialized"
);
$this->assert( !$this->app->extensions->extEnabled( $ext[1] ),
"Second extensions is expected to be disabled but it is not" );
$this->assert( !$this->app->extMgr->extEnabled( $ext[1] ),
"Second extension is expected to be disabled but it is not" );
$this->assert_str_equals(
"Disabled by FOSWIKI_DISABLED_EXTENSIONS environment variable.",
$this->app->extensions->disabledExtensions->{ $ext[1] }
);
$this->assert(
!$this->app->extensions->extEnabled( $ext[2] ),
"Third extensions is expected to be disabled but it is not"
$this->app->extMgr->disabledExtensions->{ $ext[1] }
);
$this->assert( !$this->app->extensions->extEnabled( $ext[3] ),
"Fourth extensions is expected to be disabled but it is not" );
$this->assert( !$this->app->extMgr->extEnabled( $ext[2] ),
"Third extension is expected to be disabled but it is not" );
$this->assert( !$this->app->extMgr->extEnabled( $ext[3] ),
"Fourth extension is expected to be disabled but it is not" );

$this->assert_str_contains( "Disabled extension",
$this->app->extensions->disabledExtensions->{ $ext[2] } );
$this->app->extMgr->disabledExtensions->{ $ext[2] } );

$this->assert_str_contains( "Disabled extension",
$this->app->extensions->disabledExtensions->{ $ext[3] } );
$this->app->extMgr->disabledExtensions->{ $ext[3] } );
}

sub test_circular_deps {
Expand All @@ -236,15 +234,15 @@ sub test_circular_deps {

$this->assert_deep_equals(
$expected,
$this->app->extensions->orderedList,
$this->app->extMgr->orderedList,
"Wrong order of extensions"
);

$this->assert_str_contains( "Circular dependecy found for ",
$this->app->extensions->disabledExtensions->{ $ext[1] } );
$this->app->extMgr->disabledExtensions->{ $ext[1] } );

$this->assert_str_contains( "Circular dependecy found for ",
$this->app->extensions->disabledExtensions->{ $ext[3] } );
$this->app->extMgr->disabledExtensions->{ $ext[3] } );
}

sub test_pluggable_methods {
Expand Down Expand Up @@ -332,7 +330,7 @@ EXT3
my $testClass = 'Foswiki::ExtensionsTests::SampleClass';
my $testMethod = 'testPluggableMethod';
$this->assert(
defined $Foswiki::Extensions::pluggables{$testClass}{$testMethod},
defined $Foswiki::ExtManager::pluggables{$testClass}{$testMethod},
"Method "
. $testMethod
. " of class "
Expand Down Expand Up @@ -392,14 +390,14 @@ sub test_API_VERSION {
$this->reCreateFoswikiApp;

ASSERT(
!$this->app->extensions->extEnabled( $ext[0] ),
!$this->app->extMgr->extEnabled( $ext[0] ),
"Extension with API version "
. Foswiki::fetchGlobal("\$$ext[0]::API_VERSION")
. " must have been disabled but it's not"
);

ASSERT(
$this->app->extensions->extEnabled( $ext[1] ),
$this->app->extMgr->extEnabled( $ext[1] ),
"Extension with API version "
. Foswiki::fetchGlobal("\$$ext[1]::API_VERSION")
. " must have been enabled but it's not"
Expand Down Expand Up @@ -483,7 +481,7 @@ EXT8

$this->reCreateFoswikiApp;

my $exts = $this->app->extensions;
my $exts = $this->app->extMgr;

foreach my $i ( 0 .. 7 ) {
my $expect = defined( $expect[$i] ) ? "disabled" : "enabled";
Expand Down Expand Up @@ -597,7 +595,7 @@ package Foswiki::Macros::TEST_CLASS_MACRO;
sub expand {
my \$this = shift;
my \$myExt = \$this->app->extensions->extObject('$ext');
my \$myExt = \$this->app->extMgr->extObject('$ext');
return "TEST_CLASS_MACRO_" . \$myExt->counter;
}
Expand Down Expand Up @@ -634,8 +632,7 @@ SNEXT

$this->reCreateFoswikiApp;

$this->assert_str_equals( 'AutoGenExt',
$this->app->extensions->extName($ext) );
$this->assert_str_equals( 'AutoGenExt', $this->app->extMgr->extName($ext) );
}

sub test_callbacks {
Expand Down Expand Up @@ -764,7 +761,7 @@ EXT3
$this->assert_null( $rc->{cbReturn},
"The callback call must have returned undef" );

$this->app->extensions->extObject( $ext[1] )->beTheHighlander(1);
$this->app->extMgr->extObject( $ext[1] )->beTheHighlander(1);

$rc = $cbObj->sampleCBChain;

Expand Down
2 changes: 1 addition & 1 deletion UnitTestContrib/test/unit/FeatureSetTests.pm
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ sub _registerStandardCORE {
EXTENSIONS_3 => [
str2ver( 2.99, undef, undef ),
-desc => "New and powerful OO extensions",
-doc => 'Foswiki::Extensions'
-doc => 'Foswiki::ExtManager'
],
;
}
Expand Down
1 change: 1 addition & 0 deletions core/lib/Foswiki.pm
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ sub load_package {
${ $pkgNS->{$flagSym} } = 1; # Mark package as loaded.
}
catch {
#say STDERR "! Load failed for $fullname: $_";
my $e = Foswiki::Exception->transmute( $_, 0 );
$e->rethrow;
};
Expand Down
15 changes: 8 additions & 7 deletions core/lib/Foswiki/App.pm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use Storable qw(dclone);
# shortcut functions. Must be replaced with something more reasonable.
use CGI ();
use Compress::Zlib;
use Foswiki::Extensions;
use Foswiki::FeatureSet qw(:all);
use Foswiki::Engine;
use Foswiki::Templates;
Expand Down Expand Up @@ -110,10 +109,11 @@ has env => (
is => 'rw',
required => 1,
);
has extensions => (
has extMgr => (
is => 'ro',
lazy => 1,
builder => '_prepareExtensions',
clearer => 1,
builder => '_prepareExtMgr',
);
has forms => (
is => 'ro',
Expand Down Expand Up @@ -341,7 +341,7 @@ sub BUILD {

$Foswiki::app = $this;

$this->extensions->initialize;
$this->extMgr->initialize;

unless ( $this->cfg->data->{isVALID} ) {
$this->cfg->bootstrapSystemSettings;
Expand Down Expand Up @@ -640,7 +640,7 @@ sub create {

Foswiki::load_class($class);

$class = $this->extensions->mapClass($class);
$class = $this->extMgr->mapClass($class);

my $object;

Expand Down Expand Up @@ -1572,11 +1572,12 @@ sub _prepareUser {
return undef;
}

sub _prepareExtensions {
sub _prepareExtMgr {
my $this = shift;

# Don't use create() here because the latter depends on extensions.
return Foswiki::Extensions->new( app => $this );
Foswiki::load_class('Foswiki::ExtManager');
return Foswiki::ExtManager->new( app => $this );
}

# If the X-Foswiki-Tickle header is present, this request is an attempt to
Expand Down
11 changes: 6 additions & 5 deletions core/lib/Foswiki/Aux/_ExtensibleRole.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ has __appObj => (
weak_ref => 1,
);

sub BUILD {
my $this = shift;
around BUILD => sub {
my $orig = shift;
my $this = shift;
my ($params) = @_;

#$this->_traceMsg("Storing app for extensible objet");
Expand All @@ -28,13 +29,13 @@ sub BUILD {
$this->__appObj( $params->{app} );
}

#return $orig->($this, @_);
}
return $orig->( $this, @_ );
};

# Foswiki::Object::clone support.
# Avoid full app cloning.
sub _clone__appObj {
return $_[0]->_appObj;
return $_[0]->__appObj;
}

1;
Expand Down
Loading

0 comments on commit 542ac43

Please sign in to comment.