stas 2002/06/29 12:13:49 Modified: lib/DocSet Cache.pm Config.pm Doc.pm DocSet.pm NavigateCache.pm RunTime.pm lib/DocSet/Doc Common.pm Log: DocSet sync: - fix the html title rendering, not to have <a href="" name=""> as some older browsers crush on that, use <a href=""></a><a name=""></a> instead - in each document verify that there are no duplicated anchors and croak if there are. - added the croak method to the rendering class, so if there is a problem the context is dumped (the problematic source file). - croak if the config file includes both: grouped and non-grouped items. - added docset id duplication checking, the program will croak when a duplication is detected and report the offending config file and the config file it was previous seen in. - fix a bug with properly setting doc.dir.abs_doc_root, for the docs in the very root e.g, src/404.html. - added a new special attribute: 'changes', which points to a hidden chapter including changes for each docset (if such exists). this is an improvement over the inclusion of the changes.pod or alike along with all other chapters because usually people don't want to see changes and when the docset pdf is created huge changes files can be an unwanted burden, so now if this attribute is included, the pdf for the docset won't include this file in it. - overload Carp.pm's subs in Config.pm and when die'ing or warn'ing start dumping the relevant object attributes, like the used config file, to aid the problem analysis. Revision Changes Path 1.6 +18 -11 modperl-docs/lib/DocSet/Cache.pm Index: Cache.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/Cache.pm,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Cache.pm 29 Apr 2002 17:36:54 -0000 1.5 +++ Cache.pm 29 Jun 2002 19:13:49 -0000 1.6 @@ -189,15 +189,14 @@ my($self) = shift; if (@_) { + my %args = @_; + my %required = map { $_ => 1} qw(id title stitle); + + for (keys %required) { + croak "must specify the index_node's $_" unless exists $args{$_}; + } # set - my($id, $stitle, $title, $abstract) = @_; - croak "must specify the index_node's id" unless defined $id; - croak "must specify the index_node's stitle" unless defined $stitle; - croak "must specify the index_node's title" unless defined $title; - $self->{cache}{_index}{id} = $id; - $self->{cache}{_index}{stitle} = $stitle; - $self->{cache}{_index}{title} = $title; - $self->{cache}{_index}{abstract} = $abstract; + $self->{cache}{_index} = \%args; } else { # get @@ -215,8 +214,10 @@ if (@_) { # set my($cache_path, $id, $rel_path) = @_; - croak "must specify a path to the parent cache" unless defined $cache_path; - croak "must specify a relative to parent path" unless defined $rel_path; + croak "must specify a path to the parent cache" + unless defined $cache_path; + croak "must specify a path relative to parent docset" + unless defined $rel_path; croak "must specify a parent id" unless defined $id; $self->{cache}{_parent}{cache_path} = $cache_path; $self->{cache}{_parent}{id} = $id; @@ -299,7 +300,13 @@ my @ids = $cache->ordered_ids; my $total_ids = $cache->total_ids; - $cache->index_node($id, $stitle, $title, $abstract); + $cache->index_node( + id => $id, + stitle => $stitle, + title => $title, + abstract => $abstract, + #... + ); my %index_node = $cache->index_node(); $cache->parent_node($cache_path, $id, $rel_path); 1.9 +122 -34 modperl-docs/lib/DocSet/Config.pm Index: Config.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/Config.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- Config.pm 29 Apr 2002 17:36:54 -0000 1.8 +++ Config.pm 29 Jun 2002 19:13:49 -0000 1.9 @@ -3,8 +3,6 @@ use strict; use warnings; -use Carp; - use File::Basename (); use File::Spec::Functions; @@ -46,18 +44,23 @@ sub conv_class { my($self, $src_mime, $dst_mime) = @_; # convert - die "src_mime is not defined" unless defined $src_mime; - die "dst_mime is not defined" unless defined $dst_mime; - my $conv_class = $conv_class{$src_mime}{$dst_mime} - or die "unknown input/output MIME mapping: $src_mime => $dst_mime"; - return $conv_class; + $self->croak("src_mime is not defined") unless defined $src_mime; + $self->croak("dst_mime is not defined") unless defined $dst_mime; + $self->croak("unknown input/output MIME mapping: $src_mime => $dst_mime") + unless exists $conv_class{$src_mime}{$dst_mime}; + return $conv_class{$src_mime}{$dst_mime}; } -my %attr = map {$_ => 1} qw(chapters docsets links sitemap); +my %node_attr = map {$_ => 1} qw(chapters docsets links sitemap); +my %hidden_attr = map {$_ => 1} qw(chapters docsets); +# the rest of the valid attributes in addition to 'hidden', 'changes' +# and %node_attr +my %other_attr = map {$_ => 1} qw(id stitle title abstract body + copy_glob copy_skip dir file); sub read_config { my($self, $config_file) = @_; - die "Configuration file is not specified" unless $config_file; + die "Configuration file is not specified" unless defined $config_file; $self->{config_file} = $config_file; @@ -70,7 +73,7 @@ eval join '', "package $package;", $content, ";1;"; - die "failed to eval config file at $config_file:\n$@" if $@; + $self->croak("failed to eval config file:\n$@") if $@; # parse the attributes of the docset's config file no strict 'refs'; @@ -79,12 +82,14 @@ #dumper [EMAIL PROTECTED]; - my @groups = (); my $current_group = ''; my $group_size; + my $non_grouped_node_seen = 0; for ( my $i=0; $i < @c; $i +=2 ) { my($key, $val) = @c[$i, $i+1]; if ($key eq 'group') { + $self->croak("grouped and non-grouped chapters cannot be mixed") + if $non_grouped_node_seen; # close the previous group by storing the key of its last node if ($current_group) { push @{ $self->{node_groups} }, $current_group, $group_size; @@ -94,23 +99,37 @@ $group_size = 0; } elsif ($key eq 'hidden') { - die "hidden's value must be an ARRAY reference" + $self->croak("hidden attribute's value must be an ARRAY reference") unless ref $val eq 'ARRAY'; my @h = @$val; for ( my $j=0; $j < @h; $j +=2 ) { my($key1, $val1) = @h[$j, $j+1]; - die "hidden's can include only 'chapters' and 'docsets', " . - "$key1 is invalid" unless $key1 =~ /^(docsets|chapters)$/; + $self->croak("the 'hidden' attribute can include only: ", + join(", ", keys %hidden_attr), + "attributes, $key1 is invalid") + unless exists $hidden_attr{$key1}; $self->add_node($key1, $val1, 1); } } - elsif (exists $attr{$key}) { + elsif ($key eq 'changes') { + # add as a hidden chapter + $self->add_node('chapters', $val, 1); + # by this id we can reach this hidden object from the + # docset object + $self->{extra}{$key} = $val; + } + elsif (exists $node_attr{$key}) { + $non_grouped_node_seen = 1 unless $current_group; $group_size += $self->add_node($key, $val, 0); } - else { + elsif (exists $other_attr{$key}) { $self->{$key} = $val; #dumper [$key => $val]; } + else { + #dumper [$key => $val]; + $self->croak("unknown attribute: $key"); + } } if ($current_group) { push @{ $self->{node_groups} }, $current_group, $group_size; @@ -120,7 +139,7 @@ # - alias one to another if only one was specified $self->{title} = $self->{stitle} unless exists $self->{title}; $self->{stitle} = $self->{title} unless exists $self->{stitle}; - die "Either 'title' or 'stitle' must appear in $config_file" + $self->croak("Either 'title' or 'stitle' must appear in $config_file") unless $self->{title}; # merge_config will adjust this value, for nested docsets @@ -179,8 +198,10 @@ # set path to the abs_doc_root # META: hardcoded paths! (but in this case it doesn't matter, # as long as it's set in the config file - $self->{dir}{abs_doc_root} = - join '/', ("..") x ($self->{dir}{dst_html} =~ tr|/|/|); + $self->{dir}{abs_doc_root} = + $self->{dir}{dst_html} =~ m|/| + ? join('/', ("..") x ($self->{dir}{dst_html} =~ tr|/|/|)) + : '.'; } } @@ -204,7 +225,7 @@ # is marked as dirty, but somewhere later a logic mistake # resets this value to 0, (non-dirty). if (exists $self->{modified} && !$status) { - Carp::croak("Cannot reset the 'modified' status"); + $self->croak("Cannot reset the 'modified' status"); } $self->{modified} = $status; } @@ -226,7 +247,7 @@ # is marked to rebuild the docset, but somewhere later a logic mistake # resets this value to 0, (non-dirty). if (exists $self->{rebuild} && !$status) { - Carp::croak("Cannot reset the 'rebuild' status"); + $self->croak("Cannot reset the 'rebuild' status"); } $self->{rebuild} = $status; } @@ -254,6 +275,18 @@ return scalar @values; } +# register the new docset id and verify that it wasn't registered +# before that. Croak if it was registered already. +sub check_duplicated_docset_ids { + my $self = shift; + my $id = $self->get('id'); + my @entries = DocSet::RunTime::register('unique_docset_id', $id, + $self->{config_file}); + # should be enough to report only the first returned value, since + # there can be only one duplicate before this will croak + $self->croak("Duplicated docset id: '$id'\n", + "Used already by $entries[0]") if @entries > 1; +} # return a list of files potentially to be copied # @@ -340,7 +373,7 @@ push @values, $self->{dir}{$_} } else { - cluck "no entry for dir: $_"; + $self->cluck("no entry for dir: $_"); push @values, ''; } } @@ -416,7 +449,7 @@ sub object_store { my($self, $object) = @_; - croak "no object passed" unless defined $object and ref $object; + $self->croak("no object passed") unless defined $object and ref $object; push @{ $self->{_objects_store} }, $object; } @@ -425,7 +458,20 @@ return @{ $self->{_objects_store}||[] }; } - +# extended error diagnosis +{ + require Carp; + no strict 'refs'; + for my $sub (qw(carp cluck croak confess)) { + undef &$sub if \&$sub; # overload Carp's functions + *$sub = sub { + my($self, @msg) = @_; + &{"Carp::$sub"}("[scan $sub] ", @msg, "\n", + "[config file: " . $self->{config_file} . "]\n" + ); + }; + } +} #sub chapter_data { # my $self = shift; @@ -449,32 +495,34 @@ =head1 SYNOPSIS use DocSet::Config (); - + my $mime = $self->ext2mime($ext); my $class = $self->conv_class($src_mime, $dst_mime); - + $self->read_config($config_file); $self->merge_config($src_rel_dir); - + $self->check_duplicated_docset_ids(); + + $self->files_to_scan_copy(); my @files = $self->files_to_copy(files_to_copy); my @files = $self->expand_dir(); - + $self->set($key => $val); $self->set_dir($dir_name => $val); $val = $self->get($key); $self->get_file($key); $self->get_dir($dir_name); - + #XXX my @docsets = $self->docsets(); #XXX my @links = $self->links(); #XXX my @chapters = $self->src_chapters(); my @chapters = $self->trg_chapters(); - + my $sitemap = $self->sitemap(); - + $self->cache($cache); my $cache = $self->cache(); - + $package = $self->path2package($path); $self->object_store($object); my @objects = $self->stored_objects(); @@ -700,6 +748,37 @@ can always define it in the I<hidden> container, as it'll be explained later. +=item * changes + + changes => 'Changes.pod', + +The I<changes> attribute accepts a single element which is a source +chapter for the changes file. The only difference from the I<hidden> +chapter is that it's possible to access directly to its navigation +object from within the index templates, via: + + [% + changes_id = doc.nav.index_node.extra.changes; + IF changes_id; + changes_nav = doc.nav.by_id(changes_id); + -%] + +Now C<changes_nav> points to the changes chapter, similar to +C<doc.nav>. So for example you can retrieve a link to it as: + + changes_nav.meta.link + +or the title as: + + changes_nav.meta.title + +This element was added as an improvement over the inclusion of the +I<Changes.pod> chapter or alike along with all other chapters because +usually people don't want to see changes and when the docset pdf is +created huge changes files can be an unwanted burden, so now if this +attribute is included, the pdf for the docset won't include this file +in it. + =back This is an example: @@ -720,6 +799,8 @@ chapters => 'foo/bar/zed.pod', + changes => 'Changes.pod', + links => [ { id => 'asf', @@ -747,7 +828,7 @@ part II: Troubleshooting * Debugging * Errors - * Help Links + * ASF * Offline Help This happens only if this feature is used, otherwise a plain flat toc @@ -759,7 +840,14 @@ group => 'Troubleshooting', chapters => [qw(debug.pod errors.pod)], - links => [{put link data here}], + links => [ + { + id => 'asf', + link => 'http://apache.org/foundation/projects.html', + title => 'The ASF Projects', + abstract => "There many other ASF Projects", + }, + ], chapters => ['offline_help.pod'], 1.8 +10 -1 modperl-docs/lib/DocSet/Doc.pm Index: Doc.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/Doc.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- Doc.pm 12 Jun 2002 02:53:46 -0000 1.7 +++ Doc.pm 29 Jun 2002 19:13:49 -0000 1.8 @@ -44,7 +44,8 @@ my $rel_doc_root = $self->{rel_doc_root}; my $abs_doc_root = $self->{abs_doc_root}; $abs_doc_root .= "/$rel_doc_root" - if defined $rel_doc_root and $rel_doc_root ne '.'; + if defined $rel_doc_root && + length($rel_doc_root) && $rel_doc_root ne '.'; $abs_doc_root =~ s|^./||; # IE/Mac can't handle path ./../foo @@ -140,6 +141,14 @@ return undef; } +require Carp; +sub croak { + my($self, @msg) = @_; + Carp::croak("[render croak] ", @msg, "\n", + "[src path] $self->{src_path}\n" + ); + +} # abstract methods #sub src_filter {} 1.13 +22 -15 modperl-docs/lib/DocSet/DocSet.pm Index: DocSet.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/DocSet.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- DocSet.pm 14 May 2002 05:24:57 -0000 1.12 +++ DocSet.pm 29 Jun 2002 19:13:49 -0000 1.13 @@ -35,15 +35,16 @@ # we assume that the docset was not modified since the last run. # if at least one source doc/config file was modified, the docset # is considered modified as well and should be rebuild. It's the - # responsibility of the modified object to make its parent docset - # as modified. + # responsibility of the modified object to set its parent docset + # status to 'modified'. $self->modified(0); - # currently there are 5 reasons why the docset is considered - # 'modified', if at least one of these is 'modified': - # 1. the included docset - # 2. the included chapter - # 3. the included 'copy as-is' files + # currently a given docset is considered to be in the 'modified' state, + # if any of these conditions is true: + # + # 1. the included docset is 'modified': + # 2. the included chapter is 'modified': + # 3. the included 'copy as-is' files are 'modified': # 4. config.cfg is newer than corresponding index.html # 5. the cache file is missing @@ -76,12 +77,16 @@ $cache->invalidate if get_opts('rebuild_all'); # cache the index node meta data - $cache->index_node($self->get('id'), - $self->get('stitle'), - $self->get('title'), - $self->get('abstract') + $cache->index_node(id => $self->get('id'), + stitle => $self->get('stitle'), + title => $self->get('title'), + abstract => $self->get('abstract'), + extra => $self->get('extra'), ); + # croaks if the docset id is duplicated + $self->check_duplicated_docset_ids(); + # cache the location of the parent node cache if (my $parent_o = $self->get('parent_o')) { my $parent_src_root = $parent_o->get_dir('src_root'); @@ -155,7 +160,8 @@ $cache->node_groups($self->node_groups); - # compare whether the config file is newer than the index.html + # compare whether the config file is newer than the corresponding + # index.html my $dst_root = $self->get_dir('dst_root'); my $config_file = $self->{config_file}; @@ -265,14 +271,15 @@ $rel_dst_path =~ s|^\./||; # strip the leading './' my $dst_path = "$dst_root/$rel_dst_path"; - my $rel_doc_root = join '/', ("..") x ($rel_dst_path =~ tr|/|/|); - $rel_doc_root = "." unless $rel_doc_root; + my $rel_doc_root = $rel_dst_path =~ m|/| + ? join('/', ("..") x ($rel_dst_path =~ tr|/|/|)) + : '.'; # push to the list of final chapter paths e.g. used by PS/PDF # build, which needs all the non-hidden chapters $self->trg_chapters($rel_dst_path) unless $hidden; - ### to rebuild or not to rebuild + ### to rebuild or not my($should_update, $reason) = $self->should_update($src_path, $dst_path); if (!$should_update) { note "--- $src_file: skipping ($reason)"; 1.5 +6 -0 modperl-docs/lib/DocSet/NavigateCache.pm Index: NavigateCache.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/NavigateCache.pm,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- NavigateCache.pm 11 Jun 2002 15:02:10 -0000 1.4 +++ NavigateCache.pm 29 Jun 2002 19:13:49 -0000 1.5 @@ -91,6 +91,11 @@ } } +# get the object by its id (string) within the current cache +sub by_id { + my($self, $id) = @_; + return defined $id ? $self->new($self->[CUR_PATH], $id) : undef; +} # get the object of the first item on the same level @@ -181,6 +186,7 @@ sub id { shift->[ID]; } + sub get_cache { my($cache_path) = @_; 1.10 +45 -0 modperl-docs/lib/DocSet/RunTime.pm Index: RunTime.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/RunTime.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- RunTime.pm 17 Jun 2002 02:58:22 -0000 1.9 +++ RunTime.pm 29 Jun 2002 19:13:49 -0000 1.10 @@ -16,6 +16,8 @@ @ISA = qw(Exporter); @EXPORT = qw(get_opts find_src_doc set_render_obj get_render_obj unset_render_obj); +my %registers = (); + my @search_paths = (); my %src_docs = (); my %exts = (); @@ -27,6 +29,17 @@ # }, # ); +sub registers_reset { + %registers = (); +} + +sub register { + my($register, $key, $val) = @_; + push @{$registers{$register}{$key}}, $val; + return @{ $registers{$register}{$key} || []}; +} + + sub set_opt { my(%args) = (); if (@_ == 1) { @@ -186,6 +199,7 @@ undef $render_obj; } + 1; __END__ @@ -232,6 +246,37 @@ Only get_opts() method is exported by default. =over + +=item * registers_reset() + +This function resets various run-time registers, used for validations. + +If the runtime is run more than once remember to always run first this +function and even better always run it before using the runtime. e.g.: + + DocSet::RunTime::registers_reset(); + my $docset = DocSet::DocSet::HTML->new($config_file); + $docset->set_dir(abs_root => "."); + $docset->scan; + $docset->render; + +=item * register + + my @entries = register($register_name, $key, $val); + +Push into the register for a given key the supplied value. + +Return an array of the given register's key. + +For example used to track duplicated docset ids with: + + my @entries = DocSet::RunTime::register('unique_docset_id', $id, + $self->{config_file}); + die if @entries > 1; + +because if the register returns two value for the same key, someone +has already registered that key before. The values in C<@entries> +include the config files in this example. =item * set_opt(\%args) 1.14 +15 -1 modperl-docs/lib/DocSet/Doc/Common.pm Index: Common.pm =================================================================== RCS file: /home/cvs/modperl-docs/lib/DocSet/Doc/Common.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- Common.pm 13 Jun 2002 09:20:16 -0000 1.13 +++ Common.pm 29 Jun 2002 19:13:49 -0000 1.14 @@ -142,7 +142,21 @@ $anchor =~ s/^\s*|\s*$//g; # strip leading and closing spaces $anchor =~ s/\W/_/g; my $link = $title->present($self); - return qq{<a name="$anchor" href="#toc_$anchor">$link</a>}; + + # assuming that the object lives only while a single doc is + # rendered we can use this seen techniques, by storing the seen + # anchors within the object itself. unfortunately currently there + # is no way to dump the context (where the problem is) and + # workaround is to run with -v, so the error will happen right + # after reporting the file that was rendered + + # die on duplicated anchors + my $render_obj = get_render_obj(); + $render_obj->{__seen_anchors}{$anchor}++; + $render_obj->croak("a duplicated anchor: '$anchor'\nfor title: '$title'\n") + if $render_obj->{__seen_anchors}{$anchor} > 1; + + return qq{<a name="$anchor"></a><a href="#toc_$anchor">$link</a>}; }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]