1use 5.006; 2use strict; 3use warnings; 4package CPAN::Meta::Converter; 5our $VERSION = '2.140640'; # VERSION 6 7# =head1 SYNOPSIS 8# 9# my $struct = decode_json_file('META.json'); 10# 11# my $cmc = CPAN::Meta::Converter->new( $struct ); 12# 13# my $new_struct = $cmc->convert( version => "2" ); 14# 15# =head1 DESCRIPTION 16# 17# This module converts CPAN Meta structures from one form to another. The 18# primary use is to convert older structures to the most modern version of 19# the specification, but other transformations may be implemented in the 20# future as needed. (E.g. stripping all custom fields or stripping all 21# optional fields.) 22# 23# =cut 24 25use CPAN::Meta::Validator; 26use CPAN::Meta::Requirements; 27use version 0.88 (); 28use Parse::CPAN::Meta 1.4400 (); 29use List::Util 1.33 qw/all/; 30 31sub _dclone { 32 my $ref = shift; 33 34 # if an object is in the data structure and doesn't specify how to 35 # turn itself into JSON, we just stringify the object. That does the 36 # right thing for typical things that might be there, like version objects, 37 # Path::Class objects, etc. 38 no warnings 'once'; 39 local *UNIVERSAL::TO_JSON = sub { return "$_[0]" }; 40 41 my $backend = Parse::CPAN::Meta->json_backend(); 42 return $backend->new->utf8->decode( 43 $backend->new->utf8->allow_blessed->convert_blessed->encode($ref) 44 ); 45} 46 47my %known_specs = ( 48 '2' => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec', 49 '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html', 50 '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html', 51 '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html', 52 '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html', 53 '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html' 54); 55 56my @spec_list = sort { $a <=> $b } keys %known_specs; 57my ($LOWEST, $HIGHEST) = @spec_list[0,-1]; 58 59#--------------------------------------------------------------------------# 60# converters 61# 62# called as $converter->($element, $field_name, $full_meta, $to_version) 63# 64# defined return value used for field 65# undef return value means field is skipped 66#--------------------------------------------------------------------------# 67 68sub _keep { $_[0] } 69 70sub _keep_or_one { defined($_[0]) ? $_[0] : 1 } 71 72sub _keep_or_zero { defined($_[0]) ? $_[0] : 0 } 73 74sub _keep_or_unknown { defined($_[0]) && length($_[0]) ? $_[0] : "unknown" } 75 76sub _generated_by { 77 my $gen = shift; 78 my $sig = __PACKAGE__ . " version " . (__PACKAGE__->VERSION || "<dev>"); 79 80 return $sig unless defined $gen and length $gen; 81 return $gen if $gen =~ /\Q$sig/; 82 return "$gen, $sig"; 83} 84 85sub _listify { ! defined $_[0] ? undef : ref $_[0] eq 'ARRAY' ? $_[0] : [$_[0]] } 86 87sub _prefix_custom { 88 my $key = shift; 89 $key =~ s/^(?!x_) # Unless it already starts with x_ 90 (?:x-?)? # Remove leading x- or x (if present) 91 /x_/ix; # and prepend x_ 92 return $key; 93} 94 95sub _ucfirst_custom { 96 my $key = shift; 97 $key = ucfirst $key unless $key =~ /[A-Z]/; 98 return $key; 99} 100 101sub _no_prefix_ucfirst_custom { 102 my $key = shift; 103 $key =~ s/^x_//; 104 return _ucfirst_custom($key); 105} 106 107sub _change_meta_spec { 108 my ($element, undef, undef, $version) = @_; 109 return { 110 version => $version, 111 url => $known_specs{$version}, 112 }; 113} 114 115my @open_source = ( 116 'perl', 117 'gpl', 118 'apache', 119 'artistic', 120 'artistic_2', 121 'lgpl', 122 'bsd', 123 'gpl', 124 'mit', 125 'mozilla', 126 'open_source', 127); 128 129my %is_open_source = map {; $_ => 1 } @open_source; 130 131my @valid_licenses_1 = ( 132 @open_source, 133 'unrestricted', 134 'restrictive', 135 'unknown', 136); 137 138my %license_map_1 = ( 139 ( map { $_ => $_ } @valid_licenses_1 ), 140 artistic2 => 'artistic_2', 141); 142 143sub _license_1 { 144 my ($element) = @_; 145 return 'unknown' unless defined $element; 146 if ( $license_map_1{lc $element} ) { 147 return $license_map_1{lc $element}; 148 } 149 else { 150 return 'unknown'; 151 } 152} 153 154my @valid_licenses_2 = qw( 155 agpl_3 156 apache_1_1 157 apache_2_0 158 artistic_1 159 artistic_2 160 bsd 161 freebsd 162 gfdl_1_2 163 gfdl_1_3 164 gpl_1 165 gpl_2 166 gpl_3 167 lgpl_2_1 168 lgpl_3_0 169 mit 170 mozilla_1_0 171 mozilla_1_1 172 openssl 173 perl_5 174 qpl_1_0 175 ssleay 176 sun 177 zlib 178 open_source 179 restricted 180 unrestricted 181 unknown 182); 183 184# The "old" values were defined by Module::Build, and were often vague. I have 185# made the decisions below based on reading Module::Build::API and how clearly 186# it specifies the version of the license. 187my %license_map_2 = ( 188 (map { $_ => $_ } @valid_licenses_2), 189 apache => 'apache_2_0', # clearly stated as 2.0 190 artistic => 'artistic_1', # clearly stated as 1 191 artistic2 => 'artistic_2', # clearly stated as 2 192 gpl => 'open_source', # we don't know which GPL; punt 193 lgpl => 'open_source', # we don't know which LGPL; punt 194 mozilla => 'open_source', # we don't know which MPL; punt 195 perl => 'perl_5', # clearly Perl 5 196 restrictive => 'restricted', 197); 198 199sub _license_2 { 200 my ($element) = @_; 201 return [ 'unknown' ] unless defined $element; 202 $element = [ $element ] unless ref $element eq 'ARRAY'; 203 my @new_list; 204 for my $lic ( @$element ) { 205 next unless defined $lic; 206 if ( my $new = $license_map_2{lc $lic} ) { 207 push @new_list, $new; 208 } 209 } 210 return @new_list ? \@new_list : [ 'unknown' ]; 211} 212 213my %license_downgrade_map = qw( 214 agpl_3 open_source 215 apache_1_1 apache 216 apache_2_0 apache 217 artistic_1 artistic 218 artistic_2 artistic_2 219 bsd bsd 220 freebsd open_source 221 gfdl_1_2 open_source 222 gfdl_1_3 open_source 223 gpl_1 gpl 224 gpl_2 gpl 225 gpl_3 gpl 226 lgpl_2_1 lgpl 227 lgpl_3_0 lgpl 228 mit mit 229 mozilla_1_0 mozilla 230 mozilla_1_1 mozilla 231 openssl open_source 232 perl_5 perl 233 qpl_1_0 open_source 234 ssleay open_source 235 sun open_source 236 zlib open_source 237 open_source open_source 238 restricted restrictive 239 unrestricted unrestricted 240 unknown unknown 241); 242 243sub _downgrade_license { 244 my ($element) = @_; 245 if ( ! defined $element ) { 246 return "unknown"; 247 } 248 elsif( ref $element eq 'ARRAY' ) { 249 if ( @$element > 1) { 250 if ( all { $is_open_source{ $license_downgrade_map{lc $_} || 'unknown' } } @$element ) { 251 return 'open_source'; 252 } 253 else { 254 return 'unknown'; 255 } 256 } 257 elsif ( @$element == 1 ) { 258 return $license_downgrade_map{lc $element->[0]} || "unknown"; 259 } 260 } 261 elsif ( ! ref $element ) { 262 return $license_downgrade_map{lc $element} || "unknown"; 263 } 264 return "unknown"; 265} 266 267my $no_index_spec_1_2 = { 268 'file' => \&_listify, 269 'dir' => \&_listify, 270 'package' => \&_listify, 271 'namespace' => \&_listify, 272}; 273 274my $no_index_spec_1_3 = { 275 'file' => \&_listify, 276 'directory' => \&_listify, 277 'package' => \&_listify, 278 'namespace' => \&_listify, 279}; 280 281my $no_index_spec_2 = { 282 'file' => \&_listify, 283 'directory' => \&_listify, 284 'package' => \&_listify, 285 'namespace' => \&_listify, 286 ':custom' => \&_prefix_custom, 287}; 288 289sub _no_index_1_2 { 290 my (undef, undef, $meta) = @_; 291 my $no_index = $meta->{no_index} || $meta->{private}; 292 return unless $no_index; 293 294 # cleanup wrong format 295 if ( ! ref $no_index ) { 296 my $item = $no_index; 297 $no_index = { dir => [ $item ], file => [ $item ] }; 298 } 299 elsif ( ref $no_index eq 'ARRAY' ) { 300 my $list = $no_index; 301 $no_index = { dir => [ @$list ], file => [ @$list ] }; 302 } 303 304 # common mistake: files -> file 305 if ( exists $no_index->{files} ) { 306 $no_index->{file} = delete $no_index->{file}; 307 } 308 # common mistake: modules -> module 309 if ( exists $no_index->{modules} ) { 310 $no_index->{module} = delete $no_index->{module}; 311 } 312 return _convert($no_index, $no_index_spec_1_2); 313} 314 315sub _no_index_directory { 316 my ($element, $key, $meta, $version) = @_; 317 return unless $element; 318 319 # cleanup wrong format 320 if ( ! ref $element ) { 321 my $item = $element; 322 $element = { directory => [ $item ], file => [ $item ] }; 323 } 324 elsif ( ref $element eq 'ARRAY' ) { 325 my $list = $element; 326 $element = { directory => [ @$list ], file => [ @$list ] }; 327 } 328 329 if ( exists $element->{dir} ) { 330 $element->{directory} = delete $element->{dir}; 331 } 332 # common mistake: files -> file 333 if ( exists $element->{files} ) { 334 $element->{file} = delete $element->{file}; 335 } 336 # common mistake: modules -> module 337 if ( exists $element->{modules} ) { 338 $element->{module} = delete $element->{module}; 339 } 340 my $spec = $version == 2 ? $no_index_spec_2 : $no_index_spec_1_3; 341 return _convert($element, $spec); 342} 343 344sub _is_module_name { 345 my $mod = shift; 346 return unless defined $mod && length $mod; 347 return $mod =~ m{^[A-Za-z][A-Za-z0-9_]*(?:::[A-Za-z0-9_]+)*$}; 348} 349 350sub _clean_version { 351 my ($element) = @_; 352 return 0 if ! defined $element; 353 354 $element =~ s{^\s*}{}; 355 $element =~ s{\s*$}{}; 356 $element =~ s{^\.}{0.}; 357 358 return 0 if ! length $element; 359 return 0 if ( $element eq 'undef' || $element eq '<undef>' ); 360 361 my $v = eval { version->new($element) }; 362 # XXX check defined $v and not just $v because version objects leak memory 363 # in boolean context -- dagolden, 2012-02-03 364 if ( defined $v ) { 365 return $v->is_qv ? $v->normal : $element; 366 } 367 else { 368 return 0; 369 } 370} 371 372sub _bad_version_hook { 373 my ($v) = @_; 374 $v =~ s{[a-z]+$}{}; # strip trailing alphabetics 375 my $vobj = eval { version->parse($v) }; 376 return defined($vobj) ? $vobj : version->parse(0); # or give up 377} 378 379sub _version_map { 380 my ($element) = @_; 381 return unless defined $element; 382 if ( ref $element eq 'HASH' ) { 383 # XXX turn this into CPAN::Meta::Requirements with bad version hook 384 # and then turn it back into a hash 385 my $new_map = CPAN::Meta::Requirements->new( 386 { bad_version_hook => \&_bad_version_hook } # punt 387 ); 388 while ( my ($k,$v) = each %$element ) { 389 next unless _is_module_name($k); 390 if ( !defined($v) || !length($v) || $v eq 'undef' || $v eq '<undef>' ) { 391 $v = 0; 392 } 393 # some weird, old META have bad yml with module => module 394 # so check if value is like a module name and not like a version 395 if ( _is_module_name($v) && ! version::is_lax($v) ) { 396 $new_map->add_minimum($k => 0); 397 $new_map->add_minimum($v => 0); 398 } 399 $new_map->add_string_requirement($k => $v); 400 } 401 return $new_map->as_string_hash; 402 } 403 elsif ( ref $element eq 'ARRAY' ) { 404 my $hashref = { map { $_ => 0 } @$element }; 405 return _version_map($hashref); # cleanup any weird stuff 406 } 407 elsif ( ref $element eq '' && length $element ) { 408 return { $element => 0 } 409 } 410 return; 411} 412 413sub _prereqs_from_1 { 414 my (undef, undef, $meta) = @_; 415 my $prereqs = {}; 416 for my $phase ( qw/build configure/ ) { 417 my $key = "${phase}_requires"; 418 $prereqs->{$phase}{requires} = _version_map($meta->{$key}) 419 if $meta->{$key}; 420 } 421 for my $rel ( qw/requires recommends conflicts/ ) { 422 $prereqs->{runtime}{$rel} = _version_map($meta->{$rel}) 423 if $meta->{$rel}; 424 } 425 return $prereqs; 426} 427 428my $prereqs_spec = { 429 configure => \&_prereqs_rel, 430 build => \&_prereqs_rel, 431 test => \&_prereqs_rel, 432 runtime => \&_prereqs_rel, 433 develop => \&_prereqs_rel, 434 ':custom' => \&_prefix_custom, 435}; 436 437my $relation_spec = { 438 requires => \&_version_map, 439 recommends => \&_version_map, 440 suggests => \&_version_map, 441 conflicts => \&_version_map, 442 ':custom' => \&_prefix_custom, 443}; 444 445sub _cleanup_prereqs { 446 my ($prereqs, $key, $meta, $to_version) = @_; 447 return unless $prereqs && ref $prereqs eq 'HASH'; 448 return _convert( $prereqs, $prereqs_spec, $to_version ); 449} 450 451sub _prereqs_rel { 452 my ($relation, $key, $meta, $to_version) = @_; 453 return unless $relation && ref $relation eq 'HASH'; 454 return _convert( $relation, $relation_spec, $to_version ); 455} 456 457 458BEGIN { 459 my @old_prereqs = qw( 460 requires 461 configure_requires 462 recommends 463 conflicts 464 ); 465 466 for ( @old_prereqs ) { 467 my $sub = "_get_$_"; 468 my ($phase,$type) = split qr/_/, $_; 469 if ( ! defined $type ) { 470 $type = $phase; 471 $phase = 'runtime'; 472 } 473 no strict 'refs'; 474 *{$sub} = sub { _extract_prereqs($_[2]->{prereqs},$phase,$type) }; 475 } 476} 477 478sub _get_build_requires { 479 my ($data, $key, $meta) = @_; 480 481 my $test_h = _extract_prereqs($_[2]->{prereqs}, qw(test requires)) || {}; 482 my $build_h = _extract_prereqs($_[2]->{prereqs}, qw(build requires)) || {}; 483 484 my $test_req = CPAN::Meta::Requirements->from_string_hash($test_h); 485 my $build_req = CPAN::Meta::Requirements->from_string_hash($build_h); 486 487 $test_req->add_requirements($build_req)->as_string_hash; 488} 489 490sub _extract_prereqs { 491 my ($prereqs, $phase, $type) = @_; 492 return unless ref $prereqs eq 'HASH'; 493 return scalar _version_map($prereqs->{$phase}{$type}); 494} 495 496sub _downgrade_optional_features { 497 my (undef, undef, $meta) = @_; 498 return unless exists $meta->{optional_features}; 499 my $origin = $meta->{optional_features}; 500 my $features = {}; 501 for my $name ( keys %$origin ) { 502 $features->{$name} = { 503 description => $origin->{$name}{description}, 504 requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','requires'), 505 configure_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','configure_requires'), 506 build_requires => _extract_prereqs($origin->{$name}{prereqs},'runtime','build_requires'), 507 recommends => _extract_prereqs($origin->{$name}{prereqs},'runtime','recommends'), 508 conflicts => _extract_prereqs($origin->{$name}{prereqs},'runtime','conflicts'), 509 }; 510 for my $k (keys %{$features->{$name}} ) { 511 delete $features->{$name}{$k} unless defined $features->{$name}{$k}; 512 } 513 } 514 return $features; 515} 516 517sub _upgrade_optional_features { 518 my (undef, undef, $meta) = @_; 519 return unless exists $meta->{optional_features}; 520 my $origin = $meta->{optional_features}; 521 my $features = {}; 522 for my $name ( keys %$origin ) { 523 $features->{$name} = { 524 description => $origin->{$name}{description}, 525 prereqs => _prereqs_from_1(undef, undef, $origin->{$name}), 526 }; 527 delete $features->{$name}{prereqs}{configure}; 528 } 529 return $features; 530} 531 532my $optional_features_2_spec = { 533 description => \&_keep, 534 prereqs => \&_cleanup_prereqs, 535 ':custom' => \&_prefix_custom, 536}; 537 538sub _feature_2 { 539 my ($element, $key, $meta, $to_version) = @_; 540 return unless $element && ref $element eq 'HASH'; 541 _convert( $element, $optional_features_2_spec, $to_version ); 542} 543 544sub _cleanup_optional_features_2 { 545 my ($element, $key, $meta, $to_version) = @_; 546 return unless $element && ref $element eq 'HASH'; 547 my $new_data = {}; 548 for my $k ( keys %$element ) { 549 $new_data->{$k} = _feature_2( $element->{$k}, $k, $meta, $to_version ); 550 } 551 return unless keys %$new_data; 552 return $new_data; 553} 554 555sub _optional_features_1_4 { 556 my ($element) = @_; 557 return unless $element; 558 $element = _optional_features_as_map($element); 559 for my $name ( keys %$element ) { 560 for my $drop ( qw/requires_packages requires_os excluded_os/ ) { 561 delete $element->{$name}{$drop}; 562 } 563 } 564 return $element; 565} 566 567sub _optional_features_as_map { 568 my ($element) = @_; 569 return unless $element; 570 if ( ref $element eq 'ARRAY' ) { 571 my %map; 572 for my $feature ( @$element ) { 573 my (@parts) = %$feature; 574 $map{$parts[0]} = $parts[1]; 575 } 576 $element = \%map; 577 } 578 return $element; 579} 580 581sub _is_urlish { defined $_[0] && $_[0] =~ m{\A[-+.a-z0-9]+:.+}i } 582 583sub _url_or_drop { 584 my ($element) = @_; 585 return $element if _is_urlish($element); 586 return; 587} 588 589sub _url_list { 590 my ($element) = @_; 591 return unless $element; 592 $element = _listify( $element ); 593 $element = [ grep { _is_urlish($_) } @$element ]; 594 return unless @$element; 595 return $element; 596} 597 598sub _author_list { 599 my ($element) = @_; 600 return [ 'unknown' ] unless $element; 601 $element = _listify( $element ); 602 $element = [ map { defined $_ && length $_ ? $_ : 'unknown' } @$element ]; 603 return [ 'unknown' ] unless @$element; 604 return $element; 605} 606 607my $resource2_upgrade = { 608 license => sub { return _is_urlish($_[0]) ? _listify( $_[0] ) : undef }, 609 homepage => \&_url_or_drop, 610 bugtracker => sub { 611 my ($item) = @_; 612 return unless $item; 613 if ( $item =~ m{^mailto:(.*)$} ) { return { mailto => $1 } } 614 elsif( _is_urlish($item) ) { return { web => $item } } 615 else { return } 616 }, 617 repository => sub { return _is_urlish($_[0]) ? { url => $_[0] } : undef }, 618 ':custom' => \&_prefix_custom, 619}; 620 621sub _upgrade_resources_2 { 622 my (undef, undef, $meta, $version) = @_; 623 return unless exists $meta->{resources}; 624 return _convert($meta->{resources}, $resource2_upgrade); 625} 626 627my $bugtracker2_spec = { 628 web => \&_url_or_drop, 629 mailto => \&_keep, 630 ':custom' => \&_prefix_custom, 631}; 632 633sub _repo_type { 634 my ($element, $key, $meta, $to_version) = @_; 635 return $element if defined $element; 636 return unless exists $meta->{url}; 637 my $repo_url = $meta->{url}; 638 for my $type ( qw/git svn/ ) { 639 return $type if $repo_url =~ m{\A$type}; 640 } 641 return; 642} 643 644my $repository2_spec = { 645 web => \&_url_or_drop, 646 url => \&_url_or_drop, 647 type => \&_repo_type, 648 ':custom' => \&_prefix_custom, 649}; 650 651my $resources2_cleanup = { 652 license => \&_url_list, 653 homepage => \&_url_or_drop, 654 bugtracker => sub { ref $_[0] ? _convert( $_[0], $bugtracker2_spec ) : undef }, 655 repository => sub { my $data = shift; ref $data ? _convert( $data, $repository2_spec ) : undef }, 656 ':custom' => \&_prefix_custom, 657}; 658 659sub _cleanup_resources_2 { 660 my ($resources, $key, $meta, $to_version) = @_; 661 return unless $resources && ref $resources eq 'HASH'; 662 return _convert($resources, $resources2_cleanup, $to_version); 663} 664 665my $resource1_spec = { 666 license => \&_url_or_drop, 667 homepage => \&_url_or_drop, 668 bugtracker => \&_url_or_drop, 669 repository => \&_url_or_drop, 670 ':custom' => \&_keep, 671}; 672 673sub _resources_1_3 { 674 my (undef, undef, $meta, $version) = @_; 675 return unless exists $meta->{resources}; 676 return _convert($meta->{resources}, $resource1_spec); 677} 678 679*_resources_1_4 = *_resources_1_3; 680 681sub _resources_1_2 { 682 my (undef, undef, $meta) = @_; 683 my $resources = $meta->{resources} || {}; 684 if ( $meta->{license_url} && ! $resources->{license} ) { 685 $resources->{license} = $meta->license_url 686 if _is_urlish($meta->{license_url}); 687 } 688 return unless keys %$resources; 689 return _convert($resources, $resource1_spec); 690} 691 692my $resource_downgrade_spec = { 693 license => sub { return ref $_[0] ? $_[0]->[0] : $_[0] }, 694 homepage => \&_url_or_drop, 695 bugtracker => sub { return $_[0]->{web} }, 696 repository => sub { return $_[0]->{url} || $_[0]->{web} }, 697 ':custom' => \&_no_prefix_ucfirst_custom, 698}; 699 700sub _downgrade_resources { 701 my (undef, undef, $meta, $version) = @_; 702 return unless exists $meta->{resources}; 703 return _convert($meta->{resources}, $resource_downgrade_spec); 704} 705 706sub _release_status { 707 my ($element, undef, $meta) = @_; 708 return $element if $element && $element =~ m{\A(?:stable|testing|unstable)\z}; 709 return _release_status_from_version(undef, undef, $meta); 710} 711 712sub _release_status_from_version { 713 my (undef, undef, $meta) = @_; 714 my $version = $meta->{version} || ''; 715 return ( $version =~ /_/ ) ? 'testing' : 'stable'; 716} 717 718my $provides_spec = { 719 file => \&_keep, 720 version => \&_keep, 721}; 722 723my $provides_spec_2 = { 724 file => \&_keep, 725 version => \&_keep, 726 ':custom' => \&_prefix_custom, 727}; 728 729sub _provides { 730 my ($element, $key, $meta, $to_version) = @_; 731 return unless defined $element && ref $element eq 'HASH'; 732 my $spec = $to_version == 2 ? $provides_spec_2 : $provides_spec; 733 my $new_data = {}; 734 for my $k ( keys %$element ) { 735 $new_data->{$k} = _convert($element->{$k}, $spec, $to_version); 736 $new_data->{$k}{version} = _clean_version($element->{$k}{version}) 737 if exists $element->{$k}{version}; 738 } 739 return $new_data; 740} 741 742sub _convert { 743 my ($data, $spec, $to_version) = @_; 744 745 my $new_data = {}; 746 for my $key ( keys %$spec ) { 747 next if $key eq ':custom' || $key eq ':drop'; 748 next unless my $fcn = $spec->{$key}; 749 die "spec for '$key' is not a coderef" 750 unless ref $fcn && ref $fcn eq 'CODE'; 751 my $new_value = $fcn->($data->{$key}, $key, $data, $to_version); 752 $new_data->{$key} = $new_value if defined $new_value; 753 } 754 755 my $drop_list = $spec->{':drop'}; 756 my $customizer = $spec->{':custom'} || \&_keep; 757 758 for my $key ( keys %$data ) { 759 next if $drop_list && grep { $key eq $_ } @$drop_list; 760 next if exists $spec->{$key}; # we handled it 761 $new_data->{ $customizer->($key) } = $data->{$key}; 762 } 763 764 return $new_data; 765} 766 767#--------------------------------------------------------------------------# 768# define converters for each conversion 769#--------------------------------------------------------------------------# 770 771# each converts from prior version 772# special ":custom" field is used for keys not recognized in spec 773my %up_convert = ( 774 '2-from-1.4' => { 775 # PRIOR MANDATORY 776 'abstract' => \&_keep_or_unknown, 777 'author' => \&_author_list, 778 'generated_by' => \&_generated_by, 779 'license' => \&_license_2, 780 'meta-spec' => \&_change_meta_spec, 781 'name' => \&_keep, 782 'version' => \&_keep, 783 # CHANGED TO MANDATORY 784 'dynamic_config' => \&_keep_or_one, 785 # ADDED MANDATORY 786 'release_status' => \&_release_status_from_version, 787 # PRIOR OPTIONAL 788 'keywords' => \&_keep, 789 'no_index' => \&_no_index_directory, 790 'optional_features' => \&_upgrade_optional_features, 791 'provides' => \&_provides, 792 'resources' => \&_upgrade_resources_2, 793 # ADDED OPTIONAL 794 'description' => \&_keep, 795 'prereqs' => \&_prereqs_from_1, 796 797 # drop these deprecated fields, but only after we convert 798 ':drop' => [ qw( 799 build_requires 800 configure_requires 801 conflicts 802 distribution_type 803 license_url 804 private 805 recommends 806 requires 807 ) ], 808 809 # other random keys need x_ prefixing 810 ':custom' => \&_prefix_custom, 811 }, 812 '1.4-from-1.3' => { 813 # PRIOR MANDATORY 814 'abstract' => \&_keep_or_unknown, 815 'author' => \&_author_list, 816 'generated_by' => \&_generated_by, 817 'license' => \&_license_1, 818 'meta-spec' => \&_change_meta_spec, 819 'name' => \&_keep, 820 'version' => \&_keep, 821 # PRIOR OPTIONAL 822 'build_requires' => \&_version_map, 823 'conflicts' => \&_version_map, 824 'distribution_type' => \&_keep, 825 'dynamic_config' => \&_keep_or_one, 826 'keywords' => \&_keep, 827 'no_index' => \&_no_index_directory, 828 'optional_features' => \&_optional_features_1_4, 829 'provides' => \&_provides, 830 'recommends' => \&_version_map, 831 'requires' => \&_version_map, 832 'resources' => \&_resources_1_4, 833 # ADDED OPTIONAL 834 'configure_requires' => \&_keep, 835 836 # drop these deprecated fields, but only after we convert 837 ':drop' => [ qw( 838 license_url 839 private 840 )], 841 842 # other random keys are OK if already valid 843 ':custom' => \&_keep 844 }, 845 '1.3-from-1.2' => { 846 # PRIOR MANDATORY 847 'abstract' => \&_keep_or_unknown, 848 'author' => \&_author_list, 849 'generated_by' => \&_generated_by, 850 'license' => \&_license_1, 851 'meta-spec' => \&_change_meta_spec, 852 'name' => \&_keep, 853 'version' => \&_keep, 854 # PRIOR OPTIONAL 855 'build_requires' => \&_version_map, 856 'conflicts' => \&_version_map, 857 'distribution_type' => \&_keep, 858 'dynamic_config' => \&_keep_or_one, 859 'keywords' => \&_keep, 860 'no_index' => \&_no_index_directory, 861 'optional_features' => \&_optional_features_as_map, 862 'provides' => \&_provides, 863 'recommends' => \&_version_map, 864 'requires' => \&_version_map, 865 'resources' => \&_resources_1_3, 866 867 # drop these deprecated fields, but only after we convert 868 ':drop' => [ qw( 869 license_url 870 private 871 )], 872 873 # other random keys are OK if already valid 874 ':custom' => \&_keep 875 }, 876 '1.2-from-1.1' => { 877 # PRIOR MANDATORY 878 'version' => \&_keep, 879 # CHANGED TO MANDATORY 880 'license' => \&_license_1, 881 'name' => \&_keep, 882 'generated_by' => \&_generated_by, 883 # ADDED MANDATORY 884 'abstract' => \&_keep_or_unknown, 885 'author' => \&_author_list, 886 'meta-spec' => \&_change_meta_spec, 887 # PRIOR OPTIONAL 888 'build_requires' => \&_version_map, 889 'conflicts' => \&_version_map, 890 'distribution_type' => \&_keep, 891 'dynamic_config' => \&_keep_or_one, 892 'recommends' => \&_version_map, 893 'requires' => \&_version_map, 894 # ADDED OPTIONAL 895 'keywords' => \&_keep, 896 'no_index' => \&_no_index_1_2, 897 'optional_features' => \&_optional_features_as_map, 898 'provides' => \&_provides, 899 'resources' => \&_resources_1_2, 900 901 # drop these deprecated fields, but only after we convert 902 ':drop' => [ qw( 903 license_url 904 private 905 )], 906 907 # other random keys are OK if already valid 908 ':custom' => \&_keep 909 }, 910 '1.1-from-1.0' => { 911 # CHANGED TO MANDATORY 912 'version' => \&_keep, 913 # IMPLIED MANDATORY 914 'name' => \&_keep, 915 # PRIOR OPTIONAL 916 'build_requires' => \&_version_map, 917 'conflicts' => \&_version_map, 918 'distribution_type' => \&_keep, 919 'dynamic_config' => \&_keep_or_one, 920 'generated_by' => \&_generated_by, 921 'license' => \&_license_1, 922 'recommends' => \&_version_map, 923 'requires' => \&_version_map, 924 # ADDED OPTIONAL 925 'license_url' => \&_url_or_drop, 926 'private' => \&_keep, 927 928 # other random keys are OK if already valid 929 ':custom' => \&_keep 930 }, 931); 932 933my %down_convert = ( 934 '1.4-from-2' => { 935 # MANDATORY 936 'abstract' => \&_keep_or_unknown, 937 'author' => \&_author_list, 938 'generated_by' => \&_generated_by, 939 'license' => \&_downgrade_license, 940 'meta-spec' => \&_change_meta_spec, 941 'name' => \&_keep, 942 'version' => \&_keep, 943 # OPTIONAL 944 'build_requires' => \&_get_build_requires, 945 'configure_requires' => \&_get_configure_requires, 946 'conflicts' => \&_get_conflicts, 947 'distribution_type' => \&_keep, 948 'dynamic_config' => \&_keep_or_one, 949 'keywords' => \&_keep, 950 'no_index' => \&_no_index_directory, 951 'optional_features' => \&_downgrade_optional_features, 952 'provides' => \&_provides, 953 'recommends' => \&_get_recommends, 954 'requires' => \&_get_requires, 955 'resources' => \&_downgrade_resources, 956 957 # drop these unsupported fields (after conversion) 958 ':drop' => [ qw( 959 description 960 prereqs 961 release_status 962 )], 963 964 # custom keys will be left unchanged 965 ':custom' => \&_keep 966 }, 967 '1.3-from-1.4' => { 968 # MANDATORY 969 'abstract' => \&_keep_or_unknown, 970 'author' => \&_author_list, 971 'generated_by' => \&_generated_by, 972 'license' => \&_license_1, 973 'meta-spec' => \&_change_meta_spec, 974 'name' => \&_keep, 975 'version' => \&_keep, 976 # OPTIONAL 977 'build_requires' => \&_version_map, 978 'conflicts' => \&_version_map, 979 'distribution_type' => \&_keep, 980 'dynamic_config' => \&_keep_or_one, 981 'keywords' => \&_keep, 982 'no_index' => \&_no_index_directory, 983 'optional_features' => \&_optional_features_as_map, 984 'provides' => \&_provides, 985 'recommends' => \&_version_map, 986 'requires' => \&_version_map, 987 'resources' => \&_resources_1_3, 988 989 # drop these unsupported fields, but only after we convert 990 ':drop' => [ qw( 991 configure_requires 992 )], 993 994 # other random keys are OK if already valid 995 ':custom' => \&_keep, 996 }, 997 '1.2-from-1.3' => { 998 # MANDATORY 999 'abstract' => \&_keep_or_unknown, 1000 'author' => \&_author_list, 1001 'generated_by' => \&_generated_by, 1002 'license' => \&_license_1, 1003 'meta-spec' => \&_change_meta_spec, 1004 'name' => \&_keep, 1005 'version' => \&_keep, 1006 # OPTIONAL 1007 'build_requires' => \&_version_map, 1008 'conflicts' => \&_version_map, 1009 'distribution_type' => \&_keep, 1010 'dynamic_config' => \&_keep_or_one, 1011 'keywords' => \&_keep, 1012 'no_index' => \&_no_index_1_2, 1013 'optional_features' => \&_optional_features_as_map, 1014 'provides' => \&_provides, 1015 'recommends' => \&_version_map, 1016 'requires' => \&_version_map, 1017 'resources' => \&_resources_1_3, 1018 1019 # other random keys are OK if already valid 1020 ':custom' => \&_keep, 1021 }, 1022 '1.1-from-1.2' => { 1023 # MANDATORY 1024 'version' => \&_keep, 1025 # IMPLIED MANDATORY 1026 'name' => \&_keep, 1027 'meta-spec' => \&_change_meta_spec, 1028 # OPTIONAL 1029 'build_requires' => \&_version_map, 1030 'conflicts' => \&_version_map, 1031 'distribution_type' => \&_keep, 1032 'dynamic_config' => \&_keep_or_one, 1033 'generated_by' => \&_generated_by, 1034 'license' => \&_license_1, 1035 'private' => \&_keep, 1036 'recommends' => \&_version_map, 1037 'requires' => \&_version_map, 1038 1039 # drop unsupported fields 1040 ':drop' => [ qw( 1041 abstract 1042 author 1043 provides 1044 no_index 1045 keywords 1046 resources 1047 )], 1048 1049 # other random keys are OK if already valid 1050 ':custom' => \&_keep, 1051 }, 1052 '1.0-from-1.1' => { 1053 # IMPLIED MANDATORY 1054 'name' => \&_keep, 1055 'meta-spec' => \&_change_meta_spec, 1056 'version' => \&_keep, 1057 # PRIOR OPTIONAL 1058 'build_requires' => \&_version_map, 1059 'conflicts' => \&_version_map, 1060 'distribution_type' => \&_keep, 1061 'dynamic_config' => \&_keep_or_one, 1062 'generated_by' => \&_generated_by, 1063 'license' => \&_license_1, 1064 'recommends' => \&_version_map, 1065 'requires' => \&_version_map, 1066 1067 # other random keys are OK if already valid 1068 ':custom' => \&_keep, 1069 }, 1070); 1071 1072my %cleanup = ( 1073 '2' => { 1074 # PRIOR MANDATORY 1075 'abstract' => \&_keep_or_unknown, 1076 'author' => \&_author_list, 1077 'generated_by' => \&_generated_by, 1078 'license' => \&_license_2, 1079 'meta-spec' => \&_change_meta_spec, 1080 'name' => \&_keep, 1081 'version' => \&_keep, 1082 # CHANGED TO MANDATORY 1083 'dynamic_config' => \&_keep_or_one, 1084 # ADDED MANDATORY 1085 'release_status' => \&_release_status, 1086 # PRIOR OPTIONAL 1087 'keywords' => \&_keep, 1088 'no_index' => \&_no_index_directory, 1089 'optional_features' => \&_cleanup_optional_features_2, 1090 'provides' => \&_provides, 1091 'resources' => \&_cleanup_resources_2, 1092 # ADDED OPTIONAL 1093 'description' => \&_keep, 1094 'prereqs' => \&_cleanup_prereqs, 1095 1096 # drop these deprecated fields, but only after we convert 1097 ':drop' => [ qw( 1098 build_requires 1099 configure_requires 1100 conflicts 1101 distribution_type 1102 license_url 1103 private 1104 recommends 1105 requires 1106 ) ], 1107 1108 # other random keys need x_ prefixing 1109 ':custom' => \&_prefix_custom, 1110 }, 1111 '1.4' => { 1112 # PRIOR MANDATORY 1113 'abstract' => \&_keep_or_unknown, 1114 'author' => \&_author_list, 1115 'generated_by' => \&_generated_by, 1116 'license' => \&_license_1, 1117 'meta-spec' => \&_change_meta_spec, 1118 'name' => \&_keep, 1119 'version' => \&_keep, 1120 # PRIOR OPTIONAL 1121 'build_requires' => \&_version_map, 1122 'conflicts' => \&_version_map, 1123 'distribution_type' => \&_keep, 1124 'dynamic_config' => \&_keep_or_one, 1125 'keywords' => \&_keep, 1126 'no_index' => \&_no_index_directory, 1127 'optional_features' => \&_optional_features_1_4, 1128 'provides' => \&_provides, 1129 'recommends' => \&_version_map, 1130 'requires' => \&_version_map, 1131 'resources' => \&_resources_1_4, 1132 # ADDED OPTIONAL 1133 'configure_requires' => \&_keep, 1134 1135 # other random keys are OK if already valid 1136 ':custom' => \&_keep 1137 }, 1138 '1.3' => { 1139 # PRIOR MANDATORY 1140 'abstract' => \&_keep_or_unknown, 1141 'author' => \&_author_list, 1142 'generated_by' => \&_generated_by, 1143 'license' => \&_license_1, 1144 'meta-spec' => \&_change_meta_spec, 1145 'name' => \&_keep, 1146 'version' => \&_keep, 1147 # PRIOR OPTIONAL 1148 'build_requires' => \&_version_map, 1149 'conflicts' => \&_version_map, 1150 'distribution_type' => \&_keep, 1151 'dynamic_config' => \&_keep_or_one, 1152 'keywords' => \&_keep, 1153 'no_index' => \&_no_index_directory, 1154 'optional_features' => \&_optional_features_as_map, 1155 'provides' => \&_provides, 1156 'recommends' => \&_version_map, 1157 'requires' => \&_version_map, 1158 'resources' => \&_resources_1_3, 1159 1160 # other random keys are OK if already valid 1161 ':custom' => \&_keep 1162 }, 1163 '1.2' => { 1164 # PRIOR MANDATORY 1165 'version' => \&_keep, 1166 # CHANGED TO MANDATORY 1167 'license' => \&_license_1, 1168 'name' => \&_keep, 1169 'generated_by' => \&_generated_by, 1170 # ADDED MANDATORY 1171 'abstract' => \&_keep_or_unknown, 1172 'author' => \&_author_list, 1173 'meta-spec' => \&_change_meta_spec, 1174 # PRIOR OPTIONAL 1175 'build_requires' => \&_version_map, 1176 'conflicts' => \&_version_map, 1177 'distribution_type' => \&_keep, 1178 'dynamic_config' => \&_keep_or_one, 1179 'recommends' => \&_version_map, 1180 'requires' => \&_version_map, 1181 # ADDED OPTIONAL 1182 'keywords' => \&_keep, 1183 'no_index' => \&_no_index_1_2, 1184 'optional_features' => \&_optional_features_as_map, 1185 'provides' => \&_provides, 1186 'resources' => \&_resources_1_2, 1187 1188 # other random keys are OK if already valid 1189 ':custom' => \&_keep 1190 }, 1191 '1.1' => { 1192 # CHANGED TO MANDATORY 1193 'version' => \&_keep, 1194 # IMPLIED MANDATORY 1195 'name' => \&_keep, 1196 'meta-spec' => \&_change_meta_spec, 1197 # PRIOR OPTIONAL 1198 'build_requires' => \&_version_map, 1199 'conflicts' => \&_version_map, 1200 'distribution_type' => \&_keep, 1201 'dynamic_config' => \&_keep_or_one, 1202 'generated_by' => \&_generated_by, 1203 'license' => \&_license_1, 1204 'recommends' => \&_version_map, 1205 'requires' => \&_version_map, 1206 # ADDED OPTIONAL 1207 'license_url' => \&_url_or_drop, 1208 'private' => \&_keep, 1209 1210 # other random keys are OK if already valid 1211 ':custom' => \&_keep 1212 }, 1213 '1.0' => { 1214 # IMPLIED MANDATORY 1215 'name' => \&_keep, 1216 'meta-spec' => \&_change_meta_spec, 1217 'version' => \&_keep, 1218 # IMPLIED OPTIONAL 1219 'build_requires' => \&_version_map, 1220 'conflicts' => \&_version_map, 1221 'distribution_type' => \&_keep, 1222 'dynamic_config' => \&_keep_or_one, 1223 'generated_by' => \&_generated_by, 1224 'license' => \&_license_1, 1225 'recommends' => \&_version_map, 1226 'requires' => \&_version_map, 1227 1228 # other random keys are OK if already valid 1229 ':custom' => \&_keep, 1230 }, 1231); 1232 1233#--------------------------------------------------------------------------# 1234# Code 1235#--------------------------------------------------------------------------# 1236 1237# =method new 1238# 1239# my $cmc = CPAN::Meta::Converter->new( $struct ); 1240# 1241# The constructor should be passed a valid metadata structure but invalid 1242# structures are accepted. If no meta-spec version is provided, version 1.0 will 1243# be assumed. 1244# 1245# =cut 1246 1247sub new { 1248 my ($class,$data) = @_; 1249 1250 # create an attributes hash 1251 my $self = { 1252 'data' => $data, 1253 'spec' => _extract_spec_version($data), 1254 }; 1255 1256 # create the object 1257 return bless $self, $class; 1258} 1259 1260sub _extract_spec_version { 1261 my ($data) = @_; 1262 my $spec = $data->{'meta-spec'}; 1263 1264 # is meta-spec there and valid? 1265 return "1.0" unless defined $spec && ref $spec eq 'HASH'; # before meta-spec? 1266 1267 # does the version key look like a valid version? 1268 my $v = $spec->{version}; 1269 if ( defined $v && $v =~ /^\d+(?:\.\d+)?$/ ) { 1270 return $v if defined $v && grep { $v eq $_ } keys %known_specs; # known spec 1271 return $v+0 if defined $v && grep { $v == $_ } keys %known_specs; # 2.0 => 2 1272 } 1273 1274 # otherwise, use heuristics: look for 1.x vs 2.0 fields 1275 return "2" if exists $data->{prereqs}; 1276 return "1.4" if exists $data->{configure_requires}; 1277 return "1.2"; # when meta-spec was first defined 1278} 1279 1280# =method convert 1281# 1282# my $new_struct = $cmc->convert( version => "2" ); 1283# 1284# Returns a new hash reference with the metadata converted to a different form. 1285# C<convert> will die if any conversion/standardization still results in an 1286# invalid structure. 1287# 1288# Valid parameters include: 1289# 1290# =over 1291# 1292# =item * 1293# 1294# C<version> -- Indicates the desired specification version (e.g. "1.0", "1.1" ... "1.4", "2"). 1295# Defaults to the latest version of the CPAN Meta Spec. 1296# 1297# =back 1298# 1299# Conversion proceeds through each version in turn. For example, a version 1.2 1300# structure might be converted to 1.3 then 1.4 then finally to version 2. The 1301# conversion process attempts to clean-up simple errors and standardize data. 1302# For example, if C<author> is given as a scalar, it will converted to an array 1303# reference containing the item. (Converting a structure to its own version will 1304# also clean-up and standardize.) 1305# 1306# When data are cleaned and standardized, missing or invalid fields will be 1307# replaced with sensible defaults when possible. This may be lossy or imprecise. 1308# For example, some badly structured META.yml files on CPAN have prerequisite 1309# modules listed as both keys and values: 1310# 1311# requires => { 'Foo::Bar' => 'Bam::Baz' } 1312# 1313# These would be split and each converted to a prerequisite with a minimum 1314# version of zero. 1315# 1316# When some mandatory fields are missing or invalid, the conversion will attempt 1317# to provide a sensible default or will fill them with a value of 'unknown'. For 1318# example a missing or unrecognized C<license> field will result in a C<license> 1319# field of 'unknown'. Fields that may get an 'unknown' include: 1320# 1321# =for :list 1322# * abstract 1323# * author 1324# * license 1325# 1326# =cut 1327 1328sub convert { 1329 my ($self, %args) = @_; 1330 my $args = { %args }; 1331 1332 my $new_version = $args->{version} || $HIGHEST; 1333 1334 my ($old_version) = $self->{spec}; 1335 my $converted = _dclone($self->{data}); 1336 1337 if ( $old_version == $new_version ) { 1338 $converted = _convert( $converted, $cleanup{$old_version}, $old_version ); 1339 my $cmv = CPAN::Meta::Validator->new( $converted ); 1340 unless ( $cmv->is_valid ) { 1341 my $errs = join("\n", $cmv->errors); 1342 die "Failed to clean-up $old_version metadata. Errors:\n$errs\n"; 1343 } 1344 return $converted; 1345 } 1346 elsif ( $old_version > $new_version ) { 1347 my @vers = sort { $b <=> $a } keys %known_specs; 1348 for my $i ( 0 .. $#vers-1 ) { 1349 next if $vers[$i] > $old_version; 1350 last if $vers[$i+1] < $new_version; 1351 my $spec_string = "$vers[$i+1]-from-$vers[$i]"; 1352 $converted = _convert( $converted, $down_convert{$spec_string}, $vers[$i+1] ); 1353 my $cmv = CPAN::Meta::Validator->new( $converted ); 1354 unless ( $cmv->is_valid ) { 1355 my $errs = join("\n", $cmv->errors); 1356 die "Failed to downconvert metadata to $vers[$i+1]. Errors:\n$errs\n"; 1357 } 1358 } 1359 return $converted; 1360 } 1361 else { 1362 my @vers = sort { $a <=> $b } keys %known_specs; 1363 for my $i ( 0 .. $#vers-1 ) { 1364 next if $vers[$i] < $old_version; 1365 last if $vers[$i+1] > $new_version; 1366 my $spec_string = "$vers[$i+1]-from-$vers[$i]"; 1367 $converted = _convert( $converted, $up_convert{$spec_string}, $vers[$i+1] ); 1368 my $cmv = CPAN::Meta::Validator->new( $converted ); 1369 unless ( $cmv->is_valid ) { 1370 my $errs = join("\n", $cmv->errors); 1371 die "Failed to upconvert metadata to $vers[$i+1]. Errors:\n$errs\n"; 1372 } 1373 } 1374 return $converted; 1375 } 1376} 1377 13781; 1379 1380# ABSTRACT: Convert CPAN distribution metadata structures 1381 1382__END__ 1383 1384=pod 1385 1386=encoding UTF-8 1387 1388=head1 NAME 1389 1390CPAN::Meta::Converter - Convert CPAN distribution metadata structures 1391 1392=head1 VERSION 1393 1394version 2.140640 1395 1396=head1 SYNOPSIS 1397 1398 my $struct = decode_json_file('META.json'); 1399 1400 my $cmc = CPAN::Meta::Converter->new( $struct ); 1401 1402 my $new_struct = $cmc->convert( version => "2" ); 1403 1404=head1 DESCRIPTION 1405 1406This module converts CPAN Meta structures from one form to another. The 1407primary use is to convert older structures to the most modern version of 1408the specification, but other transformations may be implemented in the 1409future as needed. (E.g. stripping all custom fields or stripping all 1410optional fields.) 1411 1412=head1 METHODS 1413 1414=head2 new 1415 1416 my $cmc = CPAN::Meta::Converter->new( $struct ); 1417 1418The constructor should be passed a valid metadata structure but invalid 1419structures are accepted. If no meta-spec version is provided, version 1.0 will 1420be assumed. 1421 1422=head2 convert 1423 1424 my $new_struct = $cmc->convert( version => "2" ); 1425 1426Returns a new hash reference with the metadata converted to a different form. 1427C<convert> will die if any conversion/standardization still results in an 1428invalid structure. 1429 1430Valid parameters include: 1431 1432=over 1433 1434=item * 1435 1436C<version> -- Indicates the desired specification version (e.g. "1.0", "1.1" ... "1.4", "2"). 1437Defaults to the latest version of the CPAN Meta Spec. 1438 1439=back 1440 1441Conversion proceeds through each version in turn. For example, a version 1.2 1442structure might be converted to 1.3 then 1.4 then finally to version 2. The 1443conversion process attempts to clean-up simple errors and standardize data. 1444For example, if C<author> is given as a scalar, it will converted to an array 1445reference containing the item. (Converting a structure to its own version will 1446also clean-up and standardize.) 1447 1448When data are cleaned and standardized, missing or invalid fields will be 1449replaced with sensible defaults when possible. This may be lossy or imprecise. 1450For example, some badly structured META.yml files on CPAN have prerequisite 1451modules listed as both keys and values: 1452 1453 requires => { 'Foo::Bar' => 'Bam::Baz' } 1454 1455These would be split and each converted to a prerequisite with a minimum 1456version of zero. 1457 1458When some mandatory fields are missing or invalid, the conversion will attempt 1459to provide a sensible default or will fill them with a value of 'unknown'. For 1460example a missing or unrecognized C<license> field will result in a C<license> 1461field of 'unknown'. Fields that may get an 'unknown' include: 1462 1463=over 4 1464 1465=item * 1466 1467abstract 1468 1469=item * 1470 1471author 1472 1473=item * 1474 1475license 1476 1477=back 1478 1479=head1 BUGS 1480 1481Please report any bugs or feature using the CPAN Request Tracker. 1482Bugs can be submitted through the web interface at 1483L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta> 1484 1485When submitting a bug or request, please include a test-file or a patch to an 1486existing test-file that illustrates the bug or desired feature. 1487 1488=head1 AUTHORS 1489 1490=over 4 1491 1492=item * 1493 1494David Golden <dagolden@cpan.org> 1495 1496=item * 1497 1498Ricardo Signes <rjbs@cpan.org> 1499 1500=back 1501 1502=head1 COPYRIGHT AND LICENSE 1503 1504This software is copyright (c) 2010 by David Golden and Ricardo Signes. 1505 1506This is free software; you can redistribute it and/or modify it under 1507the same terms as the Perl 5 programming language system itself. 1508 1509=cut 1510