App-lcpan
view release on metacpan or search on metacpan
lib/App/lcpan.pm view on Meta::CPAN
default => 'index.db', # will also be checked/set in _set_args_default
tags => ['common'],
description => <<'_',
If `index_name` is a filename without any path, e.g. `index.db` then index will
be located in the top-level of `cpan`. If `index_name` contains a path, e.g.
`./index.db` or `/home/ujang/lcpan.db` then the index will be located solely
using the `index_name`.
_
completion => sub {
my %args = @_;
my $word = $args{word} // '';
my $cmdline = $args{cmdline};
my $r = $args{r};
return undef unless $cmdline;
# force reading config file
$r->{read_config} = 1;
my $res = $cmdline->parse_argv($r);
my $args = $res->[2];
_set_args_default($args);
require Complete::File;
require Complete::Util;
Complete::Util::hashify_answer(
Complete::File::complete_file(
word => $word,
starting_path => $args->{cpan},
filter => sub {
# dir (to dig down deeper) or index.db*
(-d $_[0]) || $_[0] =~ /index\.db/;
},
),
{path_sep=>'/'},
);
},
},
use_bootstrap => {
summary => 'Whether to use bootstrap database from App-lcpan-Bootstrap',
schema => 'bool*',
default => 1, # will also be checked/set in _set_args_default
description => <<'_',
If you are indexing your private CPAN-like repository, you want to turn this
off.
_
tags => ['common'],
},
update_db_schema => {
summary => 'Whether to update database schema to the latest',
'summary.alt.bool.not' => 'Do not update database schema to the latest',
schema => 'bool*',
default => 1, # will also be checked/set in _set_args_default
description => <<'_',
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
_
tags => ['common'],
},
);
our %all_args = (
all => {
schema => 'bool',
cmdline_aliases => {a=>{}},
},
);
our %detail_args = (
detail => {
schema => 'bool',
cmdline_aliases => {l=>{}},
},
);
our %query_args = (
query => {
summary => 'Search query',
schema => 'str*',
cmdline_aliases => {q=>{}},
pos => 0,
tags => ['category:filtering'],
},
%detail_args,
);
our %query_multi_args = (
query => {
summary => 'Search query',
schema => ['array*', of=>'str*'],
cmdline_aliases => {q=>{}},
pos => 0,
slurpy => 1,
tags => ['category:filtering'],
},
detail => {
schema => 'bool',
cmdline_aliases => {l=>{}},
},
or => {
summary => 'When there are more than one query, perform OR instead of AND logic',
schema => ['bool', is=>1],
tags => ['category:filtering'],
},
);
lib/App/lcpan.pm view on Meta::CPAN
summary => 'Include core modules',
'summary.alt.bool.not' => 'Exclude core modules',
schema => 'bool',
default => 1,
tags => ['category:filtering'],
},
);
our %finclude_noncore_args = (
include_noncore => {
summary => 'Include non-core modules',
'summary.alt.bool.not' => 'Exclude non-core modules',
schema => 'bool',
default => 1,
tags => ['category:filtering'],
},
);
our %finclude_indexed_args = (
include_indexed => {
summary => 'Include modules that are indexed (listed in 02packages.details.txt.gz)',
'summary.alt.bool.not' => 'Exclude modules that are indexed (listed in 02packages.details.txt.gz)',
schema => 'bool',
default => 1,
tags => ['category:filtering'],
},
);
our %finclude_unindexed_args = (
include_unindexed => {
summary => 'Include modules that are not indexed (not listed in 02packages.details.txt.gz)',
'summary.alt.bool.not' => 'Exclude modules that are not indexed (not listed in 02packages.details.txt.gz)',
schema => 'bool',
default => 1,
cmdline_aliases => {
broken => {is_flag=>1, summary => 'Alias for --exclude-indexed --include-unindexed', code => sub { $_[0]{include_unindexed}=1; $_[0]{include_indexed}=0 }},
},
tags => ['category:filtering'],
},
);
our %fctime_args = (
added_since => {
summary => 'Include only records that are added since a certain date',
schema => ['date*', 'x.perl.coerce_rules' => ['From_str::natural']],
tags => ['category:filtering'],
},
added_since_last_index_update => {
summary => 'Include only records that are added since the last index update',
schema => 'true*',
tags => ['category:filtering'],
},
added_since_last_n_index_updates => {
summary => 'Include only records that are added since the last N index updates',
schema => 'posint*',
tags => ['category:filtering'],
},
);
our %fmtime_args = (
updated_since => {
summary => 'Include only records that are updated since certain date',
schema => ['date*', 'x.perl.coerce_rules' => ['From_str::natural']],
tags => ['category:filtering'],
},
updated_since_last_index_update => {
summary => 'Include only records that are updated since the last index update',
schema => 'true*',
tags => ['category:filtering'],
},
updated_since_last_n_index_updates => {
summary => 'Include only records that are updated since the last N index updates',
schema => 'posint*',
tags => ['category:filtering'],
},
);
our %fctime_or_mtime_args = (
added_or_updated_since => {
summary => 'Include only records that are added/updated since a certain date',
schema => ['date*', 'x.perl.coerce_rules' => ['From_str::natural']],
cmdline_aliases => {since=>{}},
tags => ['category:filtering'],
},
added_or_updated_since_last_index_update => {
summary => 'Include only records that are added/updated since the last index update',
schema => 'true*',
cmdline_aliases => {since_last_index_update=>{}},
tags => ['category:filtering'],
},
added_or_updated_since_last_n_index_updates => {
summary => 'Include only records that are added/updated since the last N index updates',
schema => 'posint*',
cmdline_aliases => {since_last_n_index_updates=>{}},
tags => ['category:filtering'],
},
);
our %perl_version_args = (
perl_version => {
summary => 'Set base Perl version for determining core modules',
schema => 'str*',
default => "$^V",
cmdline_aliases => {V=>{}},
},
);
our %random_args = (
random => {
summary => 'Random sort',
schema => 'true*',
tags => ['category:ordering'],
},
);
our %sort_args_for_authors = (
sort => {
summary => 'Sort the result',
schema => ['array*', of=>['str*', in=>[map {($_,"-$_")} qw/id name email rec_mtime/]]],
default => ['id'],
tags => ['category:ordering'],
},
%random_args,
);
our %sort_args_for_mods = (
sort => {
summary => 'Sort the result',
schema => ['array*', of=>['str*', in=>[map {($_,"-$_")} qw/module author rdeps rel_mtime/]]],
default => ['module'],
tags => ['category:ordering'],
},
%random_args,
);
our %sort_args_for_dists = (
sort => {
summary => 'Sort the result',
schema => ['array*', of=>['str*', in=>[map {($_,"-$_")} qw/dist author release rel_size rel_mtime abstract/]]],
default => ['dist'],
tags => ['category:ordering'],
},
%random_args,
);
# XXX should it be put in App/lcpan/Cmd/subs.pm?
our %sort_args_for_subs = (
sort => {
summary => 'Sort the result',
schema => ['array*', of=>['str*', in=>[map {($_,"-$_")} qw/sub package linum author/]]],
default => ['sub'],
tags => ['category:ordering'],
lib/App/lcpan.pm view on Meta::CPAN
slurpy => 1,
cmdline_src => 'stdin_or_args',
element_completion => \&_complete_dist,
},
);
our %rel_args = (
release => {
schema => 'str*', # XXX perl::relname
req => 1,
pos => 0,
completion => \&_complete_rel,
},
);
our %dist_or_rel_args = (
dist_or_release => {
schema => 'str*', # XXX [any, of=>[perl::relname, perl::distname]]
req => 1,
pos => 0,
completion => \&_complete_dist, # XXX dist/release
},
);
our %sort_args_for_rels = (
sort => {
schema => ['array*', of=>['str*', in=>[qw/author -author size -size name -name mtime -mtime/]]],
default => ['name'],
tags => ['category:sorting'],
},
%random_args,
);
our %overwrite_args = (
overwrite => {
summary => 'Whether to overwrite existing file',
schema => ['bool*', is=>1],
cmdline_aliases => {o=>{}},
},
);
$SPEC{':package'} = {
v => 1.1,
summary => 'Manage local CPAN mirror',
};
sub _set_args_default {
my $args = shift;
if (!$args->{cpan}) {
require File::HomeDir;
$args->{cpan} = File::HomeDir->my_home . '/cpan';
}
$args->{index_name} //= 'index.db';
if (!defined($args->{num_backups})) {
$args->{num_backups} = 7;
}
$args->{use_bootstrap} //= 1;
$args->{update_db_schema} //= 1;
}
# set {added_,updated_,added_or_udpated_}since from
# {added_,updated_,added_or_updated_}since_last_{index_update,n_index_updates},
# set, since SQL query will usually use the former
sub _set_since {
my ($args, $dbh) = @_;
my $num_sinces = 0;
if (defined $args->{added_since}) { $num_sinces++ }
if (defined $args->{updated_since}) { $num_sinces++ }
if (defined $args->{added_or_updated_since}) { $num_sinces++ }
if (defined $args->{added_since_last_index_update} || defined $args->{updated_since_last_index_update} || defined $args->{added_or_updated_since_last_index_update}) {
my ($time) = $dbh->selectrow_array("SELECT date FROM log WHERE category='update_index' AND summary LIKE 'Begin%' ORDER BY date DESC");
die "Index has not been updated at all, cannot use {added_,updated_,added_or_updated_}since_last_index_update option" unless $time;
if (delete $args->{added_since_last_index_update}) { $args->{added_since} //= $time; log_trace "Setting added_since=%s", $time; $num_sinces++ }
if (delete $args->{updated_since_last_index_update}) { $args->{updated_since} //= $time; log_trace "Setting updated_since=%s", $time; $num_sinces++ }
if (delete $args->{added_or_updated_since_last_index_update}) { $args->{added_or_updated_since} //= $time; log_trace "Setting added_or_updated_since=%s", $time; $num_sinces++ }
}
if (defined $args->{added_since_last_n_index_updates} || defined $args->{updated_since_last_n_index_updates} || defined $args->{added_or_updated_since_last_n_index_updates}) {
my $n = int($args->{added_since_last_n_index_updates} // $args->{updated_since_last_n_index_updates} // $args->{added_or_updated_since_last_n_index_updates});
$n = 1 if $n < 1;
my $sth = $dbh->prepare("SELECT date FROM log WHERE category='update_index' AND summary LIKE 'Begin%' ORDER BY date DESC");
$sth->execute;
my $i = 0;
my $time;
1 while ++$i <= $n && (($time) = $sth->fetchrow_array);
die "Index has not been updated that many times, please set a lower number for {,added_,updated_}since_last_n_index_updates option" if $i < $n;
if (delete $args->{added_since_last_n_index_updates}) { $args->{added_since} //= $time; log_trace "Setting added_since=%s", $time; $num_sinces++ }
if (delete $args->{updated_since_last_n_index_updates}) { $args->{updated_since} //= $time; log_trace "Setting updated_since=%s", $time; $num_sinces++ }
if (delete $args->{added_or_updated_since_last_n_index_updates}) { $args->{added_or_updated_since} //= $time; log_trace "Setting added_or_updated_since=%s", $time; $num_sinces++ }
}
die "Multiple {added_,updated_,added_or_updated_}since options set, please set only one to avoid confusion" if $num_sinces > 1;
}
sub _add_since_where_clause {
my ($args, $where, $table) = @_;
if (defined $args->{added_since} ) { push @$where, "$table.rec_ctime >= ". (0+$args->{added_since}) }
if (defined $args->{updated_since}) { push @$where, "($table.rec_mtime >= ". (0+$args->{updated_since}). " AND $table.rec_ctime < ".(0+$args->{updated_since}). ")" }
if (defined $args->{added_or_updated_since}) { push @$where, "($table.rec_ctime >= ". (0+$args->{added_or_updated_since}). " OR $table.rec_mtime >= ". (0+$args->{added_or_updated_since}). ")" }
}
sub _fmt_time {
require POSIX;
my $epoch = shift;
return '' unless defined($epoch);
POSIX::strftime("%Y-%m-%dT%H:%M:%SZ", gmtime($epoch));
}
sub _numify_ver {
my $v;
eval { $v = version->parse($_[0]) };
$v ? $v->numify : undef;
}
sub _dists_with_optional_vers2file_ids {
my ($dbh, $dists_with_optional_vers) = @_;
return [] unless $dists_with_optional_vers;
my $file_ids = [];
for my $dist_with_optional_ver (@$dists_with_optional_vers) {
my $dist = $dist_with_optional_ver;
my $file_id;
if ($dist =~ s/@(.+)$//) {
my $ver = $1;
($file_id) = $dbh->selectrow_array("SELECT id FROM file WHERE dist_name=? AND dist_version=?", {}, $dist, $ver);
do { warn "lcpan: No such dist '$dist' version '$ver'\n"; next } unless $file_id;
} else {
($file_id) = $dbh->selectrow_array("SELECT id FROM file WHERE dist_name=? AND is_latest_dist=1", {}, $dist);
do { warn "lcpan: No such dist '$dist'\n"; next } unless $file_id;
}
push @$file_ids, $file_id unless grep { $file_id == $_ } @$file_ids;
}
$file_ids;
}
sub _modules2file_ids {
my ($dbh, $modules) = @_;
return [] unless $modules && @$modules;
my $file_ids = [];
for my $module (@$modules) {
my ($file_id) = $dbh->selectrow_array("SELECT file_id FROM module WHERE name=?", {}, $module);
do { warn "lcpan: No such module '$module'\n"; next } unless $file_id;
push @$file_ids, $file_id unless grep { $file_id == $_ } @$file_ids;
}
$file_ids;
}
sub _dists2theirmods {
my ($dbh, $dists) = @_;
return [] unless $dists;
my $mods = [];
for my $dist (@$dists) {
my $sth = $dbh->prepare("SELECT name FROM module WHERE file_id IN (SELECT id FROM file WHERE dist_name=? AND is_latest_dist=1)");
$sth->execute($dist);
lib/App/lcpan.pm view on Meta::CPAN
my $path = "$cpan/modules/02packages.details.txt.gz";
log_info("Parsing %s ...", $path);
open my($fh), "<:gzip", $path or die "Can't open $path (<:gzip): $!";
my $sth_sel_file = $dbh->prepare("SELECT id FROM file WHERE name=? AND cpanid=?");
my $sth_ins_file = $dbh->prepare("INSERT INTO file (name,cpanid,mtime,size, rec_ctime,rec_mtime) VALUES (?,?,?,?, ?,?)");
my $sth_ins_mod = $dbh->prepare("INSERT INTO module (name,file_id,cpanid,version,version_numified, rec_ctime,rec_mtime) VALUES (?,?,?,?,?, ?,?)");
my $sth_upd_mod = $dbh->prepare("UPDATE module SET file_id=?,cpanid=?,version=?,version_numified=?, rec_mtime=? WHERE name=?"); # sqlite currently does not have upsert
$dbh->begin_work;
my %file_ids_in_table; # key="cpanid|filename"
my $sth = $dbh->prepare("SELECT cpanid,name,id FROM file");
$sth->execute;
while (my ($cpanid, $name, $id) = $sth->fetchrow_array) {
$file_ids_in_table{"$cpanid|$name"} = $id;
}
my %file_ids_in_02packages; # key="cpanid|filename", val=id (or undef if already exists in db)
my $line = 0;
while (<$fh>) {
$line++;
next unless /\S/;
next if /^\S+:\s/;
chomp;
my ($pkg, $ver, $path) = split /\s+/, $_;
$ver = undef if $ver eq 'undef';
my ($author, $file) = $path =~ m!^./../(.+?)/(.+)! or do {
log_warn(" line %d: Invalid path %s, skipped", $line, $path);
next;
};
my $file_id;
if (exists $file_ids_in_02packages{"$author|$file"}) {
$file_id = $file_ids_in_02packages{"$author|$file"};
} else {
$sth_sel_file->execute($file, $author);
my $path = _fullpath($file, $cpan, $author);
my @stat = stat $path;
unless ($sth_sel_file->fetchrow_arrayref) {
my $now = time();
$sth_ins_file->execute(
$file, $author, @stat ? $stat[9] : undef, @stat ? $stat[7] : undef,
$now, $now);
$file_id = $dbh->last_insert_id("","","","");
log_trace(" New file: %s (author %s)", $file, $author);
}
$file_ids_in_02packages{"$author|$file"} = $file_id;
}
next unless $file_id;
my $mod_id;
if (($mod_id) = $dbh->selectrow_array("SELECT id FROM module WHERE name=?", {}, $pkg)) {
$sth_upd_mod->execute( $file_id, $author, $ver, _numify_ver($ver), time(), $pkg);
} else {
my $now = time();
$sth_ins_mod->execute($pkg, $file_id, $author, $ver, _numify_ver($ver), $now,$now);
$mod_id = $dbh->last_insert_id("","","","");
_set_namespace($dbh, $pkg);
}
log_trace(" New/updated module: %s (file ID=%d, module ID=%d)", $pkg, $file_id, $mod_id);
} # while <fh>
# cleanup: delete file record (as well as dists, modules, and deps
# records) for files in db that are no longer in 02packages.
CLEANUP:
{
my @old_file_ids;
my @old_file_entries; # ("author|filename", ...)
for my $k (sort keys %file_ids_in_table) {
next if exists $file_ids_in_02packages{$k};
push @old_file_ids, $file_ids_in_table{$k};
push @old_file_entries, $k;
}
last CLEANUP unless @old_file_ids;
_delete_releases_records($dbh, @old_file_ids);
log_trace(" Deleted file records (%d): %s", ~~@old_file_entries, \@old_file_entries);
}
$dbh->commit;
}
my @passes;
if ($args{skip_file_indexing_pass_1}) {
log_info("Will be skipping file indexing pass 1");
} else {
push @passes, 1;
}
if ($args{skip_file_indexing_pass_2}) {
log_info("Will be skipping file indexing pass 2");
} else {
push @passes, 2;
}
if ($args{skip_file_indexing_pass_3} ||
($args{skip_sub_indexing} // 1)) {
log_info("Will be skipping file indexing pass 3");
} else {
push @passes, 3;
}
PROCESS_FILES_PASS:
for my $pass (@passes) {
# we're processing files in several passes.
# the first pass: insert content, scripts, extract meta, insert dep
# information, set file_status and meta_status.
# the second pass: extract PODs and insert module/script abstracts and
# pod mentions.
# the third pass: subroutine indexing
# we're doing it in several passes because: in pass 2, we want to
# collect all known scripts first to be able to detect links to scripts
# in POD (collected in pass 1). also some passes are more high-level
# and/or experimental and/or optional.
my $sth = $dbh->prepare(
$pass == 1 ?
"SELECT * FROM file WHERE file_status IS NULL OR meta_status IS NULL ORDER BY name" :
lib/App/lcpan.pm view on Meta::CPAN
skip_sub_indexing_files => {
summary => 'Skip one or more files from being parsed for subs',
'x.name.is_plural' => 1,
'x.name.singular' => 'skip_sub_indexing_file',
'summary.alt.plurality.singular' => 'Skip a file from being parsed for subs',
schema => ['array*', of=>'str*'],
examples => ['Foo-Bar-1.23.tar.gz'],
},
skip_sub_indexing_file_patterns => {
summary => 'Skip one or more file patterns from being parsed for subs',
'x.name.is_plural' => 1,
'x.name.singular' => 'skip_sub_indexing_file_pattern',
'summary.alt.plurality.singular' => 'Specify a file pattern to skip being parsed for subs',
schema => ['array*', of=>'re*'],
cmdline_aliases => {
},
examples => ['^Foo-Bar-\d'],
},
skip_file_indexing_pass_1 => {
schema => 'bool*',
},
skip_file_indexing_pass_2 => {
schema => 'bool*',
},
skip_file_indexing_pass_3 => {
schema => 'bool*',
},
skip_sub_indexing => {
schema => ['bool'],
default => 1,
description => <<'_',
Since sub indexing is still experimental, it is not enabled by default. To
enable it, pass the `--no-skip-sub-indexing` option.
_
},
},
tags => ['write-to-db', 'write-to-fs'],
};
sub update {
my %args = @_;
_set_args_default(\%args);
my $cpan = $args{cpan};
my $packages_path = "$cpan/modules/02packages.details.txt.gz";
my @st1 = stat($packages_path);
if (!$args{update_files}) {
log_info("Skipped updating files (reason: option update_files=0)");
} else {
_update_files(%args); # it only returns 200 or dies
}
my @st2 = stat($packages_path);
if (!$args{update_index} && !$args{force_update_index}) {
log_info("Skipped updating index (reason: option update_index=0)");
} elsif (!$args{force_update_index} && $args{update_files} &&
@st1 && @st2 && $st1[9] == $st2[9] && $st1[7] == $st2[7]) {
log_info("%s doesn't change mtime/size, skipping updating index",
$packages_path);
return [304, "Files did not change, index not updated"];
} else {
my $res = _update_index(%args);
return $res unless $res->[0] == 200;
}
[200, "OK"];
}
sub _table_exists {
my ($dbh, $schema, $name) = @_;
my $sth = $dbh->table_info(undef, $schema, $name, undef);
$sth->fetchrow_hashref ? 1:0;
}
sub _reset {
# this sub is used since v7, so we need to check tables that have not
# existed in v7 or earlier.
my ($dbh, $soft) = @_;
$dbh->do("DELETE FROM dep");
$dbh->do("DELETE FROM namespace");
$dbh->do("DELETE FROM mention") if _table_exists($dbh, "main", "mention");
$dbh->do("DELETE FROM module");
$dbh->do("DELETE FROM old_module")if _table_exists($dbh, "main", "old_module");
$dbh->do("DELETE FROM script") if _table_exists($dbh, "main", "script");
$dbh->do("DELETE FROM old_script")if _table_exists($dbh, "main", "old_script");
$dbh->do("DELETE FROM sub") if _table_exists($dbh, "main", "sub");
$dbh->do("DELETE FROM dist") if _table_exists($dbh, "main", "dist");
$dbh->do("DELETE FROM content") if _table_exists($dbh, "main", "content");
$dbh->do("DELETE FROM file");
$dbh->do("DELETE FROM old_file") if _table_exists($dbh, "main", "old_file");
$dbh->do("DELETE FROM author");
$dbh->do("DELETE FROM log") if _table_exists($dbh, "main", "log") && !$soft;
$dbh->do("DELETE FROM meta WHERE name='index_creation_time'") if !$soft;
}
$SPEC{'reset'} = {
v => 1.1,
summary => 'Reset (empty) the database index',
description => <<'_',
All data tables will be emptied. This includes all records in the `log` table as
well as `index_creation_time` record in the `meta` table, so there is no records
of previous indexing activity. There is also no record of resetting in the
`log`.
Tables are not dropped and re-created. The `meta` table is not emptied.
_
args => {
%common_args,
},
tags => ['write-to-db'],
};
sub reset {
require IO::Prompt::I18N;
my %args = @_;
my $state = _init(\%args, 'rw');
my $dbh = $state->{dbh};
lib/App/lcpan.pm view on Meta::CPAN
},
{
summary => 'List what distribution that contains Sah::Schema::filename requires',
description => <<'_',
Sah::Schema::filename is included in Sah-Schemas-Path distribution, so this
command is equivalent to "lcpan deps Sah-Schemas-Path". You can't do "lcpan deps
Sah::Schema::filename" because `lcpan` will assume that you ask "lcpan deps
Sah-Schema-filename" and there is no Sah-Schema-filename distribution.
_
argv => ['--module', 'Sah::Schema::filename'],
test => 0,
'x.doc.show_result' => 0,
},
{
summary => 'List non-core modules Module-List requires',
argv => ['Module-List', '--exclude-core'],
test => 0,
'x.doc.show_result' => 0,
},
{
summary => 'List dependencies of a specific distribution release',
argv => ['Module-List@0.004'],
test => 0,
'x.doc.show_result' => 0,
},
],
};
sub deps {
require Module::XSOrPP;
my %args = @_;
my $state = _init(\%args, 'ro');
my $dbh = $state->{dbh};
my $file_ids =
$args{dists} && !$args{modules} ? _dists_with_optional_vers2file_ids($dbh, $args{dists}) :
$args{modules} && !$args{dists} ? _modules2file_ids($dbh, $args{modules}) :
(return [400, "Please specify dists OR modules"]);
my $phase = $args{phase} // 'runtime';
my $rel = $args{rel} // 'requires';
my $plver = $args{perl_version} // "$^V";
my $level = $args{level} // 1;
my $include_core = $args{include_core} // 1;
my $include_noncore = $args{include_noncore} // 1;
my $with_xs_or_pp = $args{with_xs_or_pp};
my $include_indexed = $args{include_indexed} // 1;
my $include_unindexed = $args{include_unindexed} // 1;
_set_since(\%args, $dbh);
my $filters = {
include_core => $include_core,
include_noncore => $include_noncore,
include_indexed => $include_indexed,
include_unindexed => $include_unindexed,
exclude_deps => $args{exclude_deps},
authors => $args{authors},
authors_arent => $args{authors_arent},
added_since => $args{added_since},
updated_since => $args{updated_since},
added_or_updated_since => $args{added_or_updated_since},
};
my $res = _get_prereqs($file_ids, $dbh, {}, {},
1, $level, $filters, $plver, $args{flatten}, $args{dont_uniquify}, $phase, $rel);
return $res unless $res->[0] == 200;
my @cols;
push @cols, (qw/module/);
push @cols, "dist" if @$file_ids > 1;
push @cols, (qw/author version/);
push @cols, "is_core";
push @cols, "xs_or_pp" if $with_xs_or_pp;
for (@{$res->[2]}) {
if ($with_xs_or_pp) {
$_->{xs_or_pp} = Module::XSOrPP::xs_or_pp($_->{module});
}
$_->{module} = (" " x ($_->{level}-1)) . $_->{module}
unless $args{flatten};
delete $_->{dist} unless @$file_ids > 1 || $_->{level} > 1;
delete $_->{module_file_id};
delete $_->{level};
}
my $resmeta = {};
$resmeta->{'table.fields'} = \@cols;
$res->[3] = $resmeta;
$res;
}
my %rdeps_args = (
%common_args,
%argspec0opt_modules,
%argspecopt_dists,
%rdeps_rel_args,
%rdeps_phase_args,
%rdeps_level_args,
flatten => {
summary => 'Instead of showing tree-like information, flatten it',
schema => 'bool',
description => <<'_',
See deps' *flatten* argument for more details.
_
},
dont_uniquify => {
summary => 'Allow showing multiple modules for different dists',
schema => 'true*',
},
authors => {
'x.name.is_plural' => 1,
summary => 'Filter certain author',
schema => ['array*', of=>'str*'],
description => <<'_',
This can be used to select certain author(s).
_
completion => \&_complete_cpanid,
tags => ['category:filtering'],
lib/App/lcpan.pm view on Meta::CPAN
know whether a module is being used by another CPAN author instead of just
herself.
_
completion => \&_complete_cpanid,
tags => ['category:filtering'],
},
%fctime_args,
%fmtime_args,
%fctime_or_mtime_args,
);
our $rdeps_args_rels = {
dep_any => [flatten => ['level']],
};
$SPEC{'rdeps'} = {
v => 1.1,
summary => 'List reverse dependencies',
args => {
%rdeps_args,
},
examples => [
{
summary => 'List what distributions depend on Sah::Schema::filename',
argv => ['Sah::Schema::filename'],
test => 0,
'x.doc.show_result' => 0,
},
{
summary => 'List what distributions depend on one of the modules in Sah-Schemas-Path',
argv => ['--dist', 'Sah-Schemas-Path'],
test => 0,
'x.doc.show_result' => 0,
},
],
args_rels => {
req_one => ['modules', 'dists'],
dep_any => [flatten => ['level']],
},
};
sub rdeps {
my %args = @_;
my $state = _init(\%args, 'ro');
my $dbh = $state->{dbh};
my $mods =
$args{modules} && !$args{dists} ? $args{modules} :
$args{dists} && !$args{modules} ? _dists2theirmods($dbh, $args{dists}) :
(return [400, "Please specify modules OR dists"]);
my $level = $args{level} // 1;
my $authors = $args{authors} ? [map {uc} @{$args{authors}}] : undef;
my $authors_arent = $args{authors_arent} ? [map {uc} @{$args{authors_arent}}] : undef;
_set_since(\%args, $dbh);
my $filters = {
authors => $authors,
authors_arent => $authors_arent,
added_since => $args{added_since},
updated_since => $args{updated_since},
added_or_updated_since => $args{added_or_updated_since},
};
my $res = _get_revdeps($mods, $dbh, {}, {}, 1, $level, $filters, $args{flatten}, $args{dont_uniquify}, $args{phase}, $args{rel});
return $res unless $res->[0] == 200;
for (@{$res->[2]}) {
$_->{dist} = (" " x ($_->{level}-1)) . $_->{dist}
unless $args{flatten};
delete $_->{module} unless @$mods > 1 || $_->{level} > 1;
delete $_->{level};
}
my $resmeta = {};
$resmeta->{'table.fields'} = [qw/dist author dist_version req_version/];
$res->[3] = $resmeta;
$res;
}
$SPEC{namespaces} = {
v => 1.1,
summary => 'List namespaces',
args => {
%common_args,
%query_multi_args,
query_type => {
schema => ['str*', in=>[qw/any name exact-name regexp-name/]],
default => 'any',
cmdline_aliases => {
x => {
summary => 'Shortcut --query-type exact-name',
is_flag => 1,
code => sub { $_[0]{query_type} = 'exact-name' },
},
r => {
summary => 'Shortcut --query-type regexp-name',
is_flag => 1,
code => sub { $_[0]{query_type} = 'regexp-name' },
},
n => {
summary => 'Shortcut --query-type name',
is_flag => 1,
code => sub { $_[0]{query_type} = 'name' },
},
},
},
from_level => {
schema => ['int*', min=>0],
tags => ['category:filtering'],
},
to_level => {
schema => ['int*', min=>0],
tags => ['category:filtering'],
},
level => {
schema => ['int*', min=>0],
tags => ['category:filtering'],
},
sort => {
schema => ['str*', in=>[qw/name -name num_modules -num_modules/]],
default => 'name',
lib/App/lcpan.pm view on Meta::CPAN
push @res, $detail ? $row : $row->{name};
}
my $resmeta = {};
$resmeta->{'table.fields'} = [qw/name num_modules/]
if $detail;
[200, "OK", \@res, $resmeta];
}
1;
# ABSTRACT: Manage your local CPAN mirror
__END__
=pod
=encoding UTF-8
=head1 NAME
App::lcpan - Manage your local CPAN mirror
=head1 VERSION
This document describes version 1.074 of App::lcpan (from Perl distribution App-lcpan), released on 2023-09-26.
=head1 SYNOPSIS
See L<lcpan> script.
=head1 FUNCTIONS
=head2 authors
Usage:
authors(%args) -> [$status_code, $reason, $payload, \%result_meta]
List authors.
Examples:
=over
=item * List all authors:
authors();
=item * Find CPAN IDs which start with something:
authors(query => ["MICHAEL%"]); # -> ["MICHAEL", "MICHAELW", undef, {}]
=back
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<random> => I<true>
Random sort.
=item * B<result_limit> => I<uint>
Only return a certain number of records.
=item * B<result_start> => I<posint> (default: 1)
Only return starting from the n'th record.
=item * B<sort> => I<array[str]> (default: ["id"])
Sort the result.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
By default will return an array of CPAN ID's. If you set C<detail> to true, will
return array of records.
=head2 deps
Usage:
deps(%args) -> [$status_code, $reason, $payload, \%result_meta]
List dependencies of distributions.
Examples:
=over
=item * List what modules Module-List requires:
deps(dists => ["Module-List"]);
=item * List modules Module-List requires (module name will be converted to distro name):
deps(dists => ["Module::List"]);
=item * List what distribution that contains Sah::Schema::filename requires:
deps(modules => ["Sah::Schema::filename"]);
Sah::Schema::filename is included in Sah-Schemas-Path distribution, so this
command is equivalent to "lcpan deps Sah-Schemas-Path". You can't do "lcpan deps
Sah::Schema::filename" because C<lcpan> will assume that you ask "lcpan deps
Sah-Schema-filename" and there is no Sah-Schema-filename distribution.
=item * List non-core modules Module-List requires:
deps(dists => ["Module-List"], include_core => 0);
=item * List dependencies of a specific distribution release:
deps(dists => ["Module-List\@0.004"]);
=back
By default only runtime requires are displayed. To see prereqs for other phases
(e.g. configure, or build, or ALL) or for other relationships (e.g. recommends,
or ALL), use the C<--phase> and C<--rel> options.
Note that dependencies information are taken from C<META.json> or C<META.yml>
files. Not all releases (especially older ones) contain them. L<lcpan> (like
MetaCPAN) does not extract information from C<Makefile.PL> or C<Build.PL> because
that requires running (untrusted) code.
Also, some releases specify dynamic config, so there might actually be more
dependencies.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<dists> => I<array[perl::distname_with_optional_ver]>
Distribution names (with optional version suffix, e.g. Foo-Bar@1.23).
=item * B<dont_uniquify> => I<bool>
Allow showing multiple modules for different dists.
=item * B<exclude_deps> => I<array[perl::modname]>
Exclude some dependencies from being shown.
=item * B<flatten> => I<bool>
Instead of showing tree-like information, flatten it.
When recursing, the default is to show the final result in a tree-like table,
i.e. indented according to levels, e.g.:
% lcpan deps -R MyModule
| module | author | version |
|-------------------|---------|---------|
| Foo | AUTHOR1 | 0.01 |
| Bar | AUTHOR2 | 0.23 |
| Baz | AUTHOR3 | 1.15 |
| Qux | AUTHOR2 | 0 |
To be brief, if C<Qux> happens to also depends on C<Bar>, it will not be shown in
the result. Thus we don't know the actual C<Bar> version that is needed by the
dependency tree of C<MyModule>. For example, if C<Qux> happens to depends on C<Bar>
version 0.45 then C<MyModule> indirectly requires C<Bar> 0.45.
To list all the direct and indirect dependencies on a single flat list, with
versions already resolved to the largest version required, use the C<flatten>
option:
% lcpan deps -R --flatten MyModule
| module | author | version |
|-------------------|---------|---------|
| Foo | AUTHOR1 | 0.01 |
| Bar | AUTHOR2 | 0.45 |
| Baz | AUTHOR3 | 1.15 |
| Qux | AUTHOR2 | 0 |
Note that C<Bar>'s required version is already 0.45 in the above example.
=item * B<include_core> => I<bool> (default: 1)
Include core modules.
=item * B<include_indexed> => I<bool> (default: 1)
Include modules that are indexed (listed in 02packages.details.txt.gz).
=item * B<include_noncore> => I<bool> (default: 1)
Include non-core modules.
=item * B<include_unindexed> => I<bool> (default: 1)
Include modules that are not indexed (not listed in 02packages.details.txt.gz).
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<level> => I<int> (default: 1)
Recurse for a number of levels (-1 means unlimited).
=item * B<modules> => I<array[perl::modname]>
(No description)
=item * B<perl_version> => I<str> (default: "v5.38.0")
Set base Perl version for determining core modules.
=item * B<phase> => I<str> (default: "runtime")
(No description)
=item * B<rel> => I<str> (default: "requires")
(No description)
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=item * B<with_xs_or_pp> => I<bool>
Check each dependency as XSE<sol>PP.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 dists
Usage:
dists(%args) -> [$status_code, $reason, $payload, \%result_meta]
List distributions.
Examples:
=over
=item * List all distributions:
dists(cpan => "/cpan");
=item * List all distributions (latest version only):
dists(cpan => "/cpan", latest => 1);
=item * Grep by distribution name, return detailed record:
dists(query => ["data-table"], cpan => "/cpan");
=back
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<author> => I<str>
Filter by author.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<has_buildpl> => I<bool>
(No description)
=item * B<has_makefilepl> => I<bool>
(No description)
=item * B<has_metajson> => I<bool>
(No description)
=item * B<has_metayml> => I<bool>
(No description)
=item * B<has_multiple_rels> => I<bool>
(No description)
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<latest> => I<bool>
(No description)
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<random> => I<true>
Random sort.
=item * B<rel_mtime_newer_than> => I<date>
(No description)
=item * B<result_limit> => I<uint>
Only return a certain number of records.
=item * B<result_start> => I<posint> (default: 1)
Only return starting from the n'th record.
=item * B<sort> => I<array[str]> (default: ["dist"])
Sort the result.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
By default will return an array of distribution names. If you set C<detail> to
true, will return array of records.
=head2 log
Usage:
log(%args) -> [$status_code, $reason, $payload, \%result_meta]
Show database index log.
This function is not exported.
Arguments ('*' denotes required arguments):
=over 4
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 modules
Usage:
modules(%args) -> [$status_code, $reason, $payload, \%result_meta]
List modulesE<sol>packages.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<author> => I<str>
Filter by author.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<dist> => I<perl::distname>
Filter by distribution.
=item * B<include_core> => I<bool> (default: 1)
Include core modules.
=item * B<include_noncore> => I<bool> (default: 1)
Include non-core modules.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<latest> => I<bool>
(No description)
=item * B<namespaces> => I<array[perl::modname]>
Select modules belonging to certain namespace(s).
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<perl_version> => I<str> (default: "v5.38.0")
Set base Perl version for determining core modules.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<random> => I<true>
Random sort.
=item * B<result_limit> => I<uint>
Only return a certain number of records.
=item * B<result_start> => I<posint> (default: 1)
Only return starting from the n'th record.
=item * B<sort> => I<array[str]> (default: ["module"])
Sort the result.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
By default will return an array of package names. If you set C<detail> to true,
will return array of records.
=head2 namespaces
Usage:
namespaces(%args) -> [$status_code, $reason, $payload, \%result_meta]
List namespaces.
This function is not exported.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<from_level> => I<int>
(No description)
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<level> => I<int>
(No description)
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<sort> => I<str> (default: "name")
(No description)
=item * B<to_level> => I<int>
(No description)
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 packages
Usage:
packages(%args) -> [$status_code, $reason, $payload, \%result_meta]
List modulesE<sol>packages.
This function is not exported.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<author> => I<str>
Filter by author.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<dist> => I<perl::distname>
Filter by distribution.
=item * B<include_core> => I<bool> (default: 1)
Include core modules.
=item * B<include_noncore> => I<bool> (default: 1)
Include non-core modules.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<latest> => I<bool>
(No description)
=item * B<namespaces> => I<array[perl::modname]>
Select modules belonging to certain namespace(s).
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<perl_version> => I<str> (default: "v5.38.0")
Set base Perl version for determining core modules.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<random> => I<true>
Random sort.
=item * B<result_limit> => I<uint>
Only return a certain number of records.
=item * B<result_start> => I<posint> (default: 1)
Only return starting from the n'th record.
=item * B<sort> => I<array[str]> (default: ["module"])
Sort the result.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
By default will return an array of package names. If you set C<detail> to true,
will return array of records.
=head2 rdeps
Usage:
rdeps(%args) -> [$status_code, $reason, $payload, \%result_meta]
List reverse dependencies.
Examples:
=over
=item * List what distributions depend on Sah::Schema::filename:
rdeps(modules => ["Sah::Schema::filename"]);
=item * List what distributions depend on one of the modules in Sah-Schemas-Path:
rdeps(dists => ["Sah-Schemas-Path"]);
=back
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<authors> => I<array[str]>
Filter certain author.
This can be used to select certain author(s).
=item * B<authors_arent> => I<array[str]>
Filter out certain author.
This can be used to filter out certain author(s). For example if you want to
know whether a module is being used by another CPAN author instead of just
herself.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<dists> => I<array[perl::distname]>
Distribution names (e.g. Foo-Bar).
=item * B<dont_uniquify> => I<true>
Allow showing multiple modules for different dists.
=item * B<flatten> => I<bool>
Instead of showing tree-like information, flatten it.
See deps' I<flatten> argument for more details.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<level> => I<int> (default: 1)
Recurse for a number of levels (-1 means unlimited).
=item * B<modules> => I<array[perl::modname]>
(No description)
=item * B<phase> => I<str> (default: "ALL")
(No description)
=item * B<rel> => I<str> (default: "ALL")
(No description)
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 releases
Usage:
releases(%args) -> [$status_code, $reason, $payload, \%result_meta]
List releasesE<sol>tarballs.
The status field is the processing status of the file/release by lcpan. C<ok>
means file has been extracted and the meta files parsed, C<nofile> means file is
not found in mirror (possibly because the mirroring process excludes the file
e.g. due to file size too large), C<nometa> means file does not contain
META.{yml,json}, C<unsupported> means file archive format is not supported (e.g.
rar), C<err> means some other error in processing file.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<added_or_updated_since> => I<date>
Include only records that are addedE<sol>updated since a certain date.
=item * B<added_or_updated_since_last_index_update> => I<true>
Include only records that are addedE<sol>updated since the last index update.
=item * B<added_or_updated_since_last_n_index_updates> => I<posint>
Include only records that are addedE<sol>updated since the last N index updates.
=item * B<added_since> => I<date>
Include only records that are added since a certain date.
=item * B<added_since_last_index_update> => I<true>
Include only records that are added since the last index update.
=item * B<added_since_last_n_index_updates> => I<posint>
Include only records that are added since the last N index updates.
=item * B<author> => I<str>
Filter by author.
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<detail> => I<bool>
(No description)
=item * B<full_path> => I<bool>
(No description)
=item * B<has_buildpl> => I<bool>
(No description)
=item * B<has_makefilepl> => I<bool>
(No description)
=item * B<has_metajson> => I<bool>
(No description)
=item * B<has_metayml> => I<bool>
(No description)
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<latest> => I<bool>
(No description)
=item * B<no_path> => I<bool>
(No description)
=item * B<or> => I<bool>
When there are more than one query, perform OR instead of AND logic.
=item * B<query> => I<array[str]>
Search query.
=item * B<query_type> => I<str> (default: "any")
(No description)
=item * B<random> => I<true>
Random sort.
=item * B<result_limit> => I<uint>
Only return a certain number of records.
=item * B<result_start> => I<posint> (default: 1)
Only return starting from the n'th record.
=item * B<sort> => I<array[str]> (default: ["name"])
(No description)
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<updated_since> => I<date>
Include only records that are updated since certain date.
=item * B<updated_since_last_index_update> => I<true>
Include only records that are updated since the last index update.
=item * B<updated_since_last_n_index_updates> => I<posint>
Include only records that are updated since the last N index updates.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 reset
Usage:
reset(%args) -> [$status_code, $reason, $payload, \%result_meta]
Reset (empty) the database index.
All data tables will be emptied. This includes all records in the C<log> table as
well as C<index_creation_time> record in the C<meta> table, so there is no records
of previous indexing activity. There is also no record of resetting in the
C<log>.
Tables are not dropped and re-created. The C<meta> table is not emptied.
This function is not exported.
Arguments ('*' denotes required arguments):
=over 4
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 stats
Usage:
stats(%args) -> [$status_code, $reason, $payload, \%result_meta]
Statistics of your local CPAN mirror.
This function is not exported.
Arguments ('*' denotes required arguments):
=over 4
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<index_name> => I<filename> (default: "index.db")
Filename of index.
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head2 update
Usage:
update(%args) -> [$status_code, $reason, $payload, \%result_meta]
CreateE<sol>update local CPAN mirror.
This subcommand first create/update the mirror files by downloading from a
remote CPAN mirror, then update the index.
This function is not exported by default, but exportable.
Arguments ('*' denotes required arguments):
=over 4
=item * B<cpan> => I<dirname>
Location of your local CPAN mirror, e.g. E<sol>pathE<sol>toE<sol>cpan.
Defaults to C<~/cpan>.
=item * B<exclude_author> => I<array[str]>
Exclude files from certain author(s).
lib/App/lcpan.pm view on Meta::CPAN
If C<index_name> is a filename without any path, e.g. C<index.db> then index will
be located in the top-level of C<cpan>. If C<index_name> contains a path, e.g.
C<./index.db> or C</home/ujang/lcpan.db> then the index will be located solely
using the C<index_name>.
=item * B<max_file_size> => I<int>
If set, skip downloading files larger than this.
=item * B<remote_url> => I<str>
Select CPAN mirror to download from.
=item * B<retry_delay> => I<int>
Number of seconds to delay between retry attempt.
=item * B<retry_max_attempts> => I<int>
Number of retry attempts on failed HTTP request.
=item * B<skip_file_indexing_pass_1> => I<bool>
(No description)
=item * B<skip_file_indexing_pass_2> => I<bool>
(No description)
=item * B<skip_file_indexing_pass_3> => I<bool>
(No description)
=item * B<skip_index_file_patterns> => I<array[re]>
Skip one or more file patterns from being indexed.
=item * B<skip_index_files> => I<array[str]>
Skip one or more files from being indexed.
=item * B<skip_sub_indexing> => I<bool> (default: 1)
Since sub indexing is still experimental, it is not enabled by default. To
enable it, pass the C<--no-skip-sub-indexing> option.
=item * B<skip_sub_indexing_file_patterns> => I<array[re]>
Skip one or more file patterns from being parsed for subs.
=item * B<skip_sub_indexing_files> => I<array[str]>
Skip one or more files from being parsed for subs.
=item * B<update_db_schema> => I<bool> (default: 1)
Whether to update database schema to the latest.
By default, when the application starts and reads the index database, it updates
the database schema to the latest if the database happens to be last updated by
an older version of the application and has the old database schema (since
database schema is updated from time to time, for example at 1.070 the database
schema is at version 15).
When you disable this option, the application will not update the database
schema. This option is for testing only, because it will probably cause the
application to run abnormally and then die with a SQL error when reading/writing
to the database.
Note that in certain modes e.g. doing tab completion, the application also will
not update the database schema.
=item * B<update_files> => I<bool> (default: 1)
Update the files.
=item * B<update_index> => I<bool> (default: 1)
Update the index.
=item * B<use_bootstrap> => I<bool> (default: 1)
Whether to use bootstrap database from App-lcpan-Bootstrap.
If you are indexing your private CPAN-like repository, you want to turn this
off.
=back
Returns an enveloped result (an array).
First element ($status_code) is an integer containing HTTP-like status code
(200 means OK, 4xx caller error, 5xx function error). Second element
($reason) is a string containing error message, or something like "OK" if status is
200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
element (%result_meta) is called result metadata and is optional, a hash
that contains extra information, much like how HTTP response headers provide additional metadata.
Return value: (any)
=head1 HOMEPAGE
Please visit the project's homepage at L<https://metacpan.org/release/App-lcpan>.
=head1 SOURCE
Source repository is at L<https://github.com/perlancar/perl-App-lcpan>.
=head1 SEE ALSO
L<App::lcpan::Manual>
L<CPAN::SQLite>
L<CPAN::Mini>
=head1 HISTORY
This application began as L<CPAN::SQLite::CPANMeta>, an extension of
L<CPAN::SQLite>. C<CPAN::SQLite> parses C<02packages.details.txt.gz> and
C<01mailrc.txt.gz> and puts the parse result into a SQLite database.
( run in 0.462 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )