add use_pagespec and deptype functions

master
Joey Hess 2009-10-08 16:49:53 -04:00
parent c57908b9d0
commit 5e236f5d25
4 changed files with 197 additions and 39 deletions

View File

@ -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) {
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);
my %i=$r->influences;
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 ($$) {

4
debian/changelog vendored
View File

@ -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

View File

@ -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

30
t/use_pagespec.t 100755
View File

@ -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");