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
44
IkiWiki.pm
44
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
|
bestlink htmllink readfile writefile pagetype srcfile pagename
|
||||||
displaytime will_render gettext urlto targetpage
|
displaytime will_render gettext urlto targetpage
|
||||||
add_underlay pagetitle titlepage linkpage newpagefile
|
add_underlay pagetitle titlepage linkpage newpagefile
|
||||||
|
inject
|
||||||
%config %links %pagestate %wikistate %renderedfiles
|
%config %links %pagestate %wikistate %renderedfiles
|
||||||
%pagesources %destsources);
|
%pagesources %destsources);
|
||||||
our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
|
our $VERSION = 2.00; # plugin interface version, next is ikiwiki version
|
||||||
|
@ -898,23 +899,13 @@ sub abs2rel ($$) { #{{{
|
||||||
} #}}}
|
} #}}}
|
||||||
|
|
||||||
sub displaytime ($;$) { #{{{
|
sub displaytime ($;$) { #{{{
|
||||||
my $time=shift;
|
# Plugins can override this function to mark up the time to
|
||||||
my $format=shift;
|
# display.
|
||||||
if (exists $hooks{displaytime}) {
|
return '<span class="date">'.formattime(@_).'</span>';
|
||||||
my $ret;
|
|
||||||
run_hooks(displaytime => sub {
|
|
||||||
$ret=shift->($time, $format)
|
|
||||||
});
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return formattime($time, $format);
|
|
||||||
}
|
|
||||||
} #}}}
|
} #}}}
|
||||||
|
|
||||||
sub formattime ($;$) { #{{{
|
sub formattime ($;$) { #{{{
|
||||||
# Plugins can override this function to mark up the time for
|
# Plugins can override this function to format the time.
|
||||||
# display.
|
|
||||||
my $time=shift;
|
my $time=shift;
|
||||||
my $format=shift;
|
my $format=shift;
|
||||||
if (! defined $format) {
|
if (! defined $format) {
|
||||||
|
@ -1676,6 +1667,31 @@ sub yesno ($) { #{{{
|
||||||
return (defined $val && lc($val) eq gettext("yes"));
|
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 ($$) { #{{{
|
sub pagespec_merge ($$) { #{{{
|
||||||
my $a=shift;
|
my $a=shift;
|
||||||
my $b=shift;
|
my $b=shift;
|
||||||
|
|
|
@ -202,10 +202,16 @@ sub inject ($@) { #{{{
|
||||||
my $sub = sub {
|
my $sub = sub {
|
||||||
IkiWiki::Plugin::external::rpc_call($plugin, $params{call}, @_)
|
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;
|
no warnings;
|
||||||
eval qq{*$params{name}=\$sub};
|
eval qq{*$params{name}=\$sub};
|
||||||
use warnings;
|
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;
|
return 1;
|
||||||
} #}}}
|
} #}}}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ sub import { #{{{
|
||||||
add_underlay("javascript");
|
add_underlay("javascript");
|
||||||
hook(type => "getsetup", id => "relativedate", call => \&getsetup);
|
hook(type => "getsetup", id => "relativedate", call => \&getsetup);
|
||||||
hook(type => "format", id => "relativedate", call => \&format);
|
hook(type => "format", id => "relativedate", call => \&format);
|
||||||
hook(type => "displaytime", id => "relativedate", call => \&display);
|
inject(name => "IkiWiki::displaytime", call => \&mydisplaytime);
|
||||||
} # }}}
|
} # }}}
|
||||||
|
|
||||||
sub getsetup () { #{{{
|
sub getsetup () { #{{{
|
||||||
|
@ -43,7 +43,7 @@ sub include_javascript ($;$) { #{{{
|
||||||
'" type="text/javascript" charset="utf-8"></script>';
|
'" type="text/javascript" charset="utf-8"></script>';
|
||||||
} #}}}
|
} #}}}
|
||||||
|
|
||||||
sub display ($;$) { #{{{
|
sub mydisplaytime ($;$) { #{{{
|
||||||
my $time=shift;
|
my $time=shift;
|
||||||
my $format=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
|
the toplevel tagpage, and not closer subpages. The html links already went
|
||||||
there, but internally the links were not recorded as absolute, which could
|
there, but internally the links were not recorded as absolute, which could
|
||||||
cause confusing backlinks etc.
|
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
|
-- 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
|
>>> `targetpage`, `bestlink`, and `beautify_urlpath`. But, I noticed
|
||||||
>>> the other day that such wrappers around exported functions are only visible by
|
>>> the other day that such wrappers around exported functions are only visible by
|
||||||
>>> plugins loaded after the plugin that defines them.
|
>>> 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
|
>> 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
|
>> 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:
|
program just needs to do something like:
|
||||||
`use IkiWiki::Setup; IkiWiki::Setup::load($filename)`
|
`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
|
### Javascript
|
||||||
|
|
||||||
Some plugins use javascript to make ikiwiki look a bit more web-2.0-ish.
|
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";
|
rpc_call("getvar", "config", "url")."\n";
|
||||||
|
|
||||||
# Here's an example of how to inject an arbitrary function into
|
# Here's an example of how to inject an arbitrary function into
|
||||||
# ikiwiki, replacing a core function.
|
# ikiwiki. Note use of automatic memoization.
|
||||||
# Note use of automatic memoization.
|
rpc_call("inject", name => "IkiWiki::bob",
|
||||||
rpc_call("inject", name => "IkiWiki::formattime",
|
|
||||||
call => "formattime", memoize => 1);
|
call => "formattime", memoize => 1);
|
||||||
|
|
||||||
print STDERR "externaldemo plugin successfully imported\n";
|
print STDERR "externaldemo plugin successfully imported\n";
|
||||||
|
@ -126,9 +125,8 @@ sub preprocess {
|
||||||
return "externaldemo plugin preprocessing on $title!";
|
return "externaldemo plugin preprocessing on $title!";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub formattime {
|
sub bob {
|
||||||
print STDERR "externaldemo plugin's formattime called via RPC";
|
print STDERR "externaldemo plugin's bob called via RPC";
|
||||||
return scalar "formatted time: ".localtime(shift);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Now all that's left to do is loop and handle each incoming RPC request.
|
# Now all that's left to do is loop and handle each incoming RPC request.
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -48,7 +48,7 @@ msgstr ""
|
||||||
msgid "You are banned."
|
msgid "You are banned."
|
||||||
msgstr ""
|
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"
|
msgid "Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -913,25 +913,25 @@ msgstr ""
|
||||||
msgid "refreshing wiki.."
|
msgid "refreshing wiki.."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../IkiWiki.pm:458
|
#: ../IkiWiki.pm:459
|
||||||
msgid "Must specify url to wiki with --url when using --cgi"
|
msgid "Must specify url to wiki with --url when using --cgi"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../IkiWiki.pm:504
|
#: ../IkiWiki.pm:505
|
||||||
msgid "cannot use multiple rcs plugins"
|
msgid "cannot use multiple rcs plugins"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../IkiWiki.pm:533
|
#: ../IkiWiki.pm:534
|
||||||
#, perl-format
|
#, perl-format
|
||||||
msgid "failed to load external plugin needed for %s plugin: %s"
|
msgid "failed to load external plugin needed for %s plugin: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../IkiWiki.pm:1165
|
#: ../IkiWiki.pm:1158
|
||||||
#, perl-format
|
#, perl-format
|
||||||
msgid "preprocessing loop detected on %s at depth %i"
|
msgid "preprocessing loop detected on %s at depth %i"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../IkiWiki.pm:1674
|
#: ../IkiWiki.pm:1667
|
||||||
msgid "yes"
|
msgid "yes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue