function injection overhaul
Add an inject function, that can be used by plugins that want to replace one of ikiwiki's functions with their own version. (This is a scary thing that grubs through the symbol table, and replaces all exported occurances of a function with the injected version.) external: RPC functions can be injected to replace exported functions. Removed the stupid displaytime hook, and use injection instead.master
parent
92a43d5d38
commit
e75818572f
46
IkiWiki.pm
46
IkiWiki.pm
|
@ -21,6 +21,7 @@ our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match
|
|||
bestlink htmllink readfile writefile pagetype srcfile pagename
|
||||
displaytime will_render gettext urlto targetpage
|
||||
add_underlay pagetitle titlepage linkpage newpagefile
|
||||
inject
|
||||
%config %links %pagestate %wikistate %renderedfiles
|
||||
%pagesources %destsources);
|
||||
our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
|
||||
|
@ -898,23 +899,13 @@ sub abs2rel ($$) { #{{{
|
|||
} #}}}
|
||||
|
||||
sub displaytime ($;$) { #{{{
|
||||
my $time=shift;
|
||||
my $format=shift;
|
||||
if (exists $hooks{displaytime}) {
|
||||
my $ret;
|
||||
run_hooks(displaytime => sub {
|
||||
$ret=shift->($time, $format)
|
||||
});
|
||||
return $ret;
|
||||
}
|
||||
else {
|
||||
return formattime($time, $format);
|
||||
}
|
||||
# Plugins can override this function to mark up the time to
|
||||
# display.
|
||||
return '<span class="date">'.formattime(@_).'</span>';
|
||||
} #}}}
|
||||
|
||||
sub formattime ($;$) { #{{{
|
||||
# Plugins can override this function to mark up the time for
|
||||
# display.
|
||||
# Plugins can override this function to format the time.
|
||||
my $time=shift;
|
||||
my $format=shift;
|
||||
if (! defined $format) {
|
||||
|
@ -1676,6 +1667,31 @@ sub yesno ($) { #{{{
|
|||
return (defined $val && lc($val) eq gettext("yes"));
|
||||
} #}}}
|
||||
|
||||
sub inject { #{{{
|
||||
# Injects a new function into the symbol table to replace an
|
||||
# exported function.
|
||||
my %params=@_;
|
||||
|
||||
# This is deep ugly perl foo, beware.
|
||||
no strict;
|
||||
no warnings;
|
||||
if (! defined $params{parent}) {
|
||||
$params{parent}='::';
|
||||
$params{old}=\&{$params{name}};
|
||||
$params{name}=~s/.*:://;
|
||||
}
|
||||
my $parent=$params{parent};
|
||||
foreach my $ns (grep /^\w+::/, keys %{$parent}) {
|
||||
$ns = $params{parent} . $ns;
|
||||
inject(%params, parent => $ns) unless $ns eq '::main::';
|
||||
*{$ns . $params{name}} = $params{call}
|
||||
if exists ${$ns}{$params{name}} &&
|
||||
\&{${$ns}{$params{name}}} == $params{old};
|
||||
}
|
||||
use strict;
|
||||
use warnings;
|
||||
} #}}}
|
||||
|
||||
sub pagespec_merge ($$) { #{{{
|
||||
my $a=shift;
|
||||
my $b=shift;
|
||||
|
@ -1770,7 +1786,7 @@ sub pagespec_valid ($) { #{{{
|
|||
my $sub=pagespec_translate($spec);
|
||||
return ! $@;
|
||||
} #}}}
|
||||
|
||||
|
||||
sub glob2re ($) { #{{{
|
||||
my $re=quotemeta(shift);
|
||||
$re=~s/\\\*/.*/g;
|
||||
|
|
|
@ -202,10 +202,16 @@ sub inject ($@) { #{{{
|
|||
my $sub = sub {
|
||||
IkiWiki::Plugin::external::rpc_call($plugin, $params{call}, @_)
|
||||
};
|
||||
$sub=memoize($sub) if $params{memoize};
|
||||
|
||||
# This will add it to the symbol table even if not present.
|
||||
no warnings;
|
||||
eval qq{*$params{name}=\$sub};
|
||||
use warnings;
|
||||
memoize($params{name}) if $params{memoize};
|
||||
|
||||
# This will ensure that everywhere it was exported to sees
|
||||
# the injected version.
|
||||
IkiWiki::inject(name => $params{name}, call => $sub);
|
||||
return 1;
|
||||
} #}}}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ sub import { #{{{
|
|||
add_underlay("javascript");
|
||||
hook(type => "getsetup", id => "relativedate", call => \&getsetup);
|
||||
hook(type => "format", id => "relativedate", call => \&format);
|
||||
hook(type => "displaytime", id => "relativedate", call => \&display);
|
||||
inject(name => "IkiWiki::displaytime", call => \&mydisplaytime);
|
||||
} # }}}
|
||||
|
||||
sub getsetup () { #{{{
|
||||
|
@ -43,7 +43,7 @@ sub include_javascript ($;$) { #{{{
|
|||
'" type="text/javascript" charset="utf-8"></script>';
|
||||
} #}}}
|
||||
|
||||
sub display ($;$) { #{{{
|
||||
sub mydisplaytime ($;$) { #{{{
|
||||
my $time=shift;
|
||||
my $format=shift;
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@ ikiwiki (2.68) UNRELEASED; urgency=low
|
|||
the toplevel tagpage, and not closer subpages. The html links already went
|
||||
there, but internally the links were not recorded as absolute, which could
|
||||
cause confusing backlinks etc.
|
||||
* Add an inject function, that can be used by plugins that want to
|
||||
replace one of ikiwiki's functions with their own version.
|
||||
(This is a scary thing that grubs through the symbol table, and replaces
|
||||
all exported occurances of a function with the injected version.)
|
||||
* external: RPC functions can be injected to replace exported functions.
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Fri, 17 Oct 2008 20:11:02 -0400
|
||||
|
||||
|
|
|
@ -47,6 +47,11 @@ Any thoughts on this?
|
|||
>>> `targetpage`, `bestlink`, and `beautify_urlpath`. But, I noticed
|
||||
>>> the other day that such wrappers around exported functions are only visible by
|
||||
>>> plugins loaded after the plugin that defines them.
|
||||
>>>
|
||||
>>> Update: Take a look at the new "Function overriding" section of
|
||||
>>> [[plugins/write]]. I think you can just inject wrappers about a few ikiwiki
|
||||
>>> functions, rather than adding hooks. The `inject` function is pretty
|
||||
>>> insane^Wlow level, but seems to work great. --[[Joey]]
|
||||
>>
|
||||
>> The Discussion pages issue is something I am not sure about yet. But I will
|
||||
>> probably decide that "slave" pages, being only translations, don't deserve
|
||||
|
|
|
@ -854,6 +854,56 @@ By the way, to parse a ikiwiki setup file and populate `%config`, a
|
|||
program just needs to do something like:
|
||||
`use IkiWiki::Setup; IkiWiki::Setup::load($filename)`
|
||||
|
||||
### Function overriding
|
||||
|
||||
Sometimes using ikiwiki's pre-defined hooks is not enough. Your plugin
|
||||
may need to replace one of ikiwiki's own functions with a modified version,
|
||||
or wrap one of the functions.
|
||||
|
||||
For example, your plugin might want to override `displaytime`, to change
|
||||
the html markup used when displaying a date. Or it might want to override
|
||||
`IkiWiki::formattime`, to change how a date is formatted. Or perhaps you
|
||||
want to override `bestlink` and change how ikiwiki deals with WikiLinks.
|
||||
|
||||
By venturing into this territory, your plugin is becoming tightly tied to
|
||||
ikiwiki's internals. And it might break if those internals change. But
|
||||
don't let that stop you, if you're brave.
|
||||
|
||||
Ikiwiki provides an `inject()` function, that is a powerful way to replace
|
||||
any function with one of your own. This even allows you to inject a
|
||||
replacement for an exported function, like `bestlink`. Everything that
|
||||
imports that function will get your version instead. Pass it the name of
|
||||
the function to replace, and a new function to call.
|
||||
|
||||
For example, here's how to replace `displaytime` with a version using HTML 5
|
||||
markup:
|
||||
|
||||
inject(name => 'IkiWiki::displaytime', call => sub {
|
||||
return "<time>".formattime(@_)."</time>";
|
||||
});
|
||||
|
||||
Here's how to wrap `bestlink` with a version that tries to handle
|
||||
plural words:
|
||||
|
||||
my $origbestlink=\&bestlink;
|
||||
inject(name => 'IkiWiki::bestlink', call => \&mybestlink);
|
||||
|
||||
sub deplural ($) {
|
||||
my $word=shift;
|
||||
$word =~ s/e?s$//; # just an example :-)
|
||||
return $word;
|
||||
}
|
||||
|
||||
sub mybestlink ($$) {
|
||||
my $page=shift;
|
||||
my $link=shift;
|
||||
my $ret=$origbestlink->($page, $link);
|
||||
if (! length $ret) {
|
||||
$ret=$origbestlink->($page, deplural($link));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
### Javascript
|
||||
|
||||
Some plugins use javascript to make ikiwiki look a bit more web-2.0-ish.
|
||||
|
|
|
@ -106,9 +106,8 @@ sub import {
|
|||
rpc_call("getvar", "config", "url")."\n";
|
||||
|
||||
# Here's an example of how to inject an arbitrary function into
|
||||
# ikiwiki, replacing a core function.
|
||||
# Note use of automatic memoization.
|
||||
rpc_call("inject", name => "IkiWiki::formattime",
|
||||
# ikiwiki. Note use of automatic memoization.
|
||||
rpc_call("inject", name => "IkiWiki::bob",
|
||||
call => "formattime", memoize => 1);
|
||||
|
||||
print STDERR "externaldemo plugin successfully imported\n";
|
||||
|
@ -126,9 +125,8 @@ sub preprocess {
|
|||
return "externaldemo plugin preprocessing on $title!";
|
||||
}
|
||||
|
||||
sub formattime {
|
||||
print STDERR "externaldemo plugin's formattime called via RPC";
|
||||
return scalar "formatted time: ".localtime(shift);
|
||||
sub bob {
|
||||
print STDERR "externaldemo plugin's bob called via RPC";
|
||||
}
|
||||
|
||||
# Now all that's left to do is loop and handle each incoming RPC request.
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2008-10-19 20:06-0400\n"
|
||||
"POT-Creation-Date: 2008-10-21 17:51-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -48,7 +48,7 @@ msgstr ""
|
|||
msgid "You are banned."
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki/CGI.pm:385 ../IkiWiki/CGI.pm:386 ../IkiWiki.pm:1182
|
||||
#: ../IkiWiki/CGI.pm:385 ../IkiWiki/CGI.pm:386 ../IkiWiki.pm:1175
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
|
@ -913,25 +913,25 @@ msgstr ""
|
|||
msgid "refreshing wiki.."
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki.pm:458
|
||||
#: ../IkiWiki.pm:459
|
||||
msgid "Must specify url to wiki with --url when using --cgi"
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki.pm:504
|
||||
#: ../IkiWiki.pm:505
|
||||
msgid "cannot use multiple rcs plugins"
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki.pm:533
|
||||
#: ../IkiWiki.pm:534
|
||||
#, perl-format
|
||||
msgid "failed to load external plugin needed for %s plugin: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki.pm:1165
|
||||
#: ../IkiWiki.pm:1158
|
||||
#, perl-format
|
||||
msgid "preprocessing loop detected on %s at depth %i"
|
||||
msgstr ""
|
||||
|
||||
#: ../IkiWiki.pm:1674
|
||||
#: ../IkiWiki.pm:1667
|
||||
msgid "yes"
|
||||
msgstr ""
|
||||
|
||||
|
|
Loading…
Reference in New Issue