cvsuser 03/01/07 08:34:44
Added: Devel-Metadata Changes MANIFEST Makefile.PL README TODO
Devel-Metadata/lib/Devel Metadata.pm
Devel-Metadata/t 01.reference.t 02.distributions.t
03.modules.t 04.module.t
Log:
new file
Revision Changes Path
1.1 p5ee/Devel-Metadata/Changes
<<Binary file>>
1.1 p5ee/Devel-Metadata/MANIFEST
Index: MANIFEST
===================================================================
MANIFEST
Makefile.PL
README
Changes
TODO
lib/Devel/Metadata.pm
t/01.reference.t
t/02.distributions.t
t/03.modules.t
t/04.module.t
1.1 p5ee/Devel-Metadata/Makefile.PL
Index: Makefile.PL
===================================================================
######################################################################
## File: $Id: Makefile.PL,v 1.1 2003/01/07 16:34:44 spadkins Exp $
######################################################################
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
%opts = (
#'INSTALLDIRS' => 'perl',
'NAME' => 'Devel-Metadata',
'DISTNAME' => 'Devel-Metadata',
'VERSION' => '0.50',
'PREREQ_PM' => { # I haven't yet determined what the minimum versions should be
#'Aspect' => 0, # used for debugging
#'Class::MethodMaker' => 0, # [prereq for Aspect]
auto-generate methods
#'Compress::Zlib' => 0, # for compressed
serialization and browser responses
#'Data::Dumper' => 0, # used for debugging
#'Date::Parse' => 0, # date support
#'Date::Format' => 0, # date support
#'Storable' => 0, # used for serialization
everywhere
},
#'linkext' => { LINKTYPE=>'' }, # no link needed
'dist' => {'COMPRESS'=>'gzip -9f', 'SUFFIX' => 'gz',
'ZIP'=>'/usr/bin/zip','ZIPFLAGS'=>'-rl'},
);
######################################################################
# PREFIX
######################################################################
# I like setting the PREFIX variable in the environment,
# so I write my Makefile.PL to use it as a valid alternative
# to specifying it on the command line with
# "perl Makefile.PL PREFIX=/usr/foo".
if ($ENV{PREFIX}) {
$PREFIX = $ENV{PREFIX};
$opts{PREFIX} = $PREFIX;
}
else {
$PREFIX = "/usr/local";
}
######################################################################
# MAKE THE MAKEFILE
######################################################################
WriteMakefile(%opts);
1.1 p5ee/Devel-Metadata/README
Index: README
===================================================================
The Devel::Metadata module serves as an aggregator and cache for
Perl metadata information gathered from a number sources by way
of other modules.
The following modules are used to collect metadata.
Module::Info - Information about Perl modules
http://search.cpan.org/author/MSCHWERN/Module-Info-0.12/lib/Module/Info.pm
Devel::ModInfo - metadata about a module's methods, properties, and arguments
http://search.cpan.org/author/JTILLMAN/Devel-ModInfo-0.05/ModInfo.pm
CPANPLUS::Backend - Object-oriented interface for CPAN++
http://search.cpan.org/author/KANE/CPANPLUS-0.036/lib/CPANPLUS/Backend.pm
Devel::Symdump - load module and examine symbol table
Useful for Devel::Doc
DocSet -
Pod::Html
Module::Dependency::Grapher - creates visual dependency charts and accessible
text versions
http://search.cpan.org/author/PKENT/Module-Dependency-1.11/lib/Module/Dependency/Grapher.pm
The following is the first part of the POD doc turned into text.
NAME
Devel::Metadata - programmatic access to metadata for Perl code
(Distributions, Modules, Classes, Methods, etc.)
SYNOPSIS
use Devel::Metadata;
# public usage (perl metadata)
$dmd = Devel::Metadata->new();
@distributions = $dmd->distributions(); # get all installed distributions
@modules = $dmd->modules(); # get all installed modules
@modules = $dmd->modules("SOAP-Lite"); # get modules for a distribution
$modules_def = $dmd->module_def("CGI"); # get module metadata (hashref)
@packages = $dmd->packages("CGI"); # get packages defined in a module
$package_def = $dmd->package_def("CGI",); # get package metadata (hashref)
@functions = $dmd->functions("CGI"); # get functions/methods in a package
$function_def = $dmd->function_def("CGI","param"); # function metadata
(hashref)
@signatures = $dmd->signatures("CGI","param"); # signatures for a
function/method
# protected usage
print $dmd->dump(), "\n"; # use Data::Dumper to spit out the Perl
representation
$dmd->print(); # same as above
$value = $dmd->get($property_name);
$branch = $dmd->get_branch($branch_name,$create_flag); # get hashref
$dmd->set($property_name, $value);
DESCRIPTION
Devel::Metadata allows you to access metadata about the perl modules
(.pm) and perl libraries (.pl) you have installed on your system.
Since there is no simple way to discern this information, a variety of
techniques are used. Scanning/parsing the source code allows for
metadata to be gleaned without actually loading the code. However, it is
imperfect because standard coding idioms may not be used. Another method
involves actually loading the code to examine its effects on symbol
tables. However, this is not perfect because some routines may be
created on demand using autoloading or other special perl features.
Ultimately, this metadata may be entered explicitly and authoritatively
by the developer into the POD documentation using special documentation
tags recognized by Devel::Metadata. Thus, the determination of metadata
in perl code and the documentation system are interwoven. This is why
there are a handful of documentation features in a module about
metadata.
Because some of these operations might be resource-intensive,
Devel::Metadata can pretermine these metadata attributes and save them
to a cache area so that the information can be accessed at runtime in a
reasonably efficient manner.
METADATA DEFINITIONS
The following are definitions in Perl metadata.
Perl Site
- an entire perl site, defined by a list of directories to be
searched in order for perl modules. The default perl site is defined
by the set of directories in @INC, displayed with the following
command (in between square brackets): [perl -e 'print "@INC\n";'].
Distribution
- a group of files such as would be downloaded from CPAN which are
tested and installed together. Each Distribution has a "name",
"version", "modules" list, "libraries" list, "files" list of all
files in the distribution, and special file lists of
"autoload_files", "shlib_files", and "man_files".
Library
- a file ending in ".pl" ([p]erl [l]ibrary) which can be loaded with
the [requre "libraryname.pl";] statement. (This is mainly a feature
from Perl 4. Perl Libraries are generally replaced with Perl Modules
in Perl 5.)
Module
- a file ending in ".pm" ([p]erl [m]odule) which can be loaded with
the [use ModuleName;] statement. (See "man perlmod" for more on perl
modules.) Each Module is in a single Distribution. If that
Distribution is not known, it is assigned to the "unknown"
Distribution. A Module may be documented in its own ".pm" file or
(additionally) in an accompanying ".pod" file. Most Modules defined
functions (and perhaps variables) in a single Package and often
implement Classes, but this is not necessary.
Package
- a perl symbol namespace which is often unique to a particular
Module. (see the "package" keyword in the "perlfunc" man page.)
Sometimes a single Module will load Functions for multiple Packages.
It is possible (but rare) that a Package may have Functions defined
for it from multiple Modules.
Function
- a function is technically in a Package, not a Module. However, it
is most often defined in that Package as the result of loading a
Module. Each Function in perl can have many Signatures.
Signature
- a Signature for a Function is a set of Parameters that define a
manner in which the Function may be called. There are always one or
more Signatures for each Function.
Parameter
- a Parameter to a Function has a name, a type, and a description,
and an indicator whether it is readonly ("in"), whether it or its
referenced contents are simply to be returned ("out"), or whether it
or its referenced contents may be examined and then modified
("in/out").
Class
- a special kind of Package which has been written to facilitate
object-oriented programming. (See perlboot, perltoot, perltootc,
perlobj, perlbot.) In order for a Module to be a Class, it must have
a constructor, and its functions must be callable with the first
argument used in the special way which make them "Methods".
Method
- a special kind of Function which has been written to facilitate
object-oriented programming. If the first argument is expected to be
the class name (package name), the method is usually referred to as
a Class Method (or Static Method) to differentiate it from the usual
usage of "Method" as an Instance Method. If the first argument is
expected to be an instance of the class, it is usually referred
simply as a Method (but sometimes by the more verbose Instance
Method, Object Method, or Dynamic Method).
METADATA DICTIONARY
The metadata to be gleaned from the source code is as follows.
# Distribution Files
$self->{distfile}{$distfile}{distribution}
# Distributions
$self->{distribution}{$distribution}
$self->{distribution}{$distribution}{version}
$self->{distribution}{$distribution}{modules}
$self->{distribution}{$distribution}{libraries}
$self->{distribution}{$distribution}{files}
$self->{distribution}{$distribution}{autoload_files}
$self->{distribution}{$distribution}{shlib_files}
$self->{distribution}{$distribution}{man_files}
# Modules
$self->{module}{$module}
$self->{module}{$module}{distribution}
$self->{module}{$module}{pmfile}
$self->{module}{$module}{podfile}
$self->{module}{$module}{modulegroup}
$self->{module}{$module}{name}
$self->{module}{$module}{synopsis}
$self->{module}{$module}{description}
$self->{module}{$module}{throws}
$self->{module}{$module}{since}
$self->{module}{$module}{deprecated_since}
$self->{module}{$module}{deprecated_discontinue}
$self->{module}{$module}{authors}
$self->{module}{$module}{author}{$author}{email}
$self->{module}{$module}{license}
$self->{module}{$module}{see_also}
# Modules (object-oriented)
$self->{module}{$module}{class}
$self->{module}{$module}{parent}
$self->{module}{$module}{parents}
$self->{module}{$module}{children}
# Functions (Methods)
$self->{module}{$module}{function}{$function}
$self->{module}{$module}{function}{$function}{doc}
$self->{module}{$module}{function}{$function}{visibility}
$self->{module}{$module}{function}{$function}{signatures}
$self->{module}{$module}{function}{$function}{param}{$param}
$self->{module}{$module}{function}{$function}{param}{$param}{type}
$self->{module}{$module}{function}{$function}{param}{$param}{inout}
$self->{module}{$module}{function}{$function}{return}{$return}
$self->{module}{$module}{function}{$function}{return}{$return}{type}
$self->{module}{$module}{function}{$function}{throws}
$self->{module}{$module}{function}{$function}{since}
$self->{module}{$module}{function}{$function}{deprecated_since}
$self->{module}{$module}{function}{$function}{deprecated_discontinue}
$self->{modulegroup}{$modulegroup}
$self->{modulegroup}{$modulegroup}{modules}
1.1 p5ee/Devel-Metadata/TODO
<<Binary file>>
1.1 p5ee/Devel-Metadata/lib/Devel/Metadata.pm
Index: Metadata.pm
===================================================================
#############################################################################
## $Id: Metadata.pm,v 1.1 2003/01/07 16:34:44 spadkins Exp $
#############################################################################
package Devel::Metadata;
$VERSION = do { my @r=(q$Revision: 1.1 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
use strict;
=head1 NAME
Devel::Metadata - programmatic access to metadata for Perl code (Distributions,
Modules, Classes, Methods, etc.)
=head1 SYNOPSIS
use Devel::Metadata;
# public usage (perl metadata)
$dmd = Devel::Metadata->new();
@distributions = $dmd->distributions(); # get all installed distributions
@modules = $dmd->modules(); # get all installed modules
@modules = $dmd->modules("SOAP-Lite"); # get modules for a distribution
$modules_def = $dmd->module_def("CGI"); # get module metadata (hashref)
@packages = $dmd->packages("CGI"); # get packages defined in a module
$package_def = $dmd->package_def("CGI",); # get package metadata (hashref)
@subroutines = $dmd->subroutines("CGI"); # get subroutines/methods in a package
$subroutine_def = $dmd->subroutine_def("CGI","param"); # subroutine metadata
(hashref)
@signatures = $dmd->signatures("CGI","param"); # signatures for a
subroutine/method
# protected usage
print $dmd->dump(), "\n"; # use Data::Dumper to spit out the Perl representation
$dmd->print(); # same as above
$value = $dmd->get($property_name);
$branch = $dmd->get_branch($branch_name,$create_flag); # get hashref
$dmd->set($property_name, $value);
=head1 DESCRIPTION
Devel::Metadata allows you to access metadata about the perl modules (.pm)
and perl libraries (.pl) you have installed on your system.
This module was created to support the needs of the Devel::Perldocs
code documentation generator. However, the task of retrieving the
metadata (Devel::Metadata) is separated from the presentation of this
metadata (Devel::Perldocs) in order that the metadata may be used in
other circumstances. These might include runtime parameter checking,
runtime checking of the existence of required modules,
or other uses of metadata at runtime or during code analysis.
Because metadata may be coded in POD documentation within a module,
Devel::Metadata also helps you create and maintain this special
POD documentation. Thus, even without Devel::Perldocs, Devel::Metadata
plays a valuable role in documentation.
=head1 BACKGROUND
=head2 REQUIREMENTS
The module must drop into an existing Perl installation (Perl 5.5.3/5.005_03 or
later)
and be able to report accurately on the distributions and modules/libraries/classes
(along with their subroutines/methods and corresponding signatures) that are
installed on the system.
The module must be fast enough to make it reasonable to query metadata at program
runtime, not just offline during code analysis for tasks such as documentation
generation.
The module must allow that supplementary metadata may be supplied inside the
code (specially tagged comments and specially tagged POD sections) and outside
the code (external metadata files). This is because the Perl language is so
flexible that the metadata about method/subroutine signatures may be difficult
or impossible to discern without an extensive analysis of the code.
The module must help the developer create and maintain the POD documentation
within his code.
=head2 USE CASES
1. For the site (as defined by a particular @INC path, perhaps as supplemented by
PERL5LIB or PERLLIB), tell me:
* What are the distributions (i.e. from CPAN) installed on the system?
2. For each distribution,
* Which other distributions does each distribution depend on?
* What attributes can be known about distributions? (version? date installed?
author?)
* What are the files (.pl, .pm, .pod, .xs/.al) within each distribution?
3. For each module/library,
* What attributes can be known about each module/library? (version? is class?
author?)
* What are the subroutines are imported automatically? possibly?
* What are the subroutines/methods in a library/module?
* What are the parent (inherited) classes/modules of each class/module?
* What are the child (derived) classes/modules of each class/module?
* Which modules use which other modules? requiring which version numbers?
* Are exceptions thrown? Which ones?
4. For each subroutine/method,
* What are the possible signatures of each subroutine/method?
* What attributes can be known about a subroutine? (version introduced?)
* Are exceptions thrown? Which ones?
5. The above analysis of code must also be possible on a single directory of
modules under examination (as opposed to *all* modules installed in the
system).
6. The code analysis may also span any subset of distributions found in the
current installation.
7. After analyzing some module(s), report on any discrepancies detected
between the metadata as determined by any of the various methods.
8. After analyzing some module(s), add correctly formatted POD documentation
to those modules. This allows a developer who has quickly created some
code to put all of the easily determined POD documentation sections into
the code. He can then examine it, add to it, and modify it.
This also allows for reasonably advanced documentation to be created
using the developer's own pod2xxx tools (not necessarily Devel::Perldocs).
9. After analyzing some module(s), update correctly formatted POD documentation
to those modules. This allows a developer who has modified some code to
check to see if any of the documentation sections is out of date and helps
to keep it accurate.
=head2 PRIOR WORK
A variety of modules exist which have tackled various parts of this problem
or related problems.
1. Modinfo
http://sourceforge.net/projects/modinfo
ModInfo provides a simple means of documenting your Perl module interfaces,
and an easy way of using that documentation at runtime or design time.
The information provided is modelled after BeanInfo for Java Beans.
ModInfo requires the use of numerous "# MODINFO" comments within the code.
It is not able to report on code which has not been prepared in this way.
It may (?) allow for external metadata files to supplement the metadata
which is embedded in the code (via the special comments).
It caches this metatdata so that it is suitable for runtime use.
2. Module::Info
http://search.cpan.org/author/MBARBON/Module-Info/lib/Module/Info.pm
Module::Info is the most extensive analyzer of metadata for raw perl code
which has had no specially tagged comments added. It can report a few
elements of metadata about a module without loading it. However, most of
the interesting information (subroutines and inheritance) can only be
determined after the module is loaded. The modules get loaded in a
separate process, which is good for keeping your perl program unpolluted
but bad for runtime performance.
Module::Info requires Perl 5.6.1 and has no ability to determine
subroutine/method signatures. However, in many ways it is more reliable
than source scanning because it actually examines the Perl symbol table.
It also provides no means of determining what are the installed distributions
(from CPAN) or what the modules are that are in those distributions.
3. Attribute::Signature
http://search.cpan.org/author/JDUNCAN/Attribute-Signature/lib/Attribute/Signature.pm
This module allows for a module author to specify the signature of a
subroutine explicitly so that it is enforced by the Perl interpreter.
It requires Perl 5.6.1. It does not provide any way for an external
program to determine what that signature is (such as for documentation).
=head2 DESIGN
The Devel::Metadata module should have the following design.
* collect metadata from a variety of sources
* report on conflicts or agreement
* save metadata in a cache
* report metadata from cache when requested (for performance)
* metadata sources include:
* raw source, scanned as a first approximation
* specially tagged POD documentation supplements metadata for new code
* specially tagged comments also supplement metadata for new code
* external metadata files supplement metadata for legacy code
* Module::Info can be used to cross-check any of the other sources
* CPAN or CVS (perhaps to derive version-related or author-related information)
=head2 METADATA DEFINITIONS
The following are definitions with regard to Perl metadata.
=over 4
=item Perl Site
- an entire perl site, defined by a list of directories to be searched
in order for perl modules. The default perl site is defined by the set
of directories in @INC, displayed with the following command (in
between square brackets): [perl -e 'print "@INC\n";']. This search
path is potentially supplemented by the PERL5LIB and PERLLIB
environment variables.
Note that there may be multiple (possibly overlapping) Perl Sites on
any given machine. This might happen if the standard perl modules
are installed in "/usr/local/lib/perl5" but various versions of
application perl modules exist in other directories such as
"/usr/app/devel/lib", "/usr/app/1.0.3/lib", "/usr/app/2.0.0/lib", etc.
=item Perl Subsite
- within an entire perl site, there may be a subset of those directories
which are the target for code analysis. This is called a Perl Subsite.
Only distributions from the Subsite are returned by the
$dmd->distributions() method. This is useful when you wish to
analyze only a limited set of modules or distributions (Subsite)
within the context of the greater Site.
=item Distribution
- a group of files such as would be downloaded from CPAN which are
tested and installed together. Each Distribution has a "name",
"version", "modules" list, "libraries" list, "files" list of all
files in the distribution, and special file lists of
"autoload_files", "shlib_files", and "man_files".
=item Library
- a file ending in ".pl" ([p]erl [l]ibrary) which can be loaded with
the [require "libraryname.pl";] statement. (This is mainly a
feature from Perl 4. Perl Libraries are generally replaced with
Perl Modules in Perl 5.)
=item Module
- a file ending in ".pm" ([p]erl [m]odule) which can be loaded with
the [use ModuleName;] statement. (See "man perlmod" for more on
perl modules.)
Each Module is in a single Distribution. If that
Distribution is not known, it is assigned to the "unknown" Distribution.
A Module may be documented in its own ".pm" file or (additionally)
in an accompanying ".pod" file.
Most Modules defined subroutines (and perhaps variables) in a single
Package and often implement Classes, but this is not necessary.
=item Module Group
- some distributions are large and contain many modules which are
logically separated into different groups. These groups are
inferred by the directory structure within the distribution.
Each directory represents a Module Group. If a Module exists
with the same name as a directory, it belongs in the same
Module Group. (i.e. CPAN and CPAN::Admin are in the same Module Group.)
=item Package
- a perl symbol namespace which is often unique to a particular Module.
(see the "package" keyword in the "perlfunc" man page.)
Sometimes a single Module will load Subroutines for multiple Packages.
It is possible (but rare) that a Package may have Subroutines defined
for it from multiple Modules.
=item Subroutine
- a subroutine is technically in a Package, not a Module.
However, it is most often defined in that Package as the result
of loading a Module.
Each Subroutine in perl can have many Signatures.
=item Signature
- a Signature for a Subroutine is a set of Parameters that define
a manner in which the Subroutine may be called. There are always
one or more Signatures for each Subroutine.
=item Parameter
- a Parameter to a Subroutine has a name, a type, and a description,
and an indicator whether it is readonly ("in"), whether it
or its referenced contents are simply to be returned ("out"),
or whether it or its referenced contents may be examined and
then modified ("in/out").
=item Class
- a special kind of Package which has been written to facilitate
object-oriented programming.
(See perlboot, perltoot, perltootc, perlobj, perlbot.)
In order for a
Module to be a Class, it must have a constructor, and its subroutines
must be callable with the first argument used in the special way
which make them "Methods".
=item Method
- a special kind of Subroutine which has been written to facilitate
object-oriented programming. If the first argument is expected
to be the class name (package name), the method is usually referred
to as a Class Method (or Static Method) to differentiate it from
the usual usage of "Method" as an Instance Method.
If the first argument is
expected to be an instance of the class, it is usually referred
simply as a Method (but sometimes by the more verbose
Instance Method, Object Method, or Dynamic Method).
=back 4
=head1 METADATA DICTIONARY
The metadata to be gleaned from the source code is as follows.
# Distribution Files
$self->{distfile}{$distfile}{distribution}
# Distributions
$self->{distribution}{$distribution}
$self->{distribution}{$distribution}{version}
$self->{distribution}{$distribution}{modules}
$self->{distribution}{$distribution}{libraries}
$self->{distribution}{$distribution}{files}
$self->{distribution}{$distribution}{autoload_files}
$self->{distribution}{$distribution}{shlib_files}
$self->{distribution}{$distribution}{man_files}
# Modules
$self->{module}{$module}
$self->{module}{$module}{distribution}
$self->{module}{$module}{pmfile}
$self->{module}{$module}{podfile}
$self->{module}{$module}{modulegroup}
$self->{module}{$module}{name}
$self->{module}{$module}{synopsis}
$self->{module}{$module}{description}
$self->{module}{$module}{throws}
$self->{module}{$module}{since}
$self->{module}{$module}{deprecated_since}
$self->{module}{$module}{deprecated_discontinue}
$self->{module}{$module}{authors}
$self->{module}{$module}{author}{$author}{email}
$self->{module}{$module}{license}
$self->{module}{$module}{see_also}
# Modules (object-oriented)
$self->{module}{$module}{class}
$self->{module}{$module}{parent}
$self->{module}{$module}{parents}
$self->{module}{$module}{children}
# Subroutines (Methods)
$self->{module}{$module}{subroutine}{$subroutine}
$self->{module}{$module}{subroutine}{$subroutine}{doc}
$self->{module}{$module}{subroutine}{$subroutine}{visibility}
$self->{module}{$module}{subroutine}{$subroutine}{signatures}
$self->{module}{$module}{subroutine}{$subroutine}{param}{$param}
$self->{module}{$module}{subroutine}{$subroutine}{param}{$param}{type}
$self->{module}{$module}{subroutine}{$subroutine}{param}{$param}{inout}
$self->{module}{$module}{subroutine}{$subroutine}{return}{$return}
$self->{module}{$module}{subroutine}{$subroutine}{return}{$return}{type}
$self->{module}{$module}{subroutine}{$subroutine}{throws}
$self->{module}{$module}{subroutine}{$subroutine}{since}
$self->{module}{$module}{subroutine}{$subroutine}{deprecated_since}
$self->{module}{$module}{subroutine}{$subroutine}{deprecated_discontinue}
$self->{modulegroup}{$modulegroup}
$self->{modulegroup}{$modulegroup}{modules}
=cut
#############################################################################
# CONSTRUCTOR METHODS
#############################################################################
=head1 Constructor Methods:
=cut
#############################################################################
# new()
#############################################################################
=head2 new()
This constructor is used to create Reference objects.
Customized behavior for a particular type of Reference
is achieved by overriding the init() method.
* Signature: $dmd = Devel::Metadata->new($array_ref)
* Signature: $dmd = Devel::Metadata->new($hash_ref)
* Signature: $dmd = Devel::Metadata->new("array",@args)
* Signature: $dmd = Devel::Metadata->new(%named)
* Param: $array_ref []
* Param: $hash_ref {}
* Return: $dmd Devel::Metadata
* Throws: <none>
* Since: 0.01
Sample Usage:
use "Devel::Metadata";
$dmd = Devel::Metadata->new("array", "x", 1, -5.4, { pi => 3.1416 });
$dmd = Devel::Metadata->new( [ "x", 1, -5.4 ] );
$dmd = Devel::Metadata->new(
arg1 => 'value1',
arg2 => 'value2',
);
=cut
sub new {
my $this = shift;
my ($class, $self);
$class = ref($this) || $this;
if ($#_ == 0 && ref($_[0]) eq "HASH") {
$self = { %{$_[0]} };
}
elsif ($#_ >= 1 && $#_ % 2 == 1) {
$self = { @_ };
}
else {
$self = {};
}
bless $self, $class;
return $self;
}
#############################################################################
# PUBLIC METHODS
#############################################################################
=head1 Public Methods:
=cut
#############################################################################
# distributions()
#############################################################################
=head2 distributions()
* Signature: @distributions = $dmd->distributions();
* Param: void
* Return: @distributions @
* Throws: <none>
* Since: 0.01
Sample Usage:
@distributions = $dmd->distributions();
print join("\n", sort @distributions), "\n";
=cut
sub distributions {
my ($self) = @_;
$self->_scan_site() if (!$self->{site_scanned});
my @distributions = ();
if ($self->{distribution}) {
@distributions = (keys %{$self->{distribution}});
}
return @distributions;
}
#############################################################################
# modules()
#############################################################################
=head2 modules()
* Signature: @modules = $dmd->modules();
* Param: void
* Return: @modules @
* Throws: <none>
* Since: 0.01
Sample Usage:
@modules = $dmd->modules();
print join("\n", sort @modules), "\n";
=cut
sub modules {
my ($self, $distribution) = @_;
$self->_scan_site() if (!$self->{site_scanned});
$distribution = "all" if (!$distribution);
my @modules = ();
if ($self->{distribution}{$distribution}{modules}) {
@modules = @{$self->{distribution}{$distribution}{modules}};
}
return @modules;
}
#############################################################################
# module_def()
#############################################################################
=head2 module_def()
* Signature: $module_def = $dmd->module_def($module);
* Param: $module string
* Return: $module_def {}
* Throws: <none>
* Since: 0.01
Sample Usage:
$module_def = $dmd->module_def($module);
print "$module ", join(",", %$module_def), "\n";
=cut
sub module_def {
my ($self, $module) = @_;
$self->_scan_site() if (!$self->{site_scanned});
return({}) if (!$module || !defined $self->{module}{$module});
my ($pmfile, $podfile);
$pmfile = $self->{module}{$module}{pmfile};
if ($pmfile && !$self->{file}{$pmfile}{scanned}) {
$self->_scan_file($pmfile);
$self->{file}{$pmfile}{scanned} = 1;
}
$podfile = $self->{module}{$module}{podfile};
if ($podfile && !$self->{file}{$podfile}{scanned}) {
$self->_scan_file($podfile);
$self->{file}{$podfile}{scanned} = 1;
}
return ($self->{module}{$module});
}
#############################################################################
# write_module_ext_pod()
#############################################################################
=head2 write_module_ext_pod()
* Signature: $dmd->write_module_ext_pod($module, $filename);
* Param: $module string
* Param: $filename string
* Return: void
* Throws: <none>
* Since: 0.01
Sample Usage:
$dmd->write_module_ext_pod($module, "$module.pod");
=cut
sub write_module_ext_pod {
my ($self, $module, $dstfile) = @_;
$self->write_module_pod($module,$dstfile);
}
#############################################################################
# write_module_pod()
#############################################################################
=head2 write_module_pod()
* Signature: $dmd->write_module_pod($module, $filename);
* Param: $module string
* Param: $filename string
* Return: void
* Throws: <none>
* Since: 0.01
Sample Usage:
$dmd->write_module_pod($module, "$module.pod");
=cut
sub write_module_pod {
my ($self, $module, $dstfile) = @_;
$self->_scan_site() if (!$self->{site_scanned});
my ($srcfile, $module_def, $src_age, $dst_age, @stat);
$srcfile = $self->{module}{$module}{podfile};
$srcfile = $self->{module}{$module}{pmfile} if (!$srcfile);
if (!$srcfile) {
$module_def = $self->module_def($module);
$srcfile = $module_def->{podfile};
$srcfile = $module_def->{pmfile} if (!$srcfile);
}
if ($srcfile && -f $srcfile) {
if (-f $dstfile) {
@stat = stat($srcfile);
$src_age = $stat[9];
@stat = stat($dstfile);
$dst_age = $stat[9];
}
if (! -f $dstfile || $dst_age < $src_age) {
system("cp -f $srcfile $dstfile");
printf "Module %-60s - pod written\n", $module;
#printf " [$src_age] $srcfile\n";
#printf " [$dst_age] $dstfile\n";
}
else {
#printf "Module %-60s - pod current\n", $module;
}
}
else {
printf "Module %-60s - no source file found\n", $module;
}
#local(*main::FILE);
#if (open(main::FILE, "> $dstfile")) {
# close(main::FILE);
#}
#else {
# print "Unable to open file [$dstfile]: $!\n";
#}
}
#############################################################################
# PROTECTED METHODS
#############################################################################
=head1 Protected Methods:
The following methods are intended to be called only by this class and
other classes intimately related to this class.
=cut
#############################################################################
# get()
#############################################################################
=head2 get()
* Signature: $property_value = $dmd->get($property_name);
* Param: $property_name string
* Return: $property_value string
* Throws: <none>
* Since: 0.01
Sample Usage:
$dbi = $dmd->get("module.Devel::Metadata.dbi");
$dbuser = $dmd->get("module.Devel::Metadata.dbuser");
$dbpass = $dmd->get("module.Devel::Metadata.dbpass");
=cut
sub get {
print "get(@_)\n" if ($Devel::Metadata::DEBUG);
my ($self, $property_name, $dmd) = @_;
$dmd = $self if (!defined $dmd);
if ($property_name =~ /^(.*)([\.\{\[])([^\.\[\]\{\}]+)([\]\}]?)$/) {
my ($branch_name, $attrib, $type, $branch);
$branch_name = $1;
$type = $2;
$attrib = $3;
$branch = ref($dmd) eq "ARRAY" ? undef : $dmd->{_branch}{$branch_name};
$branch = $self->get_branch($1,0,$dmd) if (!defined $branch);
return undef if (!defined $branch || ref($branch) eq "");
return $branch->[$attrib] if (ref($branch) eq "ARRAY");
return $branch->{$attrib};
}
else {
return $self->{$property_name};
}
}
#############################################################################
# get_branch()
#############################################################################
=head2 get_branch()
* Signature: $branch = $dmd->get_branch($branch_name);
* Param: $branch_name string
* Return: $branch {}
* Throws: <none>
* Since: 0.01
Sample Usage:
$branch_name = "module.Devel::Metadata";
$branch = $dmd->get_branch($branch_name);
foreach $key (keys %$branch) {
$property = "${branch_name}.${key}";
print $property, "=", $branch->{$key}, "\n";
}
$dbi = $branch->{dbi};
$dbuser = $branch->{dbuser};
$dbpass = $branch->{dbpass};
=cut
sub get_branch {
print "get_branch(@_)\n" if ($Devel::Metadata::DEBUG);
my ($self, $branch_name, $create, $dmd) = @_;
my ($sub_branch_name, $branch_piece, $attrib, $type, $branch, $cache_ok);
$dmd = $self if (!defined $dmd);
# check the cache quickly and return the branch if found
$cache_ok = (ref($dmd) ne "ARRAY" && $dmd eq $self); # only cache from $self
$branch = $dmd->{_branch}{$branch_name} if ($cache_ok);
return ($branch) if (defined $branch);
# not found, so we need to parse the $branch_name and walk the $dmd tree
$branch = $dmd;
$sub_branch_name = "";
# these: "{field1}" "[3]" "field2." are all valid branch pieces
while ($branch_name =~ s/^([\{\[]?)([^\.\[\]\{\}]+)([\.\]\}]?)//) {
$branch_piece = $2;
$type = $3;
$sub_branch_name .= ($3 eq ".") ? "$1$2" : "$1$2$3";
if (ref($branch) eq "ARRAY") {
if (! defined $branch->[$branch_piece]) {
if ($create) {
$branch->[$branch_piece] = ($type eq "]") ? [] : {};
$branch = $branch->[$branch_piece];
$dmd->{_branch}{$sub_branch_name} = $branch if ($cache_ok);
}
else {
return(undef);
}
}
else {
$branch = $branch->[$branch_piece];
$sub_branch_name .= "$1$2$3"; # accumulate the $sub_branch_name
}
}
else {
if (! defined $branch->{$branch_piece}) {
if ($create) {
$branch->{$branch_piece} = ($type eq "]") ? [] : {};
$branch = $branch->{$branch_piece};
$dmd->{_branch}{$sub_branch_name} = $branch if ($cache_ok);
}
else {
return(undef);
}
}
else {
$branch = $branch->{$branch_piece};
}
}
$sub_branch_name .= $type if ($type eq ".");
}
return $branch;
}
#############################################################################
# set()
#############################################################################
=head2 set()
* Signature: $dmd->get($property_name, $property_value);
* Param: $property_name string
* Param: $property_value string
* Throws: <none>
* Since: 0.01
Sample Usage:
$dbi = $dmd->get("module.Devel::Metadata.dbi");
$dbuser = $dmd->get("module{Devel::Metadata}{dbuser}");
$dbpass = $dmd->get("module.Devel::Metadata{dbpass}");
=cut
sub set {
print "set(@_)\n" if ($Devel::Metadata::DEBUG);
my ($self, $property_name, $property_value, $dmd) = @_;
$dmd = $self if (!defined $dmd);
my ($branch_name, $attrib, $type, $branch, $cache_ok);
if ($property_name =~ /^(.*)([\.\{\[])([^\.\[\]\{\}]+)([\]\}]?)$/) {
$branch_name = $1;
$type = $2;
$attrib = $3;
$cache_ok = (ref($dmd) ne "ARRAY" && $dmd eq $self);
$branch = $dmd->{_branch}{$branch_name} if ($cache_ok);
$branch = $self->get_branch($1,1,$dmd) if (!defined $branch);
}
else {
$branch = $dmd;
$attrib = $property_name;
}
if (ref($branch) eq "ARRAY") {
$branch->[$attrib] = $property_value;
}
else {
$branch->{$attrib} = $property_value;
}
}
#############################################################################
# dump()
#############################################################################
=head2 dump()
* Signature: $perl = $dmd->dump();
* Param: void
* Return: $perl text
* Throws: <none>
* Since: 0.01
Sample Usage:
$dmd = $context->config();
print $dmd->dump(), "\n";
=cut
use Data::Dumper;
sub dump {
my ($self) = @_;
my $d = Data::Dumper->new([ $self ], [ "conf" ]);
$d->Indent(1);
return $d->Dump();
}
#############################################################################
# print()
#############################################################################
=head2 print()
* Signature: $dmd->print();
* Param: void
* Return: void
* Throws: <none>
* Since: 0.01
Sample Usage:
$context->print();
=cut
sub print {
my $self = shift;
print $self->dump();
}
#############################################################################
# PRIVATE METHODS
#############################################################################
=head1 Private Methods:
The following methods are intended to be called only within this class.
=cut
#############################################################################
# _scan_site()
#############################################################################
=head2 _scan_site()
* Signature: $dmd->_scan_site();
* Param: void
* Return: void
* Throws: <none>
* Since: 0.01
Sample Usage:
$dmd->_scan_site();
=cut
sub _scan_site {
my ($self) = @_;
my (@inc, $inc, $dist, $dist_def, $distfile_def, $module, $file);
@inc = @INC;
if ($ENV{PATH} =~ m/(.*)/) {
$ENV{PATH} = $1;
}
foreach $inc (@INC) {
next if (! -d $inc);
# TODO: replace this with the Perl version of "find" someday
my (@distfiles, $distfile, $tmpdir);
open (FIND, "find $inc -name '.packlist' -print |") || die "Unable to get
file list: $!\n";
@distfiles = <FIND>;
close(FIND);
chomp(@distfiles);
# Search through each distribution file
foreach $distfile (@distfiles) {
next if (defined $self->{distfile}{$distfile});
$distfile_def = {};
$self->{distfile}{$distfile} = $distfile_def;
$dist = $self->distfile2dist($distfile);
next if (!$dist); # not a distribution file!
next if (defined $self->{distribution}{$dist});
$dist_def = {};
$self->{distribution}{$dist} = $dist_def;
$dist_def->{files} = [];
$dist_def->{modules} = [];
foreach $file (split(/\n/,$self->_read_file($distfile))) {
if ($file =~ /\.pm$/) {
next if ($file =~ m!$inc/site_perl!);
next if ($file =~ m!$inc.*/5\.[0-9\.]{3,}/!);
next if ($file =~ m!$inc/[a-z][^/]+-[^/]+/!);
next if ($file =~ m!$inc/\.perldocs!);
$module = $self->file2module($file, $inc);
next if ($module !~
/^([A-Z][A-Za-z0-9_]*::)*[A-Za-z][A-Za-z0-9_]*$/);
if (! defined $self->{module}{$module}{pmfile}) {
$self->{module}{$module}{pmfile} = $file;
}
if (! defined $self->{module}{$module}{distribution}) {
$self->{module}{$module}{distribution} = $dist;
}
if (!$dist_def->{module}{$module}) {
$dist_def->{module}{$module} = 1;
push(@{$dist_def->{modules}}, $module);
}
if (!$self->{distribution}{all}{module}{$module}) {
$self->{distribution}{all}{module}{$module} = 1;
push(@{$self->{distribution}{all}{modules}}, $module);
}
if (!$dist_def->{file}{$file}) {
$dist_def->{file}{$file} = 1;
push(@{$dist_def->{files}}, $file);
$self->{file}{$file}{module} = $module;
}
if (!$self->{distribution}{all}{pmfile}{$file}) {
$self->{distribution}{all}{pmfile}{$file} = 1;
push(@{$self->{distribution}{all}{files}}, $file);
}
}
elsif ($file =~ /\.pod$/) {
}
elsif ($file =~ /\.al$/) {
}
elsif ($file =~ /\/man[1-9]\//) {
}
}
}
# replace this with the Perl version of "find" someday
my (@podfiles, $podfile);
open (FIND, "find $inc -name '*.pod' -print |") || die "Unable to get file
list: $!\n";
@podfiles = <FIND>;
close(FIND);
chomp(@podfiles);
# Search through each POD file
foreach $podfile (@podfiles) {
next if ($podfile =~ m!$inc/site_perl!);
next if ($podfile =~ m!$inc.*/5\.[0-9\.]{3,}/!);
next if ($podfile =~ m!$inc/[a-z][^/]+-[^/]+/!);
next if ($podfile =~ m!$inc/\.perldocs!);
$module = $self->file2module($podfile, $inc);
next if ($module !~ /^(?:[A-Z][A-Za-z0-9_]*::)*[A-Za-z][A-Za-z0-9_]*$/x);
if (! defined $self->{file}{$podfile}) {
$self->{file}{$podfile} = {};
}
if (! defined $self->{module}{$module}{podfile}) {
$self->{module}{$module}{podfile} = $podfile;
}
if (! defined $self->{module}{$module}{distribution}) {
$self->{module}{$module}{distribution} = "unknown";
}
if (!$self->{distribution}{all}{module}{$module}) {
$self->{distribution}{all}{module}{$module} = 1;
push(@{$self->{distribution}{all}{modules}}, $module);
$self->{distribution}{unknown}{module}{$module} = 1;
push(@{$self->{distribution}{unknown}{modules}}, $module);
}
if (!$self->{distribution}{all}{file}{$podfile}) {
$self->{distribution}{all}{file}{$podfile} = 1;
push(@{$self->{distribution}{all}{files}}, $podfile);
$self->{distribution}{unknown}{file}{$podfile} = 1;
push(@{$self->{distribution}{unknown}{files}}, $podfile);
}
}
# replace this with the Perl version of "find" someday
my (@pmfiles, $pmfile);
open (FIND, "find $inc -name '*.pm' -print |") || die "Unable to get file
list: $!\n";
@pmfiles = <FIND>;
close(FIND);
chomp(@pmfiles);
# Search through each PM file
foreach $pmfile (@pmfiles) {
next if ($pmfile =~ m!$inc/site_perl!);
next if ($pmfile =~ m!$inc.*/5\.[0-9\.]{3,}/!);
next if ($pmfile =~ m!$inc/[a-z][^/]+-[^/]+/!);
next if ($pmfile =~ m!$inc/\.perldocs!);
$module = $self->file2module($pmfile, $inc);
next if ($module !~ /^(?:[A-Z][A-Za-z0-9_]*::)*[A-Za-z][A-Za-z0-9_]*$/x);
if (! defined $self->{file}{$pmfile}) {
$self->{file}{$pmfile} = {};
}
if (! defined $self->{module}{$module}{pmfile}) {
$self->{module}{$module}{pmfile} = $pmfile;
}
if (! defined $self->{module}{$module}{distribution}) {
$self->{module}{$module}{distribution} = "unknown";
}
if (!$self->{distribution}{all}{module}{$module}) {
$self->{distribution}{all}{module}{$module} = 1;
push(@{$self->{distribution}{all}{modules}}, $module);
$self->{distribution}{unknown}{module}{$module} = 1;
push(@{$self->{distribution}{unknown}{modules}}, $module);
}
if (!$self->{distribution}{all}{file}{$pmfile}) {
$self->{distribution}{all}{file}{$pmfile} = 1;
push(@{$self->{distribution}{all}{files}}, $pmfile);
$self->{distribution}{unknown}{file}{$pmfile} = 1;
push(@{$self->{distribution}{unknown}{files}}, $pmfile);
}
}
}
$self->{site_scanned} = 1;
}
#############################################################################
# _scan_file()
#############################################################################
=head2 _scan_file()
* Signature: $dmd->_scan_file($file);
* Signature: $dmd->_scan_file($file, $module);
* Param: $file string
* Param: $module string
* Return: void
* Throws: <none>
* Since: 0.01
Sample Usage:
$dmd->_scan_file($file);
=cut
sub _scan_file {
my ($self, $file, $module) = @_;
#print "_scan_file($file, $module): entry\n";
$module = $self->file2module($file) if (!$module);
my ($source, @source);
@source = $self->_read_file($file);
$source = \@source;
$self->_scan_source_for_packages($file, $module, $source);
$self->_scan_source_for_isa($file, $module, $source);
#$self->_scan_source_for_subs($file, $module, $source);
#$self->_scan_source_for_pod($file, $module, $source);
$self->{file}{$file}{scanned} = 1;
#print "_scan_file(): exit\n";
}
sub _scan_source_for_packages {
my ($self, $file, $module, $source) = @_;
my ($lineidx, $lineno, $line, $package, @packages, %package_lines, $inpod);
$package = "main";
@packages = ();
%package_lines = ();
$inpod = 0;
for ($lineidx = 0; $lineidx <= $#$source; $lineidx++) {
$line = $source->[$lineidx];
$lineno = $lineidx + 1;
if ($inpod) {
if ($line =~ /^=cut/) {
$inpod = 0;
}
next;
}
else {
if ($line =~ /^=/) {
$inpod = 1;
next;
}
}
last if ($line =~ /^__/); # i.e. __END__ or __DATA__
if ($line =~ /^ *package +([A-Za-z0-9:_]+)/) {
$package = $1;
push(@packages, $package) if (!$package_lines{$package});
}
$package_lines{$package}++ if ($line !~ /^\s*$/ && $line !~ /^\s*#/);
}
if ($#packages == -1) {
@packages = ("main");
}
$self->{module}{$module}{packages} = \@packages;
$self->{module}{$module}{package_lines} = \%package_lines;
}
sub _scan_source_for_xyz { # standard template
my ($self, $file, $module, $source) = @_;
my ($lineidx, $lineno, $line, $package, $inpod);
$package = "main";
$inpod = 0;
for ($lineidx = 0; $lineidx <= $#$source; $lineidx++) {
$line = $source->[$lineidx];
$lineno = $lineidx + 1;
if ($inpod) {
if ($line =~ /^=cut/) {
$inpod = 0;
}
next;
}
else {
if ($line =~ /^=/) {
$inpod = 1;
next;
}
}
last if ($line =~ /^__/); # i.e. __END__ or __DATA__
if ($line =~ /^ *package +([A-Za-z0-9:_]+)/) {
$package = $1;
}
}
}
sub _scan_source_for_isa {
my ($self, $file, $module, $source) = @_;
my ($lineidx, $lineno, $line, $package, $inpod);
my ($parents, $parent, @parents, $isa_package);
$package = "main";
$inpod = 0;
for ($lineidx = 0; $lineidx <= $#$source; $lineidx++) {
$line = $source->[$lineidx];
$lineno = $lineidx + 1;
if ($inpod) {
if ($line =~ /^=cut/) {
$inpod = 0;
}
next;
}
else {
if ($line =~ /^=/) {
$inpod = 1;
next;
}
}
last if ($line =~ /^__/); # i.e. __END__ or __DATA__
if ($line =~ /^ *package +([A-Za-z0-9:_]+)/) {
$package = $1;
}
if ($line =~ /\bISA\b/) { # should be able to get inheritance here
$isa_package = $package;
if ($line =~ /([A-Za-z_:]+)::ISA/) {
$isa_package = $1;
}
if ($line =~ /\bISA *= *([^;]+)/) {
$parents = $1;
@parents = ();
if ($parents =~ /^qw *\(([^\)]+)\)/) {
$parents = $1;
$parents =~ s/^ +//;
$parents =~ s/ +$//;
foreach $parent (split(/ +/, $parents)) {
push(@parents, $parent) if ($parent =~ /^[A-Za-z0-9:_]+$/);
}
}
elsif ($parents =~ /^\(([^\)]+)\)/) {
$parents = $1;
$parents =~ s/^[ '"]+//;
$parents =~ s/[ '",]+$//;
$parents =~ s/[ '",]+/ /g;
foreach $parent (split(/ +/, $parents)) {
push(@parents, $parent) if ($parent =~ /^[A-Za-z0-9:_]+$/);
}
}
elsif ($parents =~ /^["']([A-Za-z0-9_:]+)["']/) {
$parents = $1;
@parents = ( $parents );
}
if ($#parents > -1) {
$self->{package}{$isa_package}{parent} = $parents[0];
$self->{package}{$isa_package}{parents} = \@parents;
foreach my $parent (@parents) {
if (! defined $self->{package}{$parent}{children}) {
$self->{package}{$parent}{children} = []
}
push(@{$self->{package}{$parent}{children}}, $isa_package);
}
}
}
}
}
}
sub _scan_source_for_subs {
my ($self, $file, $module, $source) = @_;
}
sub _scan_source_for_pod {
my ($self, $file, $module, $source) = @_;
}
sub file2shortfile {
my ($self, $shortfile, $incdir) = @_;
$shortfile =~ s!^$incdir!! if ($incdir); # remove leading dir known not to be
part of module name
$shortfile =~ s!site_perl/!!; # remove site_perl
$shortfile =~ s!.*/5\.[0-9\.]{3,}/!!; # remove perl version
$shortfile =~ s!^/!!; # remove leading "/"
$shortfile =~ s!^[a-z][^/]+-[^/]+/!!; # remove architecture string
return $shortfile;
}
sub file2module {
my ($self, $module, $incdir) = @_;
$module =~ s!^$incdir!! if ($incdir); # remove leading dir known not to be part
of module name
$module =~ s!site_perl/!!; # remove site_perl
$module =~ s!.*/5\.[0-9\.]{3,}/!!; # remove perl version
$module =~ s!^/!!; # remove leading "/"
$module =~ s!^[a-z][^/]+-[^/]+/!!; # remove architecture string
$module =~ s!^(?:[a-z]+/)+!!x; # remove "lib", "blib", "pod", etc.
$module =~ s!\.pm$!!; # remove .pm suffix
$module =~ s!\.pod$!!; # remove .pod suffix
$module =~ s!/!::!g; # change dir separators to "::"
return $module;
}
sub distfile2dist {
my ($self, $dist, $incdir) = @_;
return "" if ($dist !~ m!/auto/!); # .packlist files should always have
"/auto/" in the path
$dist =~ s!.*/auto/!!;
$dist =~ s!/\.packlist$!!;
$dist =~ s!/!-!g;
return $dist;
}
sub _read_file {
my ($self, $file) = @_;
local(*FILE);
my ($data, @data);
#print "Reading [$file] ...\n";
if (open(FILE, "< $file")) {
@data = <FILE>;
close(FILE);
return (@data) if (wantarray);
$data = join("", @data);
return ($data);
}
else {
print "Failed to open file for reading [$file]: $!\n";
}
return("");
}
sub _write_file {
my ($self, $file, $data) = @_;
local(*FILE);
if (open(FILE, "> $file")) {
print FILE $data;
close(FILE);
#print "Writing [$file] ...\n";
}
else {
print "Failed to open file for writing [$file]: $!\n";
}
}
=head1 ACKNOWLEDGEMENTS
* Author: Stephen Adkins <[EMAIL PROTECTED]>
* License: This is free software. It is licensed under the same terms as Perl
itself.
=head1 SEE ALSO
none
=cut
1;
__END__
my (@package_frags, $pf, $package, $pfrag);
my (@head_frags, $hf, $headlevel, $headtext, $hfrag);
my (@sub_frags, $sf, $sub, $sfrag);
my ($dist_name, $class_list, $class_group, $class, $description, $capability,
$method);
# search each package
@package_frags = split(/(\npackage .*\n)/, $source);
unshift(@package_frags, "package $module;");
for ($pf = 0; $pf <= $#package_frags; $pf += 2) {
if ($package_frags[$pf] =~ /^ *package +([A-Za-z0-9:_]+);/) {
$package = $1;
&docwarn("mfiup", $file, $package) if ($module ne $package);
$pfrag = $package_frags[$pf+1];
#print "package=[$package] pfrag=[$pfrag]\n";
# save info about where this class (package) was detected
$metadata->{classgroup}{all}{classes} = []
if (! defined $metadata->{classgroup}{all}{classes});
if (! defined $metadata->{classgroup}{all}{class}{$package}) {
push(@{$metadata->{classgroup}{all}{classes}}, $package);
$metadata->{classgroup}{all}{class}{$package} = {};
}
# take not of the module that POD for the package is in
if ($pfrag =~ /\n=head1 /s) {
$metadata->{class}{$package}{podclass} = $module;
}
# Scan the ISA list to get the inheritance tree
if ($pfrag =~ /\@ISA *= *([^;#]*)/) {
$class_list = $1;
while ($class_list =~ /([A-Z][A-Za-z0-9_:]+)/g) {
$class = $1;
$metadata->{class}{$package}{parent} = $class
if (!defined $metadata->{class}{$package}{parent});
if (!defined $metadata->{class}{$package}{parents}) {
$metadata->{class}{$package}{parents} = []
}
else {
push(@{$metadata->{class}{$package}{parents}}, $class);
}
$metadata->{class}{$class}{children} = []
if (!defined $metadata->{class}{$class}{children});
push(@{$metadata->{class}{$class}{children}}, $package);
}
}
# search each =head POD directive
@head_frags = split(/(\n=head[12] .*\n)/, $pfrag);
for ($hf = 1; $hf <= $#head_frags; $hf += 2) {
if ($head_frags[$hf] =~ /=head([12]) +(.*)/) {
$headlevel = $1;
$headtext = $2;
$hfrag = $head_frags[$hf+1];
if ($headtext =~ /NAME/) {
}
elsif ($headtext =~ /SYNOPSIS/) {
}
elsif ($headtext =~ /DESCRIPTION/) {
}
elsif ($headtext =~ /ACKNOWLEDGEMENTS/) {
}
elsif ($headtext =~ /SEE ALSO/) {
}
elsif ($headtext =~ /Attributes, Constants, Global Variables,
Class Variables/) {
}
elsif ($headtext =~ /Distribution: *(.*)/) {
$dist_name = $1;
}
elsif ($headtext =~ /Class Groups/) {
while ($hfrag =~ / \* Class Group: *([^-\n]+)( *\n *-
*([^\n]+))?/sg) {
$class_group = $1;
$description = $3;
$class_group =~ s/[\|>].*//;
$class_group =~ s/.*<//;
$metadata->{classgroup}{$class_group}{description} =
$description;
}
}
elsif ($headtext =~ /Class Group Requirements/) {
}
elsif ($headtext =~ /Class Group Design/) {
}
elsif ($headtext =~ /Class Group: *(.*)/) {
$class_group = $1;
$class_group =~ s/[\|>].*//;
$class_group =~ s/.*<//;
$metadata->{classgroup}{$class_group}{podclass} = $module;
while ($hfrag =~ / \* (Class|Document): *([^\n]+)/sg) {
$class = $2;
$class =~ s/[\|>].*//;
$class =~ s/.*<//;
$metadata->{classgroup}{$class_group}{classes} = []
if (! defined
$metadata->{classgroup}{$class_group}{classes});
if (! defined
$metadata->{classgroup}{$class_group}{class}{$class}) {
push(@{$metadata->{classgroup}{$class_group}{classes}}, $class);
$metadata->{classgroup}{$class_group}{class}{$class}
= {};
}
$metadata->{classgroup}{all}{classes} = []
if (! defined $metadata->{classgroup}{all}{classes});
if (! defined
$metadata->{classgroup}{all}{class}{$class}) {
push(@{$metadata->{classgroup}{all}{classes}},
$class);
$metadata->{classgroup}{all}{class}{$class} = {};
}
}
}
elsif ($headtext =~ /Class: *(.*)/) {
$class = $1;
}
elsif ($headtext =~ /Class Capabilities/) {
}
elsif ($headtext =~ /Class Requirements/) {
}
elsif ($headtext =~ /Class Design/) {
}
elsif ($headtext =~ /Constructor Methods: *(.*)/) {
$capability = $1;
}
elsif ($headtext =~ /Public Methods: *(.*)/) {
$capability = $1;
}
elsif ($headtext =~ /Public Methods: *(.*)/) {
$capability = $1;
}
elsif ($headtext =~ /Public Methods: *(.*)/) {
$capability = $1;
}
elsif ($headtext =~ / *(.+)\(\)/) {
$method = $1;
&docwarn("mdnh2", $file, $method) if ($headlevel != 2);
$hfrag =~ s/\nsub .*//s;
$metadata->{class}{$package}{method}{$method}{doc} = $hfrag;
}
else {
}
#print "headlevel=[$headlevel] headtext=[$headtext]
hfrag=[$hfrag]\n";
}
}
# search each subroutine/method definition
@sub_frags = split(/(\nsub .*\n)/, $source);
for ($sf = 1; $sf <= $#sub_frags; $sf += 2) {
if ($sub_frags[$sf] =~ /sub +([A-Za-z0-9_]+)/) {
$method = $1;
$sfrag = $sub_frags[$sf+1]; # { {
$sfrag =~ s/\n}.*/}/s;
$metadata->{class}{$package}{method}{$method}{code} = $sfrag;
if (! defined
$metadata->{class}{$package}{method}{$method}{doc}) {
&docwarn("mwnd", $file, $method);
}
#print "method=[$method] sfrag=[$sfrag]\n";
}
}
}
}
1.1 p5ee/Devel-Metadata/t/01.reference.t
Index: 01.reference.t
===================================================================
#!/usr/local/bin/perl -wT
use Test::More qw(no_plan);
use lib "./lib";
use lib "../lib";
BEGIN {
use_ok("Devel::Metadata");
}
use strict;
#$Devel::Metadata::DEBUG = 0;
my ($dmd, $branch);
$dmd = Devel::Metadata->new();
ok(defined $dmd, "constructor ok");
isa_ok($dmd, "Devel::Metadata", "right class");
$dmd->set("x.y.z.pi", 3.1416);
is($dmd->get("x.y.z.pi"), 3.1416, "get x.y.z.pi");
$branch = $dmd->get_branch("x.y.z");
is($branch->{pi}, 3.1416, "get_branch()");
$branch = $dmd->get_branch("zeta.alpha");
ok(! defined $branch, "non-existent branch");
$branch = $dmd->get_branch("zeta.alpha", 1);
ok(defined $branch, "newly existent branch");
exit 0;
1.1 p5ee/Devel-Metadata/t/02.distributions.t
Index: 02.distributions.t
===================================================================
#!/usr/local/bin/perl -w
use Test::More qw(no_plan);
use lib "./lib";
use lib "../lib";
BEGIN {
use_ok("Devel::Metadata");
}
my ($dmd, @distributions);
$dmd = Devel::Metadata->new();
@distributions = $dmd->distributions();
print join("\n", sort @distributions), "\n";
exit 0;
1.1 p5ee/Devel-Metadata/t/03.modules.t
Index: 03.modules.t
===================================================================
#!/usr/local/bin/perl -w
use Test::More qw(no_plan);
use lib "./lib";
use lib "../lib";
BEGIN {
use_ok("Devel::Metadata");
}
use strict;
my ($dmd, @modules, $module, $module_def, $value);
$dmd = Devel::Metadata->new();
@modules = $dmd->modules();
#print "[all]\n", join("\n", sort @modules), "\n";
foreach $module (@modules) {
$module_def = $dmd->module_def($module);
print "$module";
foreach (sort keys %$module_def) {
$value = $module_def->{$_};
next if (!defined $value);
if (ref($value) eq "ARRAY") {
print " $_=[", join(",",@$value), "]";
}
elsif (ref($value) eq "HASH") {
print " $_={", join(",",%$value), "}";
}
else {
print " $_=$value";
}
}
print "\n";
}
@modules = $dmd->modules("CPAN");
print "[CPAN]\n", join("\n", sort @modules), "\n";
exit 0;
1.1 p5ee/Devel-Metadata/t/04.module.t
Index: 04.module.t
===================================================================
#!/usr/local/bin/perl -w
use Test::More qw(no_plan);
use lib "./lib";
use lib "../lib";
BEGIN {
use_ok("Devel::Metadata");
}
#use strict;
my ($dmd, @distributions);
$dmd = Devel::Metadata->new();
@modules = $dmd->modules();
print "[all]\n", join("\n", sort @modules), "\n";
@modules = $dmd->modules("CPAN");
print "[CPAN]\n", join("\n", sort @modules), "\n";
exit 0;