summaryrefslogtreecommitdiff
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)
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 ""