]> sipb.mit.edu Git - ikiwiki.git/commitdiff
add use_pagespec and deptype functions
authorJoey Hess <joey@gnu.kitenet.net>
Thu, 8 Oct 2009 20:49:53 +0000 (16:49 -0400)
committerJoey Hess <joey@gnu.kitenet.net>
Thu, 8 Oct 2009 21:53:20 +0000 (17:53 -0400)
IkiWiki.pm
debian/changelog
doc/plugins/write.mdwn
t/use_pagespec.t [new file with mode: 0755]

index 2064c881a5497a3de1a15f28029d2f810218623e..c787612e12e622174797609fe5337ff57586bf8a 100644 (file)
@@ -17,11 +17,12 @@ use vars qw{%config %links %oldlinks %pagemtime %pagectime %pagecase
            %forcerebuild %loaded_plugins};
 
 use Exporter q{import};
            %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
                  %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}->();
 }
 
        $hooks{rcs}{rcs_receive}{call}->();
 }
 
-sub add_depends ($$;@) {
+sub add_depends ($$;$) {
        my $page=shift;
        my $pagespec=shift;
        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}/ &&
 
        # 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.
        # 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);
        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};
                }
                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 ($$) {
 }
 
 sub file_pruned ($$) {
index 565a0cffac08ae2128f13b6f0721ca068758b62c..12ddebac9fc46d4d4ca153ae2ae431ce9cd02f02 100644 (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.
   * 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.
   * 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.
     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
 
 
  -- Joey Hess <joeyh@debian.org>  Sun, 27 Sep 2009 17:40:03 -0400
 
index 2324300790d94ba871177c0fa9089efed13e38c1..3d56507582cdc42c2f4780c6dc244d3a2a94941f 100644 (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.
 
 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.
 
 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($$;@)`
 
 
 #### `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
 
 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
 
 
 ### Setup plugins
 
diff --git a/t/use_pagespec.t b/t/use_pagespec.t
new file mode 100755 (executable)
index 0000000..7b90407
--- /dev/null
@@ -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");