Skip to content

Commit

Permalink
Item14152: Some more documentation for extensions.
Browse files Browse the repository at this point in the history
  • Loading branch information
vrurg committed Sep 30, 2016
1 parent 87debdf commit 304acca
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 7 deletions.
138 changes: 137 additions & 1 deletion EmptyExtension/lib/Foswiki/Extension/Empty.pm
Expand Up @@ -39,6 +39,7 @@ application's life cycle.
It is also mandatory for an extension class to subclass =Foswiki::Extension=.
The manager would reject a class registration if this rule is broken.
#SingleExtSet
At any given moment of time there is only one active set of extensions
accessible via the application's =extensions= attribute. It means that if there
is a registered =Sample= extension then whenever we a ask for the extension's
Expand Down Expand Up @@ -77,10 +78,145 @@ our $API_VERSION = version->declare("2.99.0");
---++ Foswiki::Class exported subroutines
Being used with =extension= parameter =Foswiki::Class= exports a set of
subroutines
subroutines to simplify and improve readibility of some of extensions
functionality. As such, their use is similar to =CPAN:Moo=
[[https://metacpan.org/pod/Moo#IMPORTED-SUBROUTINES][subroutines]].
---+++ 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
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.
This doesn't apply to registered macros because there could only be single handler for
a macro.
The following subs implement this functionality:
| *Sub name* | *Description* |
| =extBefore @nameList= | Extension must be placed before extensions in =@nameList= |
| =extAfter @nameList= | Extension must be placed after extensions in =@nameList= |
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 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
preceed it in the list as other extensions could be inserted between them. A
typical example of such behaviour would be =Ext1= requiring to be placed after
=Ext3=. Besides, nothing is guaranteed about single extensions not requiring any
specific order. They can be inserted anywhere in the list. Same apply to two or
more subsets of extensions not bound to each other with directional relations.
If the graph happens not to be acyclic and we find a circular dependency then
all extensions involved into the chain are getting disabled. For example, if the
chain looks like the following example:
Ext1 %M% Ext2 %M% Ext3 %M% Ext4 %M% Ext5 %M% Ext3
then not only =Ext[3,4,5]= are disabled but =Ext1= and =Ext2= too.
This behaviour is considered questionable and may change later.
=cut

#extBefore qw(Ext1 Ext2);
#extAfter qw(Foswiki::Extension::Ext3);

=begin TML
---+++ Custom macros
Custom macros are declared using =tagHandler= subroutine. It accepts two parameters:
the first is the macro name; the second is either a coderef or a class name. For coderef
a method named after the macro is generated for extension's class. I.e.:
<verbatim>
package Foswiki::Extension::Ext;
...
tagHandler MYMACRO => sub {
my $this = shift;
my ($attrs, $topicObject, @macroArgs) = @_;
...
};
</verbatim>
would generate a method named =MYMACRO= in =Foswiki::Extension::Ext= class.
For cases when the second parameter is a class name:
<verbatim>
...
tagHandler MYMACRO => 'Foswiki::Extension::Macro::MYMACRO';
</verbatim>
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
the reference.
=cut

tagHandler EMPTYMACRO => sub {
my $this = shfit;
return __PACKAGE__ . " version is " . $VERSION;
};

=begin TML
---+++ Callbacks
Callbacks are here to replace the old *Handler mechanism. They're developed as
more powerful, flexible, and OO-friendly replacement.
Extension can install a callback handler using =callbackHandler= subroutine. It
receives two parameters: a callback name and a coderef:
<verbatim>
callbackHandler postConfig => sub {
my $this = shift;
my ($obj, $params) = @_;
...
};
</verbatim>
The coderef acts as an extension's method. The method gets reference to the
object which actually initiated this callback; and reference to a hash with
parameters supplied by the object – see =params= key of callback arguments in
=Foswiki::Aux::Callbacks=.
__NOTE:__ The method arguments are different from common callback handler as
described in =Foswiki::Aux::Callbacks= because there is no point of passing the
=data= key of arguments hash. Instead extension callback method can rely on
object's internals whenever needed.
See =Foswiki::Aux::Callbacks=
=cut

callbackHandler postConfig => sub {
my $this = shift;
my ($app) =
@_; # Foswiki::App::postConfig callback doesn't supply any params.
};

callbackHandler 'Foswiki::App::handleRequestException' => sub {
my $this = shift;
my ( $app, $params ) = @_;

if ( $params->{exception}->isa('Foswiki::Exception::DoesntExists') ) {

# Do something about this class of exceptions.
}
};

=begin TML
---++ SEE ALSO
Expand Down
12 changes: 6 additions & 6 deletions core/lib/Foswiki/Aux/Callbacks.pm
Expand Up @@ -31,7 +31,7 @@ will work only when the name is used only in one namespace. Otherwise an
exception (ASSERT) with error will be raised.
Callback is a coderef (a sub) which gets called at certain moments of a class
life cycle. A callback sub is called with the following arguments:
life cycle. A callback sub is supplied with the following arguments:
1 Reference to the object which is calling the callback.
1 A list of key/value pairs where the following keys are defined:
Expand All @@ -40,12 +40,12 @@ life cycle. A callback sub is called with the following arguments:
A named callback may have more than one handler. In this case all handlers are
executed in the order they were registerd. No return values are respecred. If a
handler wants to be the last it must raise =Foswiki::Exception::CB::Last=
exception. If set, exception's =returnValue= attribute contains what is returned
by =callback()= method then.
handler wants to be the last it must raise =Foswiki::Exception::Ext::Last=
exception. If set, exception's =rc= attribute contains what is returned by
=callback()= method then.
If a callback handler raises any other exception besides of
=Foswiki::Exception::CB::*= then that exception is rethrown further up the call
=Foswiki::Exception::Ext::*= then that exception is rethrown further up the call
stack.
Example callback handler may look like:
Expand All @@ -67,7 +67,7 @@ sub cbHandler {
# Suppose that $rc is set when the
if (defined $rc) {
Foswiki::Exception::CB::Last->throw( returnValue => $rc );
Foswiki::Exception::Ext::Last->throw( rc => $rc );
}
}
</verbatim>
Expand Down

0 comments on commit 304acca

Please sign in to comment.