aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Hess <joey@gnu.kitenet.net>2009-10-08 16:49:53 -0400
committerJoey Hess <joey@gnu.kitenet.net>2009-10-08 17:53:20 -0400
commit5e236f5d25b68f5fb4a421b24470419c6042cb1c (patch)
treea35860cda1da576f263c0b4b5a0039e0f7cde2a3
parentc57908b9d073500608d656adaf2bd3048c8cef67 (diff)
downloadikiwiki-5e236f5d25b68f5fb4a421b24470419c6042cb1c.tar
ikiwiki-5e236f5d25b68f5fb4a421b24470419c6042cb1c.tar.gz
add use_pagespec and deptype functions
-rw-r--r--IkiWiki.pm135
-rw-r--r--debian/changelog4
-rw-r--r--doc/plugins/write.mdwn61
-rwxr-xr-xt/use_pagespec.t30
4 files changed, 194 insertions, 36 deletions
diff --git a/IkiWiki.pm b/IkiWiki.pm
index 2064c881a..c787612e1 100644
--- a/IkiWiki.pm
+++ b/IkiWiki.pm
@@ -17,11 +17,12 @@ use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase
%forcerebuild %loaded_plugins};
use Exporter q{import};
-our @EXPORT = qw(hook debug error template htmlpage add_depends pagespec_match
- pagespec_match_list bestlink htmllink readfile writefile
- pagetype srcfile pagename displaytime will_render gettext urlto
- targetpage add_underlay pagetitle titlepage linkpage
- newpagefile inject add_link
+our @EXPORT = qw(hook debug error template htmlpage deptype use_pagespec
+ add_depends pagespec_match pagespec_match_list bestlink
+ htmllink readfile writefile pagetype srcfile pagename
+ displaytime will_render gettext urlto targetpage
+ add_underlay pagetitle titlepage linkpage newpagefile
+ inject add_link
%config %links %pagestate %wikistate %renderedfiles
%pagesources %destsources);
our $VERSION = 3.00; # plugin interface version, next is ikiwiki version
@@ -1768,18 +1769,10 @@ sub rcs_receive () {
$hooks{rcs}{rcs_receive}{call}->();
}
-sub add_depends ($$;@) {
+sub add_depends ($$;$) {
my $page=shift;
my $pagespec=shift;
-
- my $deptype=0;
- if (@_) {
- my %params=@_;
-
- $deptype=$deptype | $DEPEND_PRESENCE if $params{presence};
- $deptype=$deptype | $DEPEND_LINKS if $params{links};
- }
- $deptype=$DEPEND_CONTENT unless $deptype;
+ my $deptype=shift || $DEPEND_CONTENT;
# Is the pagespec a simple page name?
if ($pagespec =~ /$config{wiki_file_regexp}/ &&
@@ -1791,18 +1784,118 @@ sub add_depends ($$;@) {
# Analyse the pagespec, and match it against all pages
# to get a list of influences, and add explicit dependencies
# for those.
+ #my $sub=pagespec_translate($pagespec);
+ #return if $@;
+ #foreach my $p (keys %pagesources) {
+ # my $r=$sub->($p, location => $page );
+ # my %i=$r->influences;
+ # foreach my $i (keys %i) {
+ # $depends_simple{$page}{lc $i} |= $i{$i};
+ # }
+ #}
+ print STDERR "warning: use of add_depends; influences not tracked\n";
+
+ $depends{$page}{$pagespec} |= $deptype;
+ return 1;
+}
+
+sub use_pagespec ($$;@) {
+ my $page=shift;
+ my $pagespec=shift;
+ my %params=@_;
+
my $sub=pagespec_translate($pagespec);
- return if $@;
- foreach my $p (keys %pagesources) {
- my $r=$sub->($p, location => $page );
- my %i=$r->influences;
+ error "syntax error in pagespec \"$pagespec\""
+ if $@ || ! defined $sub;
+
+ my @candidates;
+ if (exists $params{limit}) {
+ @candidates=grep { $params{limit}->($_) } keys %pagesources;
+ }
+ else {
+ @candidates=keys %pagesources;
+ }
+
+ if (defined $params{sort}) {
+ my $f;
+ if ($params{sort} eq 'title') {
+ $f=sub { pagetitle(basename($a)) cmp pagetitle(basename($b)) };
+ }
+ elsif ($params{sort} eq 'title_natural') {
+ eval q{use Sort::Naturally};
+ if ($@) {
+ error(gettext("Sort::Naturally needed for title_natural sort"));
+ }
+ $f=sub { Sort::Naturally::ncmp(pagetitle(basename($a)), pagetitle(basename($b))) };
+ }
+ elsif ($params{sort} eq 'mtime') {
+ $f=sub { $pagemtime{$b} <=> $pagemtime{$a} };
+ }
+ elsif ($params{sort} eq 'age') {
+ $f=sub { $pagectime{$b} <=> $pagectime{$a} };
+ }
+ else {
+ error sprintf(gettext("unknown sort type %s"), $params{sort});
+ }
+ @candidates = sort { &$f } @candidates;
+ }
+
+ @candidates=reverse(@candidates) if $params{reverse};
+
+ my @matches;
+ my $firstfail;
+ my $count=0;
+ foreach my $p (@candidates) {
+ my $r=$sub->($p, location => $page);
+ if ($r) {
+ push @matches, [$p, $r];
+ last if defined $params{num} && ++$count == $params{num};
+ }
+ elsif (! defined $firstfail) {
+ $firstfail=$r;
+ }
+ }
+
+ $depends{$page}{$pagespec} |= ($params{deptype} || $DEPEND_CONTENT);
+
+ my @ret;
+ if (@matches) {
+ # Add all influences from successful matches.
+ foreach my $m (@matches) {
+ push @ret, $m->[0];
+ my %i=$m->[1]->influences;
+ foreach my $i (keys %i) {
+ $depends_simple{$page}{lc $i} |= $i{$i};
+ }
+ }
+ }
+ elsif (defined $firstfail) {
+ # Add influences from one failure. (Which one should not
+ # matter; all should have the same influences.)
+ my %i=$firstfail->influences;
foreach my $i (keys %i) {
$depends_simple{$page}{lc $i} |= $i{$i};
}
+ error(sprintf(gettext("cannot match pages: %s"), $firstfail));
}
- $depends{$page}{$pagespec} |= $deptype;
- return 1;
+ return @ret;
+}
+
+sub deptype (@) {
+ my $deptype=0;
+ foreach my $type (@_) {
+ if ($type eq 'presence') {
+ $deptype |= $DEPEND_PRESENCE;
+ }
+ elsif ($type eq 'links') {
+ $deptype |= $DEPEND_LINKS;
+ }
+ elsif ($type eq 'content') {
+ $deptype |= $DEPEND_CONTENT;
+ }
+ }
+ return $deptype;
}
sub file_pruned ($$) {
diff --git a/debian/changelog b/debian/changelog
index 565a0cffa..12ddebac9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -13,7 +13,6 @@ ikiwiki (3.14159266) UNRELEASED; urgency=low
* Added support framework for multiple types of dependencies.
* Allow declaring that a dependency is only affected by page presence
or changes to its links.
- (By passing presence => 1 or links => 1 to add_depends.)
* pagecount, calendar, postsparkline, progress: Use a presence dependency,
which makes these directives much less expensive to use, since page
edits will no longer trigger an unnecessary update.
@@ -34,6 +33,9 @@ ikiwiki (3.14159266) UNRELEASED; urgency=low
info.
* Plugins providing PageSpec `match_*` functions should pass additional
influence information when creating result objects.
+ * Added `use_pagespec` function, that plugins can use to find a list
+ of matching pages and add dependencies and influences, all at once,
+ and efficiently.
-- Joey Hess <joeyh@debian.org> Sun, 27 Sep 2009 17:40:03 -0400
diff --git a/doc/plugins/write.mdwn b/doc/plugins/write.mdwn
index 232430079..3d5650758 100644
--- a/doc/plugins/write.mdwn
+++ b/doc/plugins/write.mdwn
@@ -609,21 +609,52 @@ page created from it. (Ie, it appends ".html".)
Use this when constructing the filename of a html file. Use `urlto` when
generating a link to a page.
-#### `add_depends($$;@)`
+### `deptype(@)`
+
+Use this function to generate ikiwiki's internal representation of a
+dependency type from one or more of these keywords:
+
+* `content` is the default. Any change to the content
+ of a page triggers the dependency.
+* `presence` is only triggered by a change to the presence
+ of a page.
+* `links` is only triggered by a change to the links of a page.
+ This includes when a link is added, removed, or changes what
+ it points to due to other changes. It does not include the
+ addition or removal of a duplicate link.
+
+If multiple types are specified, they are combined.
+
+#### `use_pagespec($$;@)`
+
+Passed a page name, and [[ikiwiki/PageSpec]], returns a list of pages
+in the wiki that match the [[ikiwiki/PageSpec]].
+
+The page will automatically be made to depend on the specified
+[[ikiwiki/PageSpec]], so `add_depends` does not need to be called. This
+is significantly more efficient than calling `add_depends`
+followed by `pagespec_match_list`. You should use this anytime a plugin
+needs to match a set of pages and generate something based on that list.
+
+Additional named parameters can be specified:
+
+* `deptype` optionally specifies the type of dependency to add. Use the
+ `deptype` function to generate a dependency type.
+* `limit` is a reference to a function, that is called and passed a page,
+ and must return true for the page to be included.
+* `sort` specifies a sort order for the list. See
+ [[ikiwiki/PageSpec/sorting]] for the avilable sort methods.
+* `reverse` if true, sorts in reverse.
+* `num` if nonzero, specifies the maximum number of matching pages that
+ will be returned.
+
+#### `add_depends($$;$)`
Makes the specified page depend on the specified [[ikiwiki/PageSpec]].
By default, dependencies are full content dependencies, meaning that the
page will be updated whenever anything matching the PageSpec is modified.
-This default can be overridden by additional named parameters, which can be
-used to indicate weaker types of dependencies:
-
-* `presence` if set to true, only the presence of a matching page triggers
- the dependency.
-* `links` if set to true, any change to links on a matching page
- triggers the dependency. This includes when a link is added, removed,
- or changes what it points to due to other changes. It does not include
- the addition or removal of a duplicate link.
+This can be overridden by passing a `deptype` value as the third parameter.
#### `pagespec_match($$;@)`
@@ -984,10 +1015,12 @@ IkiWiki::ErrorReason object explaining why.
When constructing these objects, you should also include information about
of any pages whose contents or other metadata influenced the result of the
-match. For example, "backlink(foo)" is influenced by the contents of page foo;
-"link(foo)" and "title(bar)" are influenced by the contents of any
-page they match; "created_before(foo)" is influenced by the metadata of
-foo; while "glob(*)" is not influenced by the contents of any page.
+match. Do this by passing a list of pages, followed by `deptype` values.
+
+For example, "backlink(foo)" is influenced by the contents of page foo;
+"link(foo)" and "title(bar)" are influenced by the contents of any page
+they match; "created_before(foo)" is influenced by the metadata of foo;
+while "glob(*)" is not influenced by the contents of any page.
### Setup plugins
diff --git a/t/use_pagespec.t b/t/use_pagespec.t
new file mode 100755
index 000000000..7b904075e
--- /dev/null
+++ b/t/use_pagespec.t
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+use Test::More tests => 64;
+
+BEGIN { use_ok("IkiWiki"); }
+
+%pagesources=(
+ foo => "foo.mdwn",
+ bar => "bar.mdwn",
+ "post/1" => "post/1.mdwn",
+ "post/2" => "post/2.mdwn",
+ "post/3" => "post/3.mdwn",
+);
+
+is_deeply([use_pagespec("foo", "bar")], ["bar"]);
+is_deeply([sort(use_pagespec("foo", "post/*"))], ["post/1", "post/2", "post/3"]);
+is_deeply([use_pagespec("foo", "post/*", sort => "title", reverse => 1)],
+ ["post/3", "post/2", "post/1"]);
+is_deeply([use_pagespec("foo", "post/*", sort => "title", num => 2)],
+ ["post/1", "post/2"]);
+is_deeply([use_pagespec("foo", "post/*", sort => "title", num => 50)],
+ ["post/1", "post/2", "post/3"]);
+is_deeply([use_pagespec("foo", "post/*", sort => "title",
+ limit => sub { $_[0] !~ /3/}) ],
+ ["post/1", "post/2"]);
+eval { use_pagespec("foo", "beep") };
+ok($@, "fails with error when unable to match anything");
+eval { use_pagespec("foo", "this is not a legal pagespec!") };
+ok($@, "fails with error when pagespec bad");