home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / RunTime.pm < prev    next >
Encoding:
Perl POD Document  |  2002-08-18  |  11.1 KB  |  401 lines

  1. package DocSet::RunTime;
  2.  
  3. # META: this class acts as Singleton, consider to actually use
  4. # Class::Singleton
  5.  
  6. use strict;
  7. use warnings;
  8.  
  9. use File::Spec::Functions qw(catdir catfile splitdir);
  10. use File::Find;
  11.  
  12. use DocSet::Util;
  13. use Carp;
  14.  
  15. use vars qw(@ISA @EXPORT %opts);
  16. @ISA    = qw(Exporter);
  17. @EXPORT = qw(get_opts find_src_doc set_render_obj get_render_obj unset_render_obj);
  18.  
  19. my %registers = ();
  20.  
  21. my @search_paths = ();
  22. my %src_docs = ();
  23. my %exts     = ();
  24. my $render_obj;
  25.  
  26. sub registers_reset {
  27.     %registers = ();
  28. }
  29.  
  30. sub register {
  31.     my($register, $key, $val) = @_;
  32.     push @{$registers{$register}{$key}}, $val;
  33.     return @{ $registers{$register}{$key} || []};
  34. }
  35.  
  36.  
  37. sub set_opt {
  38.     my(%args) = ();
  39.     if (@_ == 1) {
  40.         my $arg = shift;
  41.         my $ref = ref $arg;
  42.         if ($ref) {
  43.             %args = $ref eq 'HASH' ? %$arg : @$arg;
  44.         } else {
  45.             die "must be a ref to or an array/hash";
  46.         }
  47.     } else {
  48.         %args = @_;
  49.     }
  50.     @opts{keys %args} = values %args;
  51. }
  52.  
  53. sub get_opts {
  54.     my $opt = shift;
  55.     exists $opts{$opt} ? $opts{$opt} : '';
  56. }
  57.  
  58. # check whether we have a Storable avalable
  59. use constant HAS_STORABLE => eval { require Storable; };
  60. sub has_storable_module {
  61.     return HAS_STORABLE;
  62. }
  63.  
  64. # check for existence of html2ps and ps2pdf
  65.  
  66. my $html2ps_exec = which('html2ps');
  67. sub can_create_ps {
  68.     # ps2html is bundled, so we can always create PS
  69.     return $html2ps_exec if $html2ps_exec;
  70.  
  71.     print 'It seems that you do not have html2ps installed! You have',
  72.         'to install it if you want to generate the PDF file';
  73.     return 0;
  74.  
  75.     # if you unbundle it make sure you write here a code similar to
  76.     # can_create_pdf()
  77. }
  78.  
  79. my $ps2pdf_exec = which('ps2pdf');
  80. sub can_create_pdf {
  81.     # check whether ps2pdf exists
  82.     return $ps2pdf_exec if $ps2pdf_exec;
  83.  
  84.     print 'It seems that you do not have ps2pdf installed! You have',
  85.         'to install it if you want to generate the PDF file';
  86.     return 0;
  87. }
  88.  
  89. sub scan_src_docs {
  90.     my($base, $ra_search_paths, $ra_search_exts) = @_;
  91.  
  92.     @search_paths = @{$ra_search_paths || []};
  93.  
  94.     # .cfg is for matching config.cfg to become index.html
  95.     %exts = map {$_ => 1} @{$ra_search_exts || []}, 'cfg';
  96.  
  97.     my @ext_accept_pattern = map {quotemeta($_)."\$"} keys %exts;
  98.     my $rsub_keep_ext = 
  99.         build_matchmany_sub(\@ext_accept_pattern);
  100.  
  101.     my %seen;
  102.     for my $rel_path (@search_paths) {
  103.         my $full_base_path = catdir $base, $rel_path;
  104.         die "'search_paths' attr: $full_base_path is not a dir" 
  105.             unless -d $full_base_path;
  106.  
  107.         my @seen_pattern = map {"^".quotemeta($_)} keys %seen;
  108.         my $rsub_skip_seen = build_matchmany_sub(\@seen_pattern);
  109.  
  110.         my $rel_uri = path2uri($rel_path);
  111.         $src_docs{$rel_uri} = {
  112.                                             # autogenerated index.html
  113.             map { s/config\.cfg$/index.html/; ($_ => 1) }
  114.                                             # full path => relative uri
  115.             map path2uri( DocSet::Util::abs2rel($_, $full_base_path) ),
  116.             grep $rsub_keep_ext->($_),      # get files with wanted exts
  117.             grep !$rsub_skip_seen->($_),    # skip seen base dirs
  118.             @{ expand_dir($full_base_path) }
  119.         };
  120.  
  121.         note "Scanning for src files: $full_base_path";
  122.         $seen{$full_base_path}++;
  123.     }
  124.  
  125. #   dumper \%src_docs;
  126. }
  127.  
  128. # this function returns a URI, so its separators are always /
  129. sub find_src_doc {
  130.     my($resource_rel_path) = @_;
  131.  
  132.     # push the html extension, because of autogenerated index.html,
  133.     # which should be found automatically
  134.     $exts{html} = 1 unless exists $exts{html};
  135.  
  136.     for my $path (keys %src_docs) {
  137.         if (my $found_path = match_in_doc_src_subset($path,
  138.                                                      $resource_rel_path)) {
  139.             return path2uri($found_path);
  140.         }
  141.     }
  142.  
  143.     # if we didn't find anything so far, it's possible that the path was
  144.     # specified with a longer prefix, that was needed (the above
  145.     # searches only the end leaves), so try locate the segments of the
  146.     # search path and search within maching sub-sets
  147.     for my $path (@search_paths) {
  148.         if ($resource_rel_path =~ m|^$path/(.*)|) {
  149.             if (my $found_path = match_in_doc_src_subset($path, $1)) {
  150.                 return path2uri($found_path);
  151.             }
  152.         }
  153.     }
  154.  
  155. #dumper  $src_docs{"docs/1.0"};
  156.     return;
  157. }
  158.  
  159. # accepts the base_path (from the @search_paths) and the rel_path as
  160. # args, then it tries to find the match by applying known extensions.
  161. #
  162. # if matched, returns the whole path relative to the root, otherwise
  163. # returns undef
  164. sub match_in_doc_src_subset {
  165.     my ($base_path, $rel_path) = @_;
  166.     for my $ext (keys %exts) {
  167. #print qq{Try:  $base_path :: $rel_path.$ext\n};
  168.         if (exists $src_docs{$base_path}{"$rel_path.$ext"}) {
  169. #print qq{Found $base_path/$rel_path.$ext\n};
  170.             return catdir $base_path, "$rel_path.$ext";
  171.         }
  172.     }
  173.     return;
  174. }
  175.  
  176. # set render object: sort of Singleton, it'll complain aloud if the
  177. # object is set over the existing object, without first unsetting it
  178. sub set_render_obj {
  179.     Carp::croak("usage: set_render_obj(\$obj) ") unless @_;
  180.     Carp::croak("unset render_obj before setting a new one") if $render_obj;
  181.     Carp::croak("undefined render_obj passed") unless defined $_[0];
  182.     $render_obj = shift;
  183. }
  184.  
  185. sub get_render_obj { 
  186.     Carp::croak("render_obj is not available") unless $render_obj;
  187.  
  188.     return $render_obj;
  189. }
  190.  
  191. sub unset_render_obj {
  192.     Carp::croak("render_obj is not set") unless $render_obj;
  193.  
  194.     undef $render_obj;
  195. }
  196.  
  197.  
  198. 1;
  199. __END__
  200.  
  201. =head1 NAME
  202.  
  203. C<DocSet::RunTime> - RunTime Configuration
  204.  
  205. =head1 SYNOPSIS
  206.  
  207.   use DocSet::RunTime;
  208.  
  209.   # run time options
  210.   DocSet::RunTime::set_opt(\%args);
  211.   if (get_opts('verbose') {
  212.       print "verbose mode";
  213.   }
  214.  
  215.   # hosting system capabilities testing
  216.   DocSet::RunTime::has_storable_module();
  217.   DocSet::RunTime::can_create_ps();
  218.   DocSet::RunTime::can_create_pdf();
  219.  
  220.   # source documents lookup
  221.   DocSet::RunTime::scan_src_docs($base_path, \@search_paths, \@search_exts);
  222.   my $full_src_path = find_src_doc($resource_rel_path);
  223.  
  224.   # rendering object singleton
  225.   set_render_obj($obj);
  226.   unset_render_obj();
  227.   $obj = get_render_obj();
  228.  
  229. =head1 DESCRIPTION
  230.  
  231. This module is a part of the docset application, and it stores the run
  232. time arguments, caches results of expensive calls and provide
  233. Singleton-like service to the whole system.
  234.  
  235. =head1 FUNCTIONS
  236.  
  237. META: To be completed, see SYNOPSIS 
  238.  
  239. =head2 Run Time Options
  240.  
  241. Only get_opts() method is exported by default.
  242.  
  243. =over
  244.  
  245. =item * registers_reset()
  246.  
  247. This function resets various run-time registers, used for validations.
  248.  
  249. If the runtime is run more than once remember to always run first this
  250. function and even better always run it before using the runtime. e.g.:
  251.  
  252.   DocSet::RunTime::registers_reset();
  253.   my $docset = DocSet::DocSet::HTML->new($config_file);
  254.   $docset->set_dir(abs_root => ".");
  255.   $docset->scan;
  256.   $docset->render;
  257.  
  258. =item * register
  259.  
  260.   my @entries = register($register_name, $key, $val);
  261.  
  262. Push into the register for a given key the supplied value.
  263.  
  264. Return an array of the given register's key.
  265.  
  266. For example used to track duplicated docset ids with:
  267.  
  268.     my @entries = DocSet::RunTime::register('unique_docset_id', $id,
  269.                                            $self->{config_file});
  270.     die if @entries > 1;
  271.  
  272. because if the register returns two value for the same key, someone
  273. has already registered that key before. The values in C<@entries>
  274. include the config files in this example.
  275.  
  276. =item * set_opt(\%args)
  277.  
  278.  
  279. =item * get_opts()
  280.  
  281.  
  282. =back
  283.  
  284. =head2 Hosting System Capabilities Testing
  285.  
  286. These methods test the capability of the system and are a part of the
  287. runtime system to perform the checking only once.
  288.  
  289. =over
  290.  
  291. =item * has_storable_module
  292.  
  293.  
  294. =item * can_create_ps
  295.  
  296.  
  297. =item * can_create_pdf
  298.  
  299. =back
  300.  
  301. =head2 Source Documents Lookup
  302.  
  303. A system for mapping L<> escapes to the located of the rendered
  304. files. This system scans once the C<@search_paths> for files with
  305. C<@search_exts> starting from C<$base_path> using scan_src_docs(). The
  306. C<@search_paths> and C<@search_exts> are configured in the
  307. I<config.cfg> file. For example:
  308.  
  309.     dir => {
  310.              # search path for pods, etc. must put more specific paths first!
  311.              search_paths => [qw(
  312.                  foo/bar
  313.                  foo
  314.                  .
  315.              )],
  316.              # what extensions to search for
  317.              search_exts => [qw(pod pm html)],
  318.          },    
  319.  
  320. So for example if the base path is I<~/myproject/src>, the files with
  321. extensions I<.pod>, I<.pm> and I<.html> will be searched in
  322. I<~/myproject/src/foo/bar>, I<~/myproject/src/foo> and
  323. I<~/myproject/src>.
  324.  
  325. Notice that you must specify more specific paths first, since for
  326. optimization the seen paths are skipped. Therefore in our example the
  327. more explicit path I<foo/bar> was listed before the more general
  328. I<foo>.
  329.  
  330. When the POD parser finds a L<> sequence it indentifies the resource
  331. part and passes it to the find_src_doc() which looks up for this file
  332. in the cache and returns its original (src) location, which can be
  333. then easily converted to the final location and optionally adjusting
  334. the extension, e.g. when the POD file is converted to HTML.
  335.  
  336. As a special extension this function automatically assumes that
  337. C<index.html> will be generated in each directory containing items of
  338. an interest. Therefore in find_src_doc() it'll automatically find
  339. things like: L<the guide|guide::index>, even though there was no
  340. source I<index.pod> or I<index.html> in first place.
  341.  
  342. Only the find_src_doc() function is exported by default.
  343.  
  344. =over
  345.  
  346. =item * scan_src_docs($base_path, \@search_paths, \@search_exts);
  347.  
  348. =item * find_src_doc($resource_rel_path);
  349.  
  350. returns C<undef> if nothing was found. See the description above.
  351.  
  352. =back
  353.  
  354.  
  355. =head2 Rendering Object Singleton
  356.  
  357. Since the rendering process may happen by a third party system, into
  358. which we provide hooks or overload some of its methods, it's quite
  359. possible that we won't be able to access the current document (or
  360. better rendering) object. One solution would be to have a global
  361. package variable, but that's very error-prone. Therefore the used
  362. solution is to provide a hook into a RunTime environment setting the
  363. current rendering object when the rendering of a single page starts
  364. via C<set_render_obj($obj)> and unsetting it when it's finished via
  365. unset_render_obj(). Between these two moments the current rendering
  366. object can be retrieved with get_render_obj() method.
  367.  
  368. Notice that this is all possible in the program which is not threaded,
  369. or/and only one rendering process exists at any given time from its
  370. start to its end.
  371.  
  372. All three methods are exported by default.
  373.  
  374. =over
  375.  
  376. =item * set_render_obj($obj)
  377.  
  378. Sets the current rendering object.
  379.  
  380. You cannot set a new rendering object before the previous one is
  381. unset. This is in order to make sure that one document won't use by
  382. mistake a rendering object of another document. So when the rendering
  383. is done remember to call the unset_render_obj() function.
  384.  
  385. =item * unset_render_obj()
  386.  
  387. Unsets the currently set rendering object.
  388.  
  389. =item * get_render_obj()
  390.  
  391. Retrieves the currently set rendering object or complains aloud if it
  392. cannot find one.
  393.  
  394. =back
  395.  
  396. =head1 AUTHORS
  397.  
  398. Stas Bekman E<lt>stas (at) stason.orgE<gt>
  399.  
  400. =cut
  401.