File Coverage

blib/lib/Module/Format/Module.pm
Criterion Covered Total %
statement 78 85 91.7
branch 12 18 66.6
condition 2 2 100.0
subroutine 21 21 100.0
pod 7 7 100.0
total 120 133 90.2


line stmt bran cond sub pod time code
1             package Module::Format::Module;
2             $Module::Format::Module::VERSION = '0.2.0';
3 4     4   69210 use warnings;
  4         14  
  4         96  
4 4     4   13 use strict;
  4         6  
  4         5624  
5              
6              
7             sub _new
8             {
9 22     22   32 my $class = shift;
10 22         34 my $self = bless {}, $class;
11 22         60 $self->_init(@_);
12 22         73 return $self;
13             }
14              
15             sub _components
16             {
17 83     83   99 my $self = shift;
18              
19 83 100       158 if (@_)
20             {
21 22         33 $self->{_components} = shift;
22             }
23 83         360 return $self->{_components};
24             }
25              
26             sub _init
27             {
28 22     22   31 my ( $self, $args ) = @_;
29              
30 22         27 $self->_components( [ @{ $args->{_components} } ] );
  22         59  
31              
32 22         24 return;
33             }
34              
35              
36             sub from_components
37             {
38 4     4 1 1014 my ( $class, $args ) = @_;
39              
40 4         6 return $class->_new( { _components => [ @{ $args->{components} } ] } );
  4         10  
41             }
42              
43              
44             my $dash_re = qr{(?:\w+-)*\w+};
45             my $colon_re = qr{(?:\w+::)*\w+};
46             my $METACPAN_REL = 'https://metacpan.org/release/';
47              
48             sub _gen_dash_format
49             {
50 8     8   22 my ($args) = @_;
51              
52 8         12 my $prefix = $args->{prefix};
53 8         12 my $name = $args->{name};
54              
55             return +{
56             name => $name,
57             regex => qr{\A\Q$prefix\E$dash_re\z},
58             input => sub {
59 3     3   7 my ( $class, $value ) = @_;
60              
61 3 50       38 if ( $value !~ s{\A\Q$prefix\E}{} )
62             {
63 0         0 die "$name value does not start with the '$prefix' prefix.";
64             }
65              
66 3         12 return $class->_calc_components_from_string(
67             { format => 'dash', value => $value } );
68             },
69             format => sub {
70 8     8   19 my ($self) = @_;
71              
72 8         25 return $prefix . $self->format_as('dash');
73             },
74 8         160 };
75             }
76              
77             sub _gen_colon_format
78             {
79 8     8   19 my ($args) = @_;
80              
81 8         13 my $prefix = $args->{prefix};
82 8         11 my $suffix = $args->{suffix};
83 8         10 my $name = $args->{name};
84              
85             return +{
86             name => $name,
87             regex => qr{\A\Q$prefix\E$colon_re\Q$suffix\E\z},
88             input => sub {
89 4     4   10 my ( $class, $value ) = @_;
90              
91 4 50       88 if ( $value !~ m{\A\Q$prefix\E((?:\w+::)*\w+)\Q$suffix\E\z} )
92             {
93 0         0 die "Improper value for $name";
94             }
95              
96 4         29 return $class->_calc_components_from_string(
97             { format => 'colon', value => $1 } );
98             },
99             format => sub {
100 5     5   8 my ($self) = @_;
101              
102 5         8 return $prefix . $self->format_as('colon') . $suffix;
103             },
104 8         389 };
105             }
106              
107             my @formats_by_priority = (
108             _gen_dash_format(
109             {
110             name => 'rpm_dash',
111             prefix => 'perl-',
112             }
113             ),
114             _gen_colon_format(
115             {
116             name => 'rpm_colon',
117             prefix => 'perl(',
118             suffix => ')',
119             }
120             ),
121             {
122             name => 'colon',
123             regex => qr{\A$colon_re\z},
124             input => sub {
125             my ( $class, $value ) = @_;
126             return [ split( /::/, $value, -1 ) ];
127             },
128             format => sub {
129             my ($self) = @_;
130              
131             return join( '::', @{ $self->_components() } );
132             },
133             },
134             {
135             name => 'dash',
136             regex => qr{\A$dash_re\z},
137             input => sub {
138             my ( $class, $value ) = @_;
139             return [ split( /-/, $value, -1 ) ];
140             },
141             format => sub {
142             my ($self) = @_;
143              
144             return join( '-', @{ $self->_components() } );
145             },
146             },
147             {
148             name => 'unix',
149             regex => qr{\A(?:\w+/)*\w+\.pm\z},
150             input => sub {
151             my ( $class, $value ) = @_;
152              
153             if ( $value !~ s{\.pm\z}{} )
154             {
155             die "Cannot find a .pm suffix in the 'unix' format.";
156             }
157              
158             return [ split( m{/}, $value, -1 ) ];
159             },
160             format => sub {
161             my ($self) = @_;
162              
163             return join( '/', @{ $self->_components() } ) . '.pm';
164             },
165             },
166             _gen_dash_format(
167             {
168             name => 'metacpan_rel',
169             prefix => $METACPAN_REL,
170             }
171             ),
172             _gen_colon_format(
173             {
174             name => 'metacpan_pod',
175             prefix => 'https://metacpan.org/pod/',
176             suffix => '',
177             }
178             ),
179             {
180             name => 'debian',
181             format => sub {
182             my ($self) = @_;
183              
184             return 'lib' . lc( $self->format_as('dash') ) . '-perl';
185             },
186             },
187             );
188              
189             my %formats = ( map { $_->{name} => $_ } @formats_by_priority );
190              
191             sub _calc_components_from_string
192             {
193 25     25   40 my ( $class, $args ) = @_;
194              
195 25         29 my $format = $args->{format};
196 25         29 my $value = $args->{value};
197              
198 25 50       48 if ( exists( $formats{$format} ) )
199             {
200 25 50       48 if ( my $handler = $formats{$format}->{'input'} )
201             {
202 25         44 return $class->$handler($value);
203             }
204             else
205             {
206 0         0 die "Format $format is output-only!";
207             }
208             }
209             else
210             {
211 0         0 die "Unknown format '$format'!";
212             }
213             }
214              
215             sub from
216             {
217 18     18 1 2002 my ( $class, $args ) = @_;
218              
219 18         28 my $format = $args->{format};
220 18         21 my $value = $args->{value};
221              
222 18         36 return $class->_new(
223             {
224             _components => $class->_calc_components_from_string($args)
225             }
226             );
227             }
228              
229              
230             sub get_components_list
231             {
232 20     20 1 4841 my $self = shift;
233              
234 20         31 return [ @{ $self->_components() } ];
  20         34  
235             }
236              
237              
238             sub format_as
239             {
240 51     51 1 97 my ( $self, $format ) = @_;
241              
242 51 50       98 if ( exists( $formats{$format} ) )
243             {
244 51         80 my $handler = $formats{$format}->{'format'};
245 51         83 return $self->$handler();
246             }
247             else
248             {
249 0         0 die "Unknown format '$format';";
250             }
251              
252 0         0 return;
253             }
254              
255              
256             sub clone
257             {
258 1     1 1 211 my $self = shift;
259              
260             return
261 1         3 ref($self)
262             ->from_components( { components => $self->get_components_list() } );
263             }
264              
265              
266             sub _all
267             {
268 4     4   7 my ( $cb, $l ) = @_;
269              
270 4         9 foreach (@$l)
271             {
272 9 100       17 if ( not $cb->($_) )
273             {
274 1         3 return;
275             }
276             }
277              
278 3         12 return 1;
279             }
280              
281             sub is_sane
282             {
283 4     4 1 10 my $self = shift;
284              
285 4     9   22 return _all( sub { m{\A\w+\z}; }, $self->_components() );
  9         37  
286             }
287              
288              
289             sub from_guess
290             {
291 12     12 1 3149 my ( $class, $args ) = @_;
292              
293 12         15 my $dummy_format_string;
294              
295 12         20 my $string = $args->{value};
296 12   100     51 my $out_format_ref = ( $args->{format_ref} || ( \$dummy_format_string ) );
297              
298             # TODO : After the previous line the indentation in vim is reset to the
299             # first column.
300              
301 12         23 foreach my $format_record (@formats_by_priority)
302             {
303 44 50       81 if ( my $regex = $format_record->{regex} )
304             {
305 44 100       173 if ( $string =~ $regex )
306             {
307 12         20 my $format_id = $format_record->{name};
308              
309 12         12 ${$out_format_ref} = $format_id;
  12         17  
310              
311 12         36 return $class->from(
312             { value => $string, format => $format_id, } );
313             }
314             }
315             }
316              
317 0           die "Could not guess the format of the value '$string'!";
318             }
319              
320              
321             1; # End of Module::Format::Module
322              
323             __END__