| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package CPAN::Changes; | 
| 2 | 30 |  |  | 30 |  | 1694367 | use strict; | 
|  | 30 |  |  |  |  | 270 |  | 
|  | 30 |  |  |  |  | 887 |  | 
| 3 | 30 |  |  | 30 |  | 178 | use warnings; | 
|  | 30 |  |  |  |  | 72 |  | 
|  | 30 |  |  |  |  | 1918 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | our $VERSION = '0.500_001'; | 
| 6 |  |  |  |  |  |  | $VERSION =~ tr/_//d; | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 30 |  |  | 30 |  | 13901 | use Sub::Quote qw(qsub); | 
|  | 30 |  |  |  |  | 126215 |  | 
|  | 30 |  |  |  |  | 1777 |  | 
| 9 | 30 |  |  | 30 |  | 19159 | use Types::Standard qw(ArrayRef HashRef InstanceOf); | 
|  | 30 |  |  |  |  | 2565903 |  | 
|  | 30 |  |  |  |  | 287 |  | 
| 10 | 30 |  |  | 30 |  | 44896 | use CPAN::Changes::Release; | 
|  | 30 |  |  |  |  | 107 |  | 
|  | 30 |  |  |  |  | 1014 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 30 |  |  | 30 |  | 202 | use Moo; | 
|  | 30 |  |  |  |  | 70 |  | 
|  | 30 |  |  |  |  | 122 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | my $release_type = (InstanceOf['CPAN::Changes::Release'])->plus_coercions( | 
| 15 |  |  |  |  |  |  | HashRef ,=> qsub q{ CPAN::Changes::Release->new($_[0]) }, | 
| 16 |  |  |  |  |  |  | ); | 
| 17 |  |  |  |  |  |  | has _releases => ( | 
| 18 |  |  |  |  |  |  | is => 'rw', | 
| 19 |  |  |  |  |  |  | init_arg => 'releases', | 
| 20 |  |  |  |  |  |  | isa => ArrayRef[$release_type], | 
| 21 |  |  |  |  |  |  | coerce => 1, | 
| 22 |  |  |  |  |  |  | default => qsub q{ [] }, | 
| 23 |  |  |  |  |  |  | ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | has preamble => ( | 
| 26 |  |  |  |  |  |  | is => 'rw', | 
| 27 |  |  |  |  |  |  | default => '', | 
| 28 |  |  |  |  |  |  | ); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | sub clone { | 
| 31 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 32 | 0 |  |  |  |  | 0 | my %attrs = %$self; | 
| 33 | 0 |  |  |  |  | 0 | $attrs{releases} = [ map $_->clone, @{delete $self->{_releases}} ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 34 | 0 |  |  |  |  | 0 | return (ref $self)->new(%attrs, @_); | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # backcompat | 
| 38 |  |  |  |  |  |  | sub releases { | 
| 39 | 37 |  |  | 37 | 1 | 86560 | my ($self, @args) = @_; | 
| 40 | 37 | 100 | 66 |  |  | 285 | if (@args > 1 or @args == 1 && ref $args[0] ne 'ARRAY') { | 
|  |  |  | 66 |  |  |  |  | 
| 41 | 3 |  |  |  |  | 8 | @args = [ @args ]; | 
| 42 |  |  |  |  |  |  | } | 
| 43 | 37 |  |  |  |  | 76 | @{ $self->_releases(@args) }; | 
|  | 37 |  |  |  |  | 888 |  | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub _numify_version { | 
| 47 | 714 |  |  | 714 |  | 1240 | my $version = shift; | 
| 48 | 714 |  |  |  |  | 1194 | $version = _fix_version($version); | 
| 49 | 714 |  |  |  |  | 1540 | $version =~ s/_//g; | 
| 50 | 714 | 100 | 66 |  |  | 2885 | if ($version =~ s/^v//i || $version =~ tr/.// > 1) { | 
| 51 | 3 |  |  |  |  | 21 | my @parts = split /\./, $version; | 
| 52 | 3 |  |  |  |  | 9 | my $n = shift @parts; | 
| 53 | 3 |  |  |  |  | 33 | $version = sprintf(join('.', '%s', ('%03s' x @parts)), $n, @parts); | 
| 54 |  |  |  |  |  |  | } | 
| 55 | 714 |  |  |  |  | 1752 | $version += 0; | 
| 56 | 714 |  |  |  |  | 1881 | return $version; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | sub _fix_version { | 
| 60 | 714 |  |  | 714 |  | 1307 | my $version = shift; | 
| 61 | 714 | 50 |  |  |  | 1433 | return 0 unless defined $version; | 
| 62 | 714 |  |  |  |  | 1599 | my $v = ($version =~ s/^v//i); | 
| 63 | 714 |  |  |  |  | 1358 | $version =~ s/[^\d\._].*//; | 
| 64 | 714 |  |  |  |  | 1761 | $version =~ s/\.[._]+/./; | 
| 65 | 714 |  |  |  |  | 1606 | $version =~ s/[._]*_[._]*/_/g; | 
| 66 | 714 |  |  |  |  | 1206 | $version =~ s/\.{2,}/./g; | 
| 67 | 714 |  | 100 |  |  | 3060 | $v ||= $version =~ tr/.// > 1; | 
| 68 | 714 |  | 50 |  |  | 1427 | $version ||= 0; | 
| 69 | 714 | 100 |  |  |  | 1953 | return (($v ? 'v' : '') . $version); | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | sub find_release { | 
| 73 | 14 |  |  | 14 | 1 | 2486 | my ($self, $version) = @_; | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 14 |  |  |  |  | 29 | my ($release) = grep { $_->version eq $version } @{ $self->_releases }; | 
|  | 805 |  |  |  |  | 2448 |  | 
|  | 14 |  |  |  |  | 371 |  | 
| 76 | 14 | 100 |  |  |  | 72 | return $release | 
| 77 |  |  |  |  |  |  | if $release; | 
| 78 | 7 |  | 50 |  |  | 30 | $version = _numify_version($version) || return undef; | 
| 79 | 7 |  |  |  |  | 13 | ($release) = grep { _numify_version($_->version) == $version } @{ $self->_releases }; | 
|  | 685 |  |  |  |  | 1677 |  | 
|  | 7 |  |  |  |  | 187 |  | 
| 80 | 7 |  |  |  |  | 58 | return $release; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | sub reversed { | 
| 84 | 0 |  |  | 0 | 1 | 0 | my ($self) = @_; | 
| 85 | 0 |  |  |  |  | 0 | return $self->clone(releases => [ reverse @{ $self->_releases } ]); | 
|  | 0 |  |  |  |  | 0 |  | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | sub serialize { | 
| 89 | 16 |  |  | 16 | 1 | 12326 | my ($self, %opts) = @_; | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 16 | 50 |  |  |  | 56 | if ($opts{reverse}) { | 
| 92 | 0 |  |  |  |  | 0 | $self = $self->reversed; | 
| 93 |  |  |  |  |  |  | } | 
| 94 | 16 |  | 50 |  |  | 97 | my $width = $opts{width} || 75; | 
| 95 | 16 | 50 |  |  |  | 28 | my @styles = @{ $opts{styles} || ['', '[]', '-', '*'] }; | 
|  | 16 |  |  |  |  | 124 |  | 
| 96 | 16 | 50 |  |  |  | 52 | my @indents = @{ $opts{indents} || ['', ' ', ''] }; | 
|  | 16 |  |  |  |  | 74 |  | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 16 |  | 100 |  |  | 86 | my $out = $self->preamble || ''; | 
| 99 | 16 | 100 |  |  |  | 47 | $out .= "\n\n" | 
| 100 |  |  |  |  |  |  | if $out; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 16 |  |  |  |  | 28 | for my $release (reverse @{$self->_releases}) { | 
|  | 16 |  |  |  |  | 318 |  | 
| 103 | 19 |  |  |  |  | 148 | my $styles = \@styles; | 
| 104 | 19 |  |  |  |  | 33 | my $indents = \@indents; | 
| 105 | 19 | 100 | 100 |  |  | 31 | if ( | 
| 106 |  |  |  |  |  |  | grep { | 
| 107 | 36 | 50 |  |  |  | 373 | length($styles->[1]) > 1 | 
| 108 |  |  |  |  |  |  | && length($indents->[0] . $styles->[1] . $_->text) > $width | 
| 109 |  |  |  |  |  |  | } | 
| 110 | 19 |  |  |  |  | 368 | @{ $release->entries } | 
| 111 |  |  |  |  |  |  | or | 
| 112 | 24 |  |  |  |  | 203 | !grep { $_->has_entries } | 
| 113 | 16 |  |  |  |  | 261 | @{ $release->entries } | 
| 114 |  |  |  |  |  |  | ) { | 
| 115 | 9 |  |  |  |  | 68 | $styles = [ '', '-', '*' ]; | 
| 116 |  |  |  |  |  |  | } | 
| 117 | 19 | 100 | 100 |  |  | 201 | $out .= "\n" | 
| 118 |  |  |  |  |  |  | unless $out eq '' || $out =~ /\n\n\z/; | 
| 119 | 19 |  |  |  |  | 449 | $out .= $release->serialize( | 
| 120 |  |  |  |  |  |  | %opts, | 
| 121 |  |  |  |  |  |  | indents => $indents, | 
| 122 |  |  |  |  |  |  | styles => $styles, | 
| 123 |  |  |  |  |  |  | width => $width - length $indents->[0], | 
| 124 |  |  |  |  |  |  | ); | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 16 |  |  |  |  | 96 | return $out; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | require CPAN::Changes::Parser; | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | # :( i know people use these | 
| 132 |  |  |  |  |  |  | our $W3CDTF_REGEX = $CPAN::Changes::Parser::_ISO_8601_DATE; | 
| 133 |  |  |  |  |  |  | our $UNKNOWN_VALS = $CPAN::Changes::Parser::_UNKNOWN_DATE; | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub load { | 
| 136 | 18 |  |  | 18 | 1 | 2652 | my ($class, $filename, %args) = @_; | 
| 137 |  |  |  |  |  |  | $args{version_like} = $args{next_token} | 
| 138 | 18 | 100 |  |  |  | 89 | if exists $args{next_token}; | 
| 139 | 18 |  |  |  |  | 142 | require CPAN::Changes::Parser; | 
| 140 | 18 |  |  |  |  | 208 | CPAN::Changes::Parser->new(%args)->parse_file($filename); | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub load_string { | 
| 144 | 9 |  |  | 9 | 1 | 6237 | my ($class, $string, %args) = @_; | 
| 145 |  |  |  |  |  |  | $args{version_like} = $args{next_token} | 
| 146 | 9 | 50 |  |  |  | 37 | if exists $args{next_token}; | 
| 147 | 9 |  |  |  |  | 87 | require CPAN::Changes::Parser; | 
| 148 | 9 |  |  |  |  | 146 | CPAN::Changes::Parser->new(%args)->parse_string($string); | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | sub add_release { | 
| 152 | 11 |  |  | 11 | 1 | 13350 | my ($self, @new_releases) = @_; | 
| 153 | 11 |  |  |  |  | 28 | @new_releases = map { $release_type->coerce($_) } @new_releases; | 
|  | 11 |  |  |  |  | 37 |  | 
| 154 | 11 |  |  |  |  | 2427 | my @releases = @{ $self->_releases }; | 
|  | 11 |  |  |  |  | 203 |  | 
| 155 | 11 |  |  |  |  | 97 | for my $new_release (@new_releases) { | 
| 156 | 11 |  |  |  |  | 47 | my $version = _numify_version($new_release->version); | 
| 157 | 11 |  |  |  |  | 28 | for my $release (@releases) { | 
| 158 | 11 | 100 |  |  |  | 31 | if (_numify_version($release->version) == $version) { | 
| 159 | 1 |  |  |  |  | 2 | $release = $new_release; | 
| 160 | 1 |  |  |  |  | 3 | undef $new_release; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  | } | 
| 164 | 11 |  |  |  |  | 27 | push @releases, grep { defined } @new_releases; | 
|  | 11 |  |  |  |  | 34 |  | 
| 165 | 11 |  |  |  |  | 209 | $self->_releases(\@releases); | 
| 166 | 11 |  |  |  |  | 1010 | return 1; | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | sub delete_release { | 
| 170 | 0 |  |  | 0 | 1 | 0 | my ($self, @versions) = @_; | 
| 171 | 0 |  |  |  |  | 0 | my @releases = @{ $self->_releases }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 172 | 0 |  |  |  |  | 0 | for my $version (map { _numify_version($_) } @versions) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 173 | 0 |  |  |  |  | 0 | @releases = grep { _numify_version($_->version) != $version } @releases; | 
|  | 0 |  |  |  |  | 0 |  | 
| 174 |  |  |  |  |  |  | } | 
| 175 | 0 |  |  |  |  | 0 | $self->_releases(\@releases); | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | sub release { | 
| 179 | 7 |  |  | 7 | 1 | 609 | my ($self, $version) = @_; | 
| 180 | 7 |  |  |  |  | 22 | $self->find_release($version); | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | sub delete_empty_groups { | 
| 184 | 2 |  |  | 2 | 1 | 3213 | my ($self) = @_; | 
| 185 | 2 |  |  |  |  | 4 | for my $release ( @{ $self->_releases } ) { | 
|  | 2 |  |  |  |  | 38 |  | 
| 186 | 4 |  |  |  |  | 137 | $release->delete_empty_groups; | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | 1; | 
| 191 |  |  |  |  |  |  | __END__ |