File Coverage

blib/lib/Module/Changes/ADAMK.pm
Criterion Covered Total %
statement 78 87 89.6
branch 9 24 37.5
condition n/a
subroutine 19 20 95.0
pod 0 8 0.0
total 106 139 76.2


line stmt bran cond sub pod time code
1             package Module::Changes::ADAMK;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Module::Changes::ADAMK - Parse a traditional Changes file (as ADAMK interpretes it)
8              
9             =head1 SYNOPSIS
10              
11             my $changes = Module::Changes::ADAMK->read('Changes');
12             my $latest = $changes->current;
13             my $datetime = $changes->datetime; # DateTime object
14              
15             =head1 DESCRIPTION
16              
17             This module was written for parsing ADAMK's Changes files (which are a pretty
18             traditional format that might be of us to others).
19              
20             It is provided to the CPAN community for discussion and testing purposes.
21              
22             It is currently not documented in detail, see the source code for the API.
23              
24             =cut
25              
26 5     5   112625 use 5.006;
  5         21  
  5         186  
27 5     5   28 use strict;
  5         8  
  5         175  
28 5     5   35 use warnings;
  5         10  
  5         246  
29 5     5   28 use Carp 'croak';
  5         8  
  5         362  
30 5     5   7129 use DateTime 0.4501 ();
  5         1391620  
  5         224  
31 5     5   22994 use DateTime::Format::CLDR 1.06 ();
  5         56922  
  5         264  
32 5     5   8223 use DateTime::Format::DateParse 0.04 ();
  5         76801  
  5         146  
33              
34 5     5   47 use vars qw{$VERSION};
  5         13  
  5         239  
35             BEGIN {
36 5     5   82 $VERSION = '0.11';
37             }
38              
39 5     5   15393 use Module::Changes::ADAMK::Release ();
  5         15  
  5         111  
40 5     5   34 use Module::Changes::ADAMK::Change ();
  5         12  
  5         130  
41              
42 5         32 use Object::Tiny 1.03 qw{
43             file
44             string
45             header
46             dist_name
47             module_name
48 5     5   26 };
  5         103  
49              
50              
51              
52              
53              
54             #####################################################################
55             # Constructor and Accessors
56              
57             sub read {
58 4     4 0 3585 my $class = shift;
59              
60             # Check the file
61 4 50       20 my $file = shift or croak('You did not specify a file name');
62 4 50       92 croak("File '$file' does not exist") unless -e $file;
63 4 50       22 croak("'$file' is a directory, not a file") unless -f _;
64 4 50       28 croak("Insufficient permissions to read '$file'") unless -r _;
65              
66             # Slurp in the file
67 4         17 local $/ = undef;
68 4 50       151 open CFG, $file or croak("Failed to open file '$file': $!");
69 4         103777 my $contents = ;
70 4         110 close CFG;
71              
72             # Hand off to the actual parser
73 4         57 my $self = $class->read_string( $contents );
74              
75             # Keep the file name so we can save later
76 4         13 $self->{file} = $file;
77 4         34 return $self;
78             }
79              
80             sub read_string {
81 5     5 0 27 my $class = shift;
82              
83             # Normalize newlines
84 5         13 my $string = shift;
85 5 50       33 return undef unless defined $string;
86 5         2402 $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/gs;
87              
88             # Create the unpopulated object
89 5         78 my $self = $class->new(
90             string => $string,
91             );
92              
93             # Split into paragraphs
94 5         375 my @paragraphs = split /\n{2,}(?=[^ \t])/, $string;
95 5         22 foreach ( @paragraphs ) {
96 91         122 s/\n\z//;
97             }
98              
99             # The first paragraph contains the name of the module, which
100             # should be the last word.
101 5         44 $self->{header} = shift @paragraphs;
102 5         73 my @header_words = $self->{header} =~ /([\w:-]+)/g;
103 5 50       25 unless ( @header_words ) {
104 0         0 croak("Failed to find any words in the header");
105             }
106 5         14 my $name = $header_words[-1];
107 5         13 $self->{dist_name} = $name;
108 5         12 $self->{module_name} = $name;
109 5 50       28 if ( $name =~ /-/ ) {
    0          
110 5         23 $self->{module_name} =~ s/-/::/g;
111             } elsif ( $name =~ /::/ ) {
112 0         0 $self->{dist_name} =~ s/::/-/g;
113             }
114              
115             # Parse each paragraph into a release
116 5         10 my @releases = ();
117 5         12 foreach my $paragraph ( @paragraphs ) {
118 86 50       320 next unless $paragraph =~ /\S/;
119 86         359 push @releases, Module::Changes::ADAMK::Release->new($paragraph);
120             }
121 5         20 $self->{releases} = \@releases;
122              
123 5         76 return $self;
124             }
125              
126             sub releases {
127 6     6 0 4990 return @{$_[0]->{releases}};
  6         29  
128             }
129              
130             sub save {
131 0     0 0 0 my $self = shift;
132 0 0       0 unless ( $self->{file} ) {
133 0         0 die("Tried to save Changes file without a file name");
134             }
135              
136             # Generate and write
137 0 0       0 open( CFG, '>', $self->{file} ) or die "open: $!";
138 0         0 print CFG $self->as_string;
139 0         0 close CFG;
140              
141 0         0 return 1;
142             }
143              
144              
145              
146              
147              
148             #####################################################################
149             # Main Methods
150              
151             sub current {
152 9     9 0 4448 $_[0]->{releases}->[0];
153             }
154              
155             sub current_version {
156 2     2 0 8 $_[0]->current->version;
157             }
158              
159              
160              
161              
162              
163             #####################################################################
164             # Stringification
165              
166             sub as_string {
167 3     3 0 535 my $self = shift;
168 24         66 my @parts = (
169             $self->header,
170 3         72 map { $_->as_string } $self->releases,
171             );
172 3         10 return join "\n", map { "$_\n" } @parts;
  27         109  
173             }
174              
175             sub roundtrips {
176 1     1 0 216 $_[0]->string eq $_[0]->as_string
177             }
178              
179             1;
180              
181             =pod
182              
183             =head1 SUPPORT
184              
185             Bugs should be reported via the CPAN bug tracker at
186              
187             L
188              
189             =head1 AUTHOR
190              
191             Adam Kennedy
192              
193             =head1 COPYRIGHT
194              
195             Copyright 2007 - 2009 Adam Kennedy.
196              
197             This program is free software; you can redistribute
198             it and/or modify it under the same terms as Perl itself.
199              
200             The full text of the license can be found in the
201             LICENSE file included with this module.
202              
203             =cut