Skip to content

Commit

Permalink
Item14152: Adding handlers.
Browse files Browse the repository at this point in the history
Handlers are keywrods describing basic extension functionality. So far the
following are added: extClass, extAfter, extBefore, plugBefore. All are
part of Foswiki::Class 'extension' option.

Another Foswiki::Class option added is 'extensible' to be used by classes
allowing their functionality to be overriden. This option exports
'pluggable' handler to declare overridable methods.

- TBM (to be merged into Item13897) – Foswiki::Class imports 5.14 feature
set into a module. No need to always `use v5.14;' or a kind of.

- TBM Foswiki::Exception::transmute doesn't enforce an exception into
destination class if the exception's class is a subclass of destination.

- Added Foswiki::Extension – base class for extensions.

- Foswiki::Extensions performs registration of some of extension parameters
upon loading stage.

- Fixed erroneous use of fetchVar name instead of fetchGlobal. Thanks to
George Clark for pointing me out.

- TBM? Finding name space glob code has been extracted from
Foswiki::fetchGlobal() into Foswiki::getNS() function. May even replace use
of Devel::Symbols in some cases.
  • Loading branch information
vrurg committed Aug 30, 2016
1 parent d1cf26f commit 5bb22fa
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 52 deletions.
67 changes: 46 additions & 21 deletions core/lib/Foswiki.pm
Expand Up @@ -35,8 +35,6 @@ our $UNICODE = 1; # flag that extensions can use to test if the core is unicode
our $TRUE = 1;
our $FALSE = 0;
our $TranslationToken = "\0"; # Do not deprecate - used in many plugins
our $system_message; # Important broadcast message from the system
my $bootstrap_message = ''; # Bootstrap message.

# Note: the following marker is used in text to mark RENDERZONE
# macros that have been hoisted from the source text of a page. It is
Expand Down Expand Up @@ -907,6 +905,30 @@ sub saveFile {

=begin TML
---++ StaticMethod getNS( $module ) => $globRef
Returns GLOB pointing to namespace of a module. Returns undef if module isn't
loaded.
=cut

sub getNS {
my ($module) = @_;

my @keys = split /::/, $module;

my $ref = \%::;
while (@keys) {
my $key = shift @keys;
my $sym = "$key\:\:";
return undef unless defined $ref->{$sym};
$ref = $ref->{$sym};
}
return $ref;
}

=begin TML
---++ StaticMethod fetchGlobal($fullName) => $value
Fetches a variable value by it's full name. 'Full' means it includes type of
Expand All @@ -915,43 +937,46 @@ returned.
The purpose of this function is to avoid use of =no strict 'refs'= in the code.
*Example:* fetchVar('$Foswiki::Extension::Sample::API_VERSION');
*Example:* fetchGlobal('$Foswiki::Extension::Sample::API_VERSION');
=cut

sub fetchGlobal {
my ($fullName) = @_;

$fullName =~ s/^([\$%@&])//
or Foswiki::Exception::Fatal->throw( text =>
"Incorrect variable name `$fullName' in a call to Foswiki::fetchVar()"
);
or Foswiki::Exception::Fatal->throw(
text => "Foswiki::fetchGlobal(): Invalid sigil in `$fullName'" );
my $sigil = $1;

if ( $sigil eq '&' ) {
$fullName =~ /^(.+)::([^:]+)$/;
my ( $pkg, $func ) = ( $1, $2 );
return $pkg->can($func);
}
my @keys = split /::/, $fullName;
my $symbol = pop @keys;
my $module = join( '::', @keys );

my @keys = split /::/, $fullName;
my $ns = getNS($module);

my $ref = \%::;
while ( @keys > 1 ) {
my $key = shift @keys;
$ref = $ref->{ $key . "::" };
}
my $varName = shift @keys;
Foswiki::Exception::Fatal->throw( text => "Module $module not found" )
unless defined $ns;

state $sigilMap = {
state $sigilSub = {
'$' => sub { return ${ $_[0] } },
'%' => sub { return %{ $_[0] } },
'@' => sub { return @{ $_[0] } },
'&' => sub { return *{ $_[0] }{CODE} },
};
state $sigilKey = {
'$' => 'SCALAR',
'%' => 'HASH',
'@' => 'ARRAY',
'&' => 'CODE',
};

say STDERR ${ $ref->{$varName} };
Foswiki::Exception::Fatal->throw(
text => "$sigil$symbol not declared in " . $ns )
unless defined $ns->{$symbol}
&& *{ $ns->{$symbol} }{ $sigilKey->{$sigil} };

return $sigilMap->{$sigil}->( $ref->{$varName} );
return $sigilSub->{$sigil}->( $ns->{$symbol} );
}

1;
Expand Down
65 changes: 57 additions & 8 deletions core/lib/Foswiki/Class.pm
Expand Up @@ -78,11 +78,14 @@ manually by the class using =with=.

use Carp;

require Foswiki;
require Moo::Role;
require Moo;
require namespace::clean;
use B::Hooks::EndOfScope 'on_scope_end';

use constant DEFAULT_FEATURESET => ':5.14';

our @ISA = qw(Moo);

my %_assignedRoles;
Expand All @@ -99,13 +102,26 @@ sub import {
# Keywords exported with this option.
keywords => [qw(callback_names)],
},
app => { use => 0, },
app => { use => 0, },
extension => {
use => 0,
keywords => [qw(extClass extAfter extBefore plugBefore)],
},
extensible => {
use => 0,
keywords => [qw(pluggable)],
},
);

my @p;
my @noNsClean = qw(meta);
my @noNsClean = qw(meta);
my $featureSet = DEFAULT_FEATURESET;
while (@_) {
my $param = shift;
if ( $param =~ /^:/ ) {
$featureSet = $param;
next;
}
if ( exists $options{$param} ) {
my $opt = $options{$param};
$opt->{use} = 1;
Expand All @@ -127,6 +143,8 @@ sub import {
$class->_apply_roles;
};

feature->import($featureSet);

namespace::clean->import(
-cleanee => $target,
-except => \@noNsClean,
Expand All @@ -139,11 +157,9 @@ sub import {
# Actually we're duplicating Moo::_install_coderef here in a way. But we better
# avoid using a module's internalls.
sub _inject_code {
my ( $name, $code ) = @_;
my ( $target, $name, $code ) = @_;

no strict "refs";
*{$name} = $code;
use strict "refs";
Foswiki::getNS($target)->{$name} = $code;
}

sub _apply_roles {
Expand All @@ -165,10 +181,10 @@ sub _install_callbacks {
my ( $class, $target ) = @_;

_assign_role( $target, 'Foswiki::Aux::Callbacks' );
_inject_code( "${target}::callback_names", \&_handler_callbacks );
_inject_code( $target, "callback_names", \&_handler_callbacks );
}

sub _handler_callbacks {
sub _handler_callbacks (@) {
my $target = caller;
Foswiki::Aux::Callbacks::registerCallbackNames( $target, @_ );
}
Expand All @@ -178,6 +194,39 @@ sub _install_app {
_assign_role( $target, 'Foswiki::AppObject' );
}

sub _handler_plugBefore ($&) {
my ( $plug, $code ) = @_;
say STDERR "Replacing plug $plug with $code";
}

sub _handler_extClass ($$) {
my ( $class, $subClass ) = @_;
my $target = caller;

Foswiki::Extensions::registerSubClass( $target, $class, $subClass );
}

sub _handler_extAfter (@) {
my $target = caller;

Foswiki::Extensions::registerDeps( $target, @_ );
}

sub _handler_extBefore (@) {
my $target = caller;

Foswiki::Extensions::registerDeps( $_, $target ) foreach @_;
}

sub _install_extension {
my ( $class, $target ) = @_;

_inject_code( $target, 'plugBefore', \&_handler_plugBefore );
_inject_code( $target, 'extClass', \&_handler_extClass );
_inject_code( $target, 'extAfter', \&_handler_extAfter );
_inject_code( $target, 'extBefore', \&_handler_extBefore );
}

1;
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
Expand Down
2 changes: 1 addition & 1 deletion core/lib/Foswiki/Exception.pm
Expand Up @@ -296,7 +296,7 @@ sub transmute {
if DEBUG;
if ( ref($e) ) {
if ( $e->isa('Foswiki::Exception') ) {
if ( !$enforce || ( ref($e) eq $class ) ) {
if ( !$enforce || $e->isa($class) ) {
return $e;
}
return $class->new( %$e, @_ );
Expand Down
26 changes: 26 additions & 0 deletions core/lib/Foswiki/Extension.pm
@@ -0,0 +1,26 @@
# See bottom of file for license and copyright information

package Foswiki::Extension;

use Foswiki::Class qw(app);
extends qw(Foswiki::Object);

1;
__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
Copyright (C) 2016 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version. For
more details read LICENSE in the root of this distribution.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
As per the GPL, removal of this notice is prohibited.
11 changes: 7 additions & 4 deletions core/lib/Foswiki/Extension/Empty.pm
@@ -1,10 +1,13 @@
package Foswiki::Extension::Empty;
use v5.14;

use Foswiki::Class;
extends qw(Foswiki::Object);
use Foswiki::Class qw(extension);
extends qw(Foswiki::Extension);

use version 0.77; our $VERSION = version->declare(0.0.1);
our $API_VERSION = version->declare("2.98.0");
our $API_VERSION = version->declare("2.99.0");

extAfter qw(Sample);

extBefore qw(Test1 Foswiki::Extension::Test2);

1;
14 changes: 10 additions & 4 deletions core/lib/Foswiki/Extension/Sample.pm
@@ -1,12 +1,18 @@

package Foswiki::Extension::Sample;
use v5.14;

use Foswiki::Class;
extends qw(Foswiki::Object);
use Foswiki::Class qw(extension);
extends qw(Foswiki::Extension);

use version 0.77; our $VERSION = version->declare(0.0.1);
our $API_VERSION = version->declare("2.99.0");

#our $API_VERSION = version->declare("2.98.0");
plugBefore 'Foswiki::Exception::transmute' => sub {

};

extClass 'Foswiki::Logger', 'Foswiki::Extension::Sample::Logger';

extBefore qw(Empty);

1;

0 comments on commit 5bb22fa

Please sign in to comment.