1use 5.006; 2use strict; 3use warnings; 4package CPAN::Meta; 5our $VERSION = '2.120921'; # VERSION 6 7 8use Carp qw(carp croak); 9use CPAN::Meta::Feature; 10use CPAN::Meta::Prereqs; 11use CPAN::Meta::Converter; 12use CPAN::Meta::Validator; 13use Parse::CPAN::Meta 1.4403 (); 14 15BEGIN { *_dclone = \&CPAN::Meta::Converter::_dclone } 16 17 18BEGIN { 19 my @STRING_READERS = qw( 20 abstract 21 description 22 dynamic_config 23 generated_by 24 name 25 release_status 26 version 27 ); 28 29 no strict 'refs'; 30 for my $attr (@STRING_READERS) { 31 *$attr = sub { $_[0]{ $attr } }; 32 } 33} 34 35 36BEGIN { 37 my @LIST_READERS = qw( 38 author 39 keywords 40 license 41 ); 42 43 no strict 'refs'; 44 for my $attr (@LIST_READERS) { 45 *$attr = sub { 46 my $value = $_[0]{ $attr }; 47 croak "$attr must be called in list context" 48 unless wantarray; 49 return @{ _dclone($value) } if ref $value; 50 return $value; 51 }; 52 } 53} 54 55sub authors { $_[0]->author } 56sub licenses { $_[0]->license } 57 58 59BEGIN { 60 my @MAP_READERS = qw( 61 meta-spec 62 resources 63 provides 64 no_index 65 66 prereqs 67 optional_features 68 ); 69 70 no strict 'refs'; 71 for my $attr (@MAP_READERS) { 72 (my $subname = $attr) =~ s/-/_/; 73 *$subname = sub { 74 my $value = $_[0]{ $attr }; 75 return _dclone($value) if $value; 76 return {}; 77 }; 78 } 79} 80 81 82sub custom_keys { 83 return grep { /^x_/i } keys %{$_[0]}; 84} 85 86sub custom { 87 my ($self, $attr) = @_; 88 my $value = $self->{$attr}; 89 return _dclone($value) if ref $value; 90 return $value; 91} 92 93 94sub _new { 95 my ($class, $struct, $options) = @_; 96 my $self; 97 98 if ( $options->{lazy_validation} ) { 99 # try to convert to a valid structure; if succeeds, then return it 100 my $cmc = CPAN::Meta::Converter->new( $struct ); 101 $self = $cmc->convert( version => 2 ); # valid or dies 102 return bless $self, $class; 103 } 104 else { 105 # validate original struct 106 my $cmv = CPAN::Meta::Validator->new( $struct ); 107 unless ( $cmv->is_valid) { 108 die "Invalid metadata structure. Errors: " 109 . join(", ", $cmv->errors) . "\n"; 110 } 111 } 112 113 # up-convert older spec versions 114 my $version = $struct->{'meta-spec'}{version} || '1.0'; 115 if ( $version == 2 ) { 116 $self = $struct; 117 } 118 else { 119 my $cmc = CPAN::Meta::Converter->new( $struct ); 120 $self = $cmc->convert( version => 2 ); 121 } 122 123 return bless $self, $class; 124} 125 126sub new { 127 my ($class, $struct, $options) = @_; 128 my $self = eval { $class->_new($struct, $options) }; 129 croak($@) if $@; 130 return $self; 131} 132 133 134sub create { 135 my ($class, $struct, $options) = @_; 136 my $version = __PACKAGE__->VERSION || 2; 137 $struct->{generated_by} ||= __PACKAGE__ . " version $version" ; 138 $struct->{'meta-spec'}{version} ||= int($version); 139 my $self = eval { $class->_new($struct, $options) }; 140 croak ($@) if $@; 141 return $self; 142} 143 144 145sub load_file { 146 my ($class, $file, $options) = @_; 147 $options->{lazy_validation} = 1 unless exists $options->{lazy_validation}; 148 149 croak "load_file() requires a valid, readable filename" 150 unless -r $file; 151 152 my $self; 153 eval { 154 my $struct = Parse::CPAN::Meta->load_file( $file ); 155 $self = $class->_new($struct, $options); 156 }; 157 croak($@) if $@; 158 return $self; 159} 160 161 162sub load_yaml_string { 163 my ($class, $yaml, $options) = @_; 164 $options->{lazy_validation} = 1 unless exists $options->{lazy_validation}; 165 166 my $self; 167 eval { 168 my ($struct) = Parse::CPAN::Meta->load_yaml_string( $yaml ); 169 $self = $class->_new($struct, $options); 170 }; 171 croak($@) if $@; 172 return $self; 173} 174 175 176sub load_json_string { 177 my ($class, $json, $options) = @_; 178 $options->{lazy_validation} = 1 unless exists $options->{lazy_validation}; 179 180 my $self; 181 eval { 182 my $struct = Parse::CPAN::Meta->load_json_string( $json ); 183 $self = $class->_new($struct, $options); 184 }; 185 croak($@) if $@; 186 return $self; 187} 188 189 190sub save { 191 my ($self, $file, $options) = @_; 192 193 my $version = $options->{version} || '2'; 194 my $layer = $] ge '5.008001' ? ':utf8' : ''; 195 196 if ( $version ge '2' ) { 197 carp "'$file' should end in '.json'" 198 unless $file =~ m{\.json$}; 199 } 200 else { 201 carp "'$file' should end in '.yml'" 202 unless $file =~ m{\.yml$}; 203 } 204 205 my $data = $self->as_string( $options ); 206 open my $fh, ">$layer", $file 207 or die "Error opening '$file' for writing: $!\n"; 208 209 print {$fh} $data; 210 close $fh 211 or die "Error closing '$file': $!\n"; 212 213 return 1; 214} 215 216 217sub meta_spec_version { 218 my ($self) = @_; 219 return $self->meta_spec->{version}; 220} 221 222 223sub effective_prereqs { 224 my ($self, $features) = @_; 225 $features ||= []; 226 227 my $prereq = CPAN::Meta::Prereqs->new($self->prereqs); 228 229 return $prereq unless @$features; 230 231 my @other = map {; $self->feature($_)->prereqs } @$features; 232 233 return $prereq->with_merged_prereqs(\@other); 234} 235 236 237sub should_index_file { 238 my ($self, $filename) = @_; 239 240 for my $no_index_file (@{ $self->no_index->{file} || [] }) { 241 return if $filename eq $no_index_file; 242 } 243 244 for my $no_index_dir (@{ $self->no_index->{directory} }) { 245 $no_index_dir =~ s{$}{/} unless $no_index_dir =~ m{/\z}; 246 return if index($filename, $no_index_dir) == 0; 247 } 248 249 return 1; 250} 251 252 253sub should_index_package { 254 my ($self, $package) = @_; 255 256 for my $no_index_pkg (@{ $self->no_index->{package} || [] }) { 257 return if $package eq $no_index_pkg; 258 } 259 260 for my $no_index_ns (@{ $self->no_index->{namespace} }) { 261 return if index($package, "${no_index_ns}::") == 0; 262 } 263 264 return 1; 265} 266 267 268sub features { 269 my ($self) = @_; 270 271 my $opt_f = $self->optional_features; 272 my @features = map {; CPAN::Meta::Feature->new($_ => $opt_f->{ $_ }) } 273 keys %$opt_f; 274 275 return @features; 276} 277 278 279sub feature { 280 my ($self, $ident) = @_; 281 282 croak "no feature named $ident" 283 unless my $f = $self->optional_features->{ $ident }; 284 285 return CPAN::Meta::Feature->new($ident, $f); 286} 287 288 289sub as_struct { 290 my ($self, $options) = @_; 291 my $struct = _dclone($self); 292 if ( $options->{version} ) { 293 my $cmc = CPAN::Meta::Converter->new( $struct ); 294 $struct = $cmc->convert( version => $options->{version} ); 295 } 296 return $struct; 297} 298 299 300sub as_string { 301 my ($self, $options) = @_; 302 303 my $version = $options->{version} || '2'; 304 305 my $struct; 306 if ( $self->meta_spec_version ne $version ) { 307 my $cmc = CPAN::Meta::Converter->new( $self->as_struct ); 308 $struct = $cmc->convert( version => $version ); 309 } 310 else { 311 $struct = $self->as_struct; 312 } 313 314 my ($data, $backend); 315 if ( $version ge '2' ) { 316 $backend = Parse::CPAN::Meta->json_backend(); 317 $data = $backend->new->pretty->canonical->encode($struct); 318 } 319 else { 320 $backend = Parse::CPAN::Meta->yaml_backend(); 321 $data = eval { no strict 'refs'; &{"$backend\::Dump"}($struct) }; 322 if ( $@ ) { 323 croak $backend->can('errstr') ? $backend->errstr : $@ 324 } 325 } 326 327 return $data; 328} 329 330# Used by JSON::PP, etc. for "convert_blessed" 331sub TO_JSON { 332 return { %{ $_[0] } }; 333} 334 3351; 336 337# ABSTRACT: the distribution metadata for a CPAN dist 338 339 340 341=pod 342 343=head1 NAME 344 345CPAN::Meta - the distribution metadata for a CPAN dist 346 347=head1 VERSION 348 349version 2.120921 350 351=head1 SYNOPSIS 352 353 my $meta = CPAN::Meta->load_file('META.json'); 354 355 printf "testing requirements for %s version %s\n", 356 $meta->name, 357 $meta->version; 358 359 my $prereqs = $meta->requirements_for('configure'); 360 361 for my $module ($prereqs->required_modules) { 362 my $version = get_local_version($module); 363 364 die "missing required module $module" unless defined $version; 365 die "version for $module not in range" 366 unless $prereqs->accepts_module($module, $version); 367 } 368 369=head1 DESCRIPTION 370 371Software distributions released to the CPAN include a F<META.json> or, for 372older distributions, F<META.yml>, which describes the distribution, its 373contents, and the requirements for building and installing the distribution. 374The data structure stored in the F<META.json> file is described in 375L<CPAN::Meta::Spec>. 376 377CPAN::Meta provides a simple class to represent this distribution metadata (or 378I<distmeta>), along with some helpful methods for interrogating that data. 379 380The documentation below is only for the methods of the CPAN::Meta object. For 381information on the meaning of individual fields, consult the spec. 382 383=head1 METHODS 384 385=head2 new 386 387 my $meta = CPAN::Meta->new($distmeta_struct, \%options); 388 389Returns a valid CPAN::Meta object or dies if the supplied metadata hash 390reference fails to validate. Older-format metadata will be up-converted to 391version 2 if they validate against the original stated specification. 392 393It takes an optional hashref of options. Valid options include: 394 395=over 396 397=item * 398 399lazy_validation -- if true, new will attempt to convert the given metadata 400to version 2 before attempting to validate it. This means than any 401fixable errors will be handled by CPAN::Meta::Converter before validation. 402(Note that this might result in invalid optional data being silently 403dropped.) The default is false. 404 405=back 406 407=head2 create 408 409 my $meta = CPAN::Meta->create($distmeta_struct, \%options); 410 411This is same as C<new()>, except that C<generated_by> and C<meta-spec> fields 412will be generated if not provided. This means the metadata structure is 413assumed to otherwise follow the latest L<CPAN::Meta::Spec>. 414 415=head2 load_file 416 417 my $meta = CPAN::Meta->load_file($distmeta_file, \%options); 418 419Given a pathname to a file containing metadata, this deserializes the file 420according to its file suffix and constructs a new C<CPAN::Meta> object, just 421like C<new()>. It will die if the deserialized version fails to validate 422against its stated specification version. 423 424It takes the same options as C<new()> but C<lazy_validation> defaults to 425true. 426 427=head2 load_yaml_string 428 429 my $meta = CPAN::Meta->load_yaml_string($yaml, \%options); 430 431This method returns a new CPAN::Meta object using the first document in the 432given YAML string. In other respects it is identical to C<load_file()>. 433 434=head2 load_json_string 435 436 my $meta = CPAN::Meta->load_json_string($json, \%options); 437 438This method returns a new CPAN::Meta object using the structure represented by 439the given JSON string. In other respects it is identical to C<load_file()>. 440 441=head2 save 442 443 $meta->save($distmeta_file, \%options); 444 445Serializes the object as JSON and writes it to the given file. The only valid 446option is C<version>, which defaults to '2'. On Perl 5.8.1 or later, the file 447is saved with UTF-8 encoding. 448 449For C<version> 2 (or higher), the filename should end in '.json'. L<JSON::PP> 450is the default JSON backend. Using another JSON backend requires L<JSON> 2.5 or 451later and you must set the C<$ENV{PERL_JSON_BACKEND}> to a supported alternate 452backend like L<JSON::XS>. 453 454For C<version> less than 2, the filename should end in '.yml'. 455L<CPAN::Meta::Converter> is used to generate an older metadata structure, which 456is serialized to YAML. CPAN::Meta::YAML is the default YAML backend. You may 457set the C<$ENV{PERL_YAML_BACKEND}> to a supported alternative backend, though 458this is not recommended due to subtle incompatibilities between YAML parsers on 459CPAN. 460 461=head2 meta_spec_version 462 463This method returns the version part of the C<meta_spec> entry in the distmeta 464structure. It is equivalent to: 465 466 $meta->meta_spec->{version}; 467 468=head2 effective_prereqs 469 470 my $prereqs = $meta->effective_prereqs; 471 472 my $prereqs = $meta->effective_prereqs( \@feature_identifiers ); 473 474This method returns a L<CPAN::Meta::Prereqs> object describing all the 475prereqs for the distribution. If an arrayref of feature identifiers is given, 476the prereqs for the identified features are merged together with the 477distribution's core prereqs before the CPAN::Meta::Prereqs object is returned. 478 479=head2 should_index_file 480 481 ... if $meta->should_index_file( $filename ); 482 483This method returns true if the given file should be indexed. It decides this 484by checking the C<file> and C<directory> keys in the C<no_index> property of 485the distmeta structure. 486 487C<$filename> should be given in unix format. 488 489=head2 should_index_package 490 491 ... if $meta->should_index_package( $package ); 492 493This method returns true if the given package should be indexed. It decides 494this by checking the C<package> and C<namespace> keys in the C<no_index> 495property of the distmeta structure. 496 497=head2 features 498 499 my @feature_objects = $meta->features; 500 501This method returns a list of L<CPAN::Meta::Feature> objects, one for each 502optional feature described by the distribution's metadata. 503 504=head2 feature 505 506 my $feature_object = $meta->feature( $identifier ); 507 508This method returns a L<CPAN::Meta::Feature> object for the optional feature 509with the given identifier. If no feature with that identifier exists, an 510exception will be raised. 511 512=head2 as_struct 513 514 my $copy = $meta->as_struct( \%options ); 515 516This method returns a deep copy of the object's metadata as an unblessed has 517reference. It takes an optional hashref of options. If the hashref contains 518a C<version> argument, the copied metadata will be converted to the version 519of the specification and returned. For example: 520 521 my $old_spec = $meta->as_struct( {version => "1.4"} ); 522 523=head2 as_string 524 525 my $string = $meta->as_string( \%options ); 526 527This method returns a serialized copy of the object's metadata as a character 528string. (The strings are B<not> UTF-8 encoded.) It takes an optional hashref 529of options. If the hashref contains a C<version> argument, the copied metadata 530will be converted to the version of the specification and returned. For 531example: 532 533 my $string = $meta->as_struct( {version => "1.4"} ); 534 535For C<version> greater than or equal to 2, the string will be serialized as 536JSON. For C<version> less than 2, the string will be serialized as YAML. In 537both cases, the same rules are followed as in the C<save()> method for choosing 538a serialization backend. 539 540=head1 STRING DATA 541 542The following methods return a single value, which is the value for the 543corresponding entry in the distmeta structure. Values should be either undef 544or strings. 545 546=over 4 547 548=item * 549 550abstract 551 552=item * 553 554description 555 556=item * 557 558dynamic_config 559 560=item * 561 562generated_by 563 564=item * 565 566name 567 568=item * 569 570release_status 571 572=item * 573 574version 575 576=back 577 578=head1 LIST DATA 579 580These methods return lists of string values, which might be represented in the 581distmeta structure as arrayrefs or scalars: 582 583=over 4 584 585=item * 586 587authors 588 589=item * 590 591keywords 592 593=item * 594 595licenses 596 597=back 598 599The C<authors> and C<licenses> methods may also be called as C<author> and 600C<license>, respectively, to match the field name in the distmeta structure. 601 602=head1 MAP DATA 603 604These readers return hashrefs of arbitrary unblessed data structures, each 605described more fully in the specification: 606 607=over 4 608 609=item * 610 611meta_spec 612 613=item * 614 615resources 616 617=item * 618 619provides 620 621=item * 622 623no_index 624 625=item * 626 627prereqs 628 629=item * 630 631optional_features 632 633=back 634 635=head1 CUSTOM DATA 636 637A list of custom keys are available from the C<custom_keys> method and 638particular keys may be retrieved with the C<custom> method. 639 640 say $meta->custom($_) for $meta->custom_keys; 641 642If a custom key refers to a data structure, a deep clone is returned. 643 644=for Pod::Coverage TO_JSON abstract author authors custom custom_keys description dynamic_config 645generated_by keywords license licenses meta_spec name no_index 646optional_features prereqs provides release_status resources version 647 648=head1 BUGS 649 650Please report any bugs or feature using the CPAN Request Tracker. 651Bugs can be submitted through the web interface at 652L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta> 653 654When submitting a bug or request, please include a test-file or a patch to an 655existing test-file that illustrates the bug or desired feature. 656 657=head1 SEE ALSO 658 659=over 4 660 661=item * 662 663L<CPAN::Meta::Converter> 664 665=item * 666 667L<CPAN::Meta::Validator> 668 669=back 670 671=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan 672 673=head1 SUPPORT 674 675=head2 Bugs / Feature Requests 676 677Please report any bugs or feature requests through the issue tracker 678at L<http://rt.cpan.org/Public/Dist/Display.html?Name=CPAN-Meta>. 679You will be notified automatically of any progress on your issue. 680 681=head2 Source Code 682 683This is open source software. The code repository is available for 684public review and contribution under the terms of the license. 685 686L<http://github.com/dagolden/cpan-meta> 687 688 git clone git://github.com/dagolden/cpan-meta.git 689 690=head1 AUTHORS 691 692=over 4 693 694=item * 695 696David Golden <dagolden@cpan.org> 697 698=item * 699 700Ricardo Signes <rjbs@cpan.org> 701 702=back 703 704=head1 COPYRIGHT AND LICENSE 705 706This software is copyright (c) 2010 by David Golden and Ricardo Signes. 707 708This is free software; you can redistribute it and/or modify it under 709the same terms as the Perl 5 programming language system itself. 710 711=cut 712 713 714__END__ 715 716 717