view release on metacpan or search on metacpan
- Metadata merging now does deep hash merging as long as keys
don't conflict
2.150001 2015-03-09 14:41:39-04:00 America/New_York
[DOCUMENTED]
- Include allowed values for license field in 1.x historic licenses rather
than linking to Module::Build
- Documented when fragment merging became available
2.143240 2014-11-20 10:26:30-05:00 America/New_York
[FIXED]
- Give correct path in nested merges such as resources
- Removed strings test that should have been removed when
CPAN::Meta::Requirements was removed to a separate dist
2.141520 2014-05-31 23:41:13-04:00 America/New_York
[DOCUMENTED]
- Clarified use of 'file' for the 'provides' field in the Spec
2.141170 2014-04-27 13:03:37-04:00 America/New_York
[ADDED]
- Added ability for CPAN::Meta::Converter to convert metadata fragments
(incomplete portions of a metadata structure)
[CHANGED]
- Optimized internal use of JSON for datastructure cloning
[FIXED]
- Removed dependency on List::Util 1.33
lib/CPAN/Meta/Prereqs.pm
lib/CPAN/Meta/Spec.pm
lib/CPAN/Meta/Validator.pm
lib/Parse/CPAN/Meta.pm
perlcritic.rc
t/00-report-prereqs.dd
t/00-report-prereqs.t
t/README-data.txt
t/converter-bad.t
t/converter-fail.t
t/converter-fragments.t
t/converter.t
t/data-fail/META-1_0.yml
t/data-fail/META-1_1.yml
t/data-fail/META-1_2.yml
t/data-fail/META-1_3.yml
t/data-fail/META-1_4.yml
t/data-fail/META-2.json
t/data-fixable/107650337-META.yml
t/data-fixable/1598804075-META.yml
t/data-fixable/1927486199-META.yml
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
my $new_data = {};
for my $k ( keys %$element ) {
$new_data->{$k} = _convert($element->{$k}, $spec, $to_version);
$new_data->{$k}{version} = _clean_version($element->{$k}{version})
if exists $element->{$k}{version};
}
return $new_data;
}
sub _convert {
my ($data, $spec, $to_version, $is_fragment) = @_;
my $new_data = {};
for my $key ( keys %$spec ) {
next if $key eq ':custom' || $key eq ':drop';
next unless my $fcn = $spec->{$key};
if ( $is_fragment && $key eq 'generated_by' ) {
$fcn = \&_keep;
}
die "spec for '$key' is not a coderef"
unless ref $fcn && ref $fcn eq 'CODE';
my $new_value = $fcn->($data->{$key}, $key, $data, $to_version);
$new_data->{$key} = $new_value if defined $new_value;
}
my $drop_list = $spec->{':drop'};
my $customizer = $spec->{':custom'} || \&_keep;
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
'requires' => \&_version_map,
# other random keys are OK if already valid
':custom' => \&_keep,
},
);
# for a given field in a spec version, what fields will it feed
# into in the *latest* spec (i.e. v2); meta-spec omitted because
# we always expect a meta-spec to be generated
my %fragments_generate = (
'2' => {
'abstract' => 'abstract',
'author' => 'author',
'generated_by' => 'generated_by',
'license' => 'license',
'name' => 'name',
'version' => 'version',
'dynamic_config' => 'dynamic_config',
'release_status' => 'release_status',
'keywords' => 'keywords',
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
'optional_features' => 'optional_features',
'provides' => 'provides',
'recommends' => 'prereqs',
'requires' => 'prereqs',
'resources' => 'resources',
'configure_requires' => 'prereqs',
},
);
# this is not quite true but will work well enough
# as 1.4 is a superset of earlier ones
$fragments_generate{$_} = $fragments_generate{'1.4'} for qw/1.3 1.2 1.1 1.0/;
#--------------------------------------------------------------------------#
# Code
#--------------------------------------------------------------------------#
#pod =method new
#pod
#pod my $cmc = CPAN::Meta::Converter->new( $struct );
#pod
#pod The constructor should be passed a valid metadata structure but invalid
#pod structures are accepted. If no meta-spec version is provided, version 1.0 will
#pod be assumed.
#pod
#pod Optionally, you can provide a C<default_version> argument after C<$struct>:
#pod
#pod my $cmc = CPAN::Meta::Converter->new( $struct, default_version => "1.4" );
#pod
#pod This is only needed when converting a metadata fragment that does not include a
#pod C<meta-spec> field.
#pod
#pod =cut
sub new {
my ($class,$data,%args) = @_;
# create an attributes hash
my $self = {
'data' => $data,
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
#pod * author
#pod * license
#pod
#pod =cut
sub convert {
my ($self, %args) = @_;
my $args = { %args };
my $new_version = $args->{version} || $HIGHEST;
my $is_fragment = $args->{is_fragment};
my ($old_version) = $self->{spec};
my $converted = _dclone($self->{data});
if ( $old_version == $new_version ) {
$converted = _convert( $converted, $cleanup{$old_version}, $old_version, $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to clean-up $old_version metadata. Errors:\n$errs\n";
}
}
return $converted;
}
elsif ( $old_version > $new_version ) {
my @vers = sort { $b <=> $a } keys %known_specs;
for my $i ( 0 .. $#vers-1 ) {
next if $vers[$i] > $old_version;
last if $vers[$i+1] < $new_version;
my $spec_string = "$vers[$i+1]-from-$vers[$i]";
$converted = _convert( $converted, $down_convert{$spec_string}, $vers[$i+1], $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to downconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
}
}
}
return $converted;
}
else {
my @vers = sort { $a <=> $b } keys %known_specs;
for my $i ( 0 .. $#vers-1 ) {
next if $vers[$i] < $old_version;
last if $vers[$i+1] > $new_version;
my $spec_string = "$vers[$i+1]-from-$vers[$i]";
$converted = _convert( $converted, $up_convert{$spec_string}, $vers[$i+1], $is_fragment );
unless ( $args->{is_fragment} ) {
my $cmv = CPAN::Meta::Validator->new( $converted );
unless ( $cmv->is_valid ) {
my $errs = join("\n", $cmv->errors);
die "Failed to upconvert metadata to $vers[$i+1]. Errors:\n$errs\n";
}
}
}
return $converted;
}
}
#pod =method upgrade_fragment
#pod
#pod my $new_struct = $cmc->upgrade_fragment;
#pod
#pod Returns a new hash reference with the metadata converted to the latest version
#pod of the CPAN Meta Spec. No validation is done on the result -- you must
#pod validate after merging fragments into a complete metadata document.
#pod
#pod Available since version 2.141170.
#pod
#pod =cut
sub upgrade_fragment {
my ($self) = @_;
my ($old_version) = $self->{spec};
my %expected =
map {; $_ => 1 }
grep { defined }
map { $fragments_generate{$old_version}{$_} }
keys %{ $self->{data} };
my $converted = $self->convert( version => $HIGHEST, is_fragment => 1 );
for my $key ( keys %$converted ) {
next if $key =~ /^x_/i || $key eq 'meta-spec';
delete $converted->{$key} unless $expected{$key};
}
return $converted;
}
1;
# ABSTRACT: Convert CPAN distribution metadata structures
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
my $cmc = CPAN::Meta::Converter->new( $struct );
The constructor should be passed a valid metadata structure but invalid
structures are accepted. If no meta-spec version is provided, version 1.0 will
be assumed.
Optionally, you can provide a C<default_version> argument after C<$struct>:
my $cmc = CPAN::Meta::Converter->new( $struct, default_version => "1.4" );
This is only needed when converting a metadata fragment that does not include a
C<meta-spec> field.
=head2 convert
my $new_struct = $cmc->convert( version => "2" );
Returns a new hash reference with the metadata converted to a different form.
C<convert> will die if any conversion/standardization still results in an
invalid structure.
lib/CPAN/Meta/Converter.pm view on Meta::CPAN
=item *
author
=item *
license
=back
=head2 upgrade_fragment
my $new_struct = $cmc->upgrade_fragment;
Returns a new hash reference with the metadata converted to the latest version
of the CPAN Meta Spec. No validation is done on the result -- you must
validate after merging fragments into a complete metadata document.
Available since version 2.141170.
=head1 BUGS
Please report any bugs or feature using the CPAN Request Tracker.
Bugs can be submitted through the web interface at
L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
When submitting a bug or request, please include a test-file or a patch to an
lib/CPAN/Meta/Merge.pm view on Meta::CPAN
my ($self, @items) = @_;
my $current = {};
for my $next (@items) {
if ( blessed($next) && $next->isa('CPAN::Meta') ) {
$next = $next->as_struct;
}
elsif ( ref($next) eq 'HASH' ) {
my $cmc = CPAN::Meta::Converter->new(
$next, default_version => $self->{default_version}
);
$next = $cmc->upgrade_fragment;
}
else {
croak "Don't know how to merge '$next'";
}
$current = _merge($current, $next, $self->{mapping}, []);
}
return $current;
}
1;
# ABSTRACT: Merging CPAN Meta fragments
# vim: ts=2 sts=2 sw=2 et :
__END__
=pod
=encoding UTF-8
=head1 NAME
CPAN::Meta::Merge - Merging CPAN Meta fragments
=head1 VERSION
version 2.150015
=head1 SYNOPSIS
my $merger = CPAN::Meta::Merge->new(default_version => "2");
my $meta = $merger->merge($base, @additional);
lib/CPAN/Meta/Merge.pm view on Meta::CPAN
extra_mappings => {
'optional_features' => \&custom_merge_function,
'x_custom' => 'set_addition',
'x_meta_meta' => {
name => 'identical',
tags => 'set_addition',
}
}
);
=head2 merge(@fragments)
Merge all C<@fragments> together. It will accept both CPAN::Meta objects and
(possibly incomplete) hashrefs of metadata.
=head1 MERGE STRATEGIES
C<merge> uses various strategies to combine different elements of the CPAN::Meta objects. The following strategies can be used with the extra_mappings argument of C<new>:
=over
=item identical
lib/CPAN/Meta/Validator.pm view on Meta::CPAN
}
# _uri_split taken from URI::Split by Gisle Aas, Copyright 2003
sub _uri_split {
return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
}
sub url {
my ($self,$key,$value) = @_;
if(defined $value) {
my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
unless ( defined $scheme && length $scheme ) {
$self->_error( "'$value' for '$key' does not have a URL scheme" );
return 0;
}
unless ( defined $auth && length $auth ) {
$self->_error( "'$value' for '$key' does not have a URL authority" );
return 0;
}
return 1;
}
t/converter-fragments.t view on Meta::CPAN
'meta-spec' => $spec2,
license => [ 'perl_5' ],
},
},
);
for my $c (@cases) {
my $cmc = CPAN::Meta::Converter->new(
$c->{input}, $c->{from} ? (default_version => $c->{from} ) : ()
);
my $got = $cmc->upgrade_fragment;
my $exp = $c->{expect};
is_deeply( $got, $exp, $c->{label} )
or diag "GOT:\n", explain($got), "EXPECTED:\n", explain($exp);
}
done_testing;
# vim: ts=8 sts=4 sw=4 et :
t/optional_feature-merge.t view on Meta::CPAN
Baz => {
file => 'lib/Baz.pm',
},
},
'meta-spec' => {
url => "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
version => 2,
},
);
my $fragment1 = {
'optional_features' => {
'FeatureName' => {
'description' => 'desc',
'x_default' => 1,
'prereqs' => { 'runtime' => { 'requires' => { 'A' => '0' } } }
}
}
};
my $fragment2 = {
'optional_features' => {
'FeatureName' => {
'description' => 'desc',
'prereqs' => { 'test' => { 'requires' => { 'B' => '0' } } }
}
}
};
my $merger = CPAN::Meta::Merge->new(default_version => "2");
my $meta1 = $merger->merge(\%base, $fragment1);
is_deeply(
$meta1,
{
%base,
%$fragment1,
},
'merged first optional_feature fragment into base',
);
my $meta2 = $merger->merge($meta1, $fragment2);
is_deeply(
$meta2,
{
%base,
'optional_features' => {
'FeatureName' => {
'description' => 'desc',
'x_default' => 1,
'prereqs' => {
'runtime' => { 'requires' => { 'A' => '0' } },
'test' => { 'requires' => { 'B' => '0' } },
}
}
}
},
'merged second optional_feature fragment into the first',
);
my $fragment3 = {
'optional_features' => {
'FeatureName' => {
'description' => 'other desc',
'prereqs' => { 'test' => { 'requires' => { 'B' => '0' } } }
}
}
};
is( eval { $merger->merge($meta1, $fragment3) }, undef, 'Trying to merge optional_features with same feature name and different descriptions gives an exception');
like $@, qr/^Cannot merge two optional_features named 'FeatureName' with different 'description' values/, 'Exception looks right';
my $fragment4 = {
'optional_features' => {
'FeatureName' => {
'description' => 'desc',
'x_default' => 0,
'prereqs' => { 'test' => { 'requires' => { 'B' => '0' } } }
}
}
};
is( eval { $merger->merge($meta1, $fragment4) }, undef, 'Trying to merge optional_features with same feature name and differences in other keys gives an exception');
like $@, qr/^Cannot merge two optional_features named 'FeatureName' with different 'x_default' values/, 'Exception looks right';
my $fragment5 = {
'optional_features' => {
'Another FeatureName' => {
'description' => 'desc',
'prereqs' => { 'test' => { 'requires' => { 'B' => '0' } } }
}
}
};
my $meta5 = $merger->merge($meta1, $fragment5);
is_deeply(
$meta5,
{
%base,
'optional_features' => {
'FeatureName' => {
'description' => 'desc',
'x_default' => 1,
'prereqs' => { 'runtime' => { 'requires' => { 'A' => '0' } } },
},