aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Hess <joey@kodama.kitenet.net>2008-10-21 17:57:19 -0400
committerJoey Hess <joey@kodama.kitenet.net>2008-10-21 17:57:19 -0400
commite75818572fff5256d16221a2b065b214d8cb9f5d (patch)
tree30e45561111b4dcc48f4726bfaef0ca99f5d47d2
parent92a43d5d384ba4e504c5255989a869ced424219c (diff)
downloadikiwiki-e75818572fff5256d16221a2b065b214d8cb9f5d.tar
ikiwiki-e75818572fff5256d16221a2b065b214d8cb9f5d.tar.gz
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.
-rw-r--r--IkiWiki.pm46
-rw-r--r--IkiWiki/Plugin/external.pm8
-rw-r--r--IkiWiki/Plugin/relativedate.pm4
-rw-r--r--debian/changelog5
-rw-r--r--doc/plugins/contrib/po.mdwn5
-rw-r--r--doc/plugins/write.mdwn50
-rwxr-xr-xplugins/externaldemo10
-rw-r--r--po/ikiwiki.pot14
8 files changed, 111 insertions, 31 deletions
diff --git a/IkiWiki.pm b/IkiWiki.pm
index 207ca87fb..e0454963d 100644
--- a/IkiWiki.pm
+++ b/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;
diff --git a/IkiWiki/Plugin/external.pm b/IkiWiki/Plugin/external.pm
index 287e118f1..4ce9c8bab 100644
--- a/IkiWiki/Plugin/external.pm
+++ b/IkiWiki/Plugin/external.pm
@@ -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;
} #}}}
diff --git a/IkiWiki/Plugin/relativedate.pm b/IkiWiki/Plugin/relativedate.pm
index f4dba61a4..dc8f7d538 100644
--- a/IkiWiki/Plugin/relativedate.pm
+++ b/IkiWiki/Plugin/relativedate.pm
@@ -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;
diff --git a/debian/changelog b/debian/changelog
index 928cd8666..e1baea8ce 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -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
diff --git a/doc/plugins/contrib/po.mdwn b/doc/plugins/contrib/po.mdwn
index 30ede95a6..c4b7f9ee9 100644
--- a/doc/plugins/contrib/po.mdwn
+++ b/doc/plugins/contrib/po.mdwn
@@ -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
diff --git a/doc/plugins/write.mdwn b/doc/plugins/write.mdwn
index 856b34ba1..2e11e6234 100644
--- a/doc/plugins/write.mdwn
+++ b/doc/plugins/write.mdwn
@@ -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.
diff --git a/plugins/externaldemo b/plugins/externaldemo
index 4d13f2444..be7aba8b9 100755
--- a/plugins/externaldemo
+++ b/plugins/externaldemo
@@ -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.
diff --git a/po/ikiwiki.pot b/po/ikiwiki.pot
index 335575f02..4452ea8dc 100644
--- a/po/ikiwiki.pot
+++ b/po/ikiwiki.pot
@@ -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 ""