NAME

Test::CPAN::Health::Distribution - Represents a CPAN distribution under analysis

SYNOPSIS

use Test::CPAN::Health::Distribution;

# From a local unpacked path
my $dist = Test::CPAN::Health::Distribution->new(path => '/tmp/LWP-UserAgent-6.77');

# From a CPAN distribution name (downloads and unpacks)
my $dist = Test::CPAN::Health::Distribution->from_cpan('LWP-UserAgent');

# From a module name (resolves dist via MetaCPAN, then downloads)
my $dist = Test::CPAN::Health::Distribution->from_module('LWP::UserAgent');

printf "%s %s by %s\n", $dist->name, $dist->version, $dist->author;
my @pm_files = @{ $dist->pm_files };

DESCRIPTION

Abstracts the distribution being analysed. All checks receive a Distribution object, which provides uniform access to the filesystem, metadata, and derived file lists regardless of whether the distribution came from a local path or was downloaded from CPAN.

All file-list methods return absolute paths.

LIMITATIONS

  • from_cpan and from_module require network access and Archive::Tar to unpack tarballs. The download is cached in a temporary directory that is cleaned up at object destruction.

  • meta searches in order: META.json, META.yml, MYMETA.json, MYMETA.yml. MYMETA files are generated by perl Makefile.PL and serve as a fallback for local checkouts. Canonical META files are produced by make distdir (MakeMaker) or dzil build (Dist::Zilla) and must be committed separately. Returns undef only when none of these files are present.

  • file_path does not sanitise @parts for path-traversal sequences (e.g. ../). It is the caller's responsibility to supply trusted path components. Do not pass user-supplied strings to file_path without prior validation.

  • all_source_files returns the union of pm_files and pl_files at the time of the first call; the result is cached. Files added to the distribution tree afterwards are not visible.

from_cpan

PURPOSE

Factory method: resolve a CPAN distribution name to the latest release, download the tarball via MetaCPAN, unpack it to a temp directory, and return a Distribution instance pointing at the unpacked tree.

API SPECIFICATION

INPUT

dist_name  Scalar  required  e.g. 'LWP-UserAgent'

OUTPUT

Test::CPAN::Health::Distribution object.

MESSAGES

Code  | Severity | Message                            | Resolution
------+----------+------------------------------------+---------------------
DST01 | FATAL    | Cannot resolve dist from MetaCPAN  | Check dist name and network
DST02 | FATAL    | Download failed                    | Check CPAN mirror availability
DST03 | FATAL    | Unpack failed                      | Check Archive::Tar installation

FORMAL SPECIFICATION

-- Z schema (placeholder) --
FromCpanOp
dist_name? : String
dist!      : Distribution
-------------------------------------------------------
dist_name? /= ""
dist!.name = resolve(dist_name?)
exists(dist!.path) /\ is_dir(dist!.path)

SIDE EFFECTS

Downloads a tarball from CPAN. Creates and owns a temporary directory that is removed when the returned object is destroyed.

USAGE EXAMPLE

my $dist = Test::CPAN::Health::Distribution->from_cpan('LWP-UserAgent');

from_module

PURPOSE

Factory method: resolve a module name (Foo::Bar) to its containing distribution via MetaCPAN, then delegate to from_cpan.

API SPECIFICATION

INPUT

module_name  Scalar  required  e.g. 'LWP::UserAgent'

OUTPUT

Test::CPAN::Health::Distribution object.

MESSAGES

Code  | Severity | Message                            | Resolution
------+----------+------------------------------------+---------------------
DST04 | FATAL    | Cannot resolve module from MetaCPAN | Check module name and network

FORMAL SPECIFICATION

-- Z schema (placeholder) --
FromModuleOp
module_name? : String
dist_name!   : String
-------------------------------------------------------
dist_name! = metacpan_resolve(module_name?)

SIDE EFFECTS

As from_cpan.

USAGE EXAMPLE

my $dist = Test::CPAN::Health::Distribution->from_module('LWP::UserAgent');

path

PURPOSE

Returns the absolute filesystem path to the root of the unpacked distribution.

API SPECIFICATION

INPUT

None.

OUTPUT

Absolute scalar path string.

MESSAGES

Code  | Severity | Message                            | Resolution
------+----------+------------------------------------+---------------------
      |          |                                    |

FORMAL SPECIFICATION

-- Z schema (placeholder) --
path : String
-------------------------------------------------------
is_absolute(path) /\ is_dir(path)

SIDE EFFECTS

None.

USAGE EXAMPLE

print $dist->path;

meta

PURPOSE

Returns a CPAN::Meta object parsed from the best available META file. The search order is: META.json, META.yml, MYMETA.json, MYMETA.yml. META files are authoritative (shipped with the dist); MYMETA files are generated by perl Makefile.PL and used as a fallback for local checkouts where perl Makefile.PL has been run but no canonical META file has been committed. Returns undef if no META file of any kind is found.

API SPECIFICATION

INPUT

None.

OUTPUT

CPAN::Meta object or undef.

MESSAGES

Code  | Severity | Message                              | Resolution
------+----------+--------------------------------------+---------------------
DST05 | WARNING  | Cannot parse <file>: <reason>        | Fix or regenerate the META file

FORMAL SPECIFICATION

-- Z schema (placeholder) --
meta : CPAN::Meta | undefined

SIDE EFFECTS

None (reads META file on first call, caches result).

USAGE EXAMPLE

my $meta = $dist->meta or warn "No META file";
print $meta->name;

name

Returns the distribution name from META (e.g. LWP-UserAgent), or a best-guess derived from the path basename if META is absent.

version

Returns the distribution version string from META, or undef if not determinable. The result is cached after the first call; a dist whose META lacks a version field always returns undef without re-parsing.

author

Returns the first author string from META, or undef. Cached after the first call.

pm_files

Returns an arrayref of absolute paths to all .pm files under lib/.

t_files

Returns an arrayref of absolute paths to all .t files under t/.

pl_files

Returns an arrayref of absolute paths to all .pl files and files under bin/ or script/.

all_source_files

Convenience: returns the union of pm_files and pl_files as a single arrayref. The result is cached after the first call.

has_dir

Returns true if the given subdirectory exists within the distribution root. Accepts a single directory name or a list of alternatives.

file_path

Returns the absolute path to a named file within the distribution root, or undef if it does not exist.

AUTHOR

Nigel Horne, <njh at nigelhorne.com>

LICENSE AND COPYRIGHT

Copyright (C) 2025-2026 Nigel Horne.

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.