File Coverage

blib/lib/Config/MVP/Writer/INI.pm
Criterion Covered Total %
statement 62 73 84.9
branch 26 32 81.2
condition 5 8 62.5
subroutine 10 11 90.9
pod 1 1 100.0
total 104 125 83.2


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of Config-MVP-Writer-INI
4             #
5             # This software is copyright (c) 2012 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 3     3   172481 use strict;
  3         11  
  3         120  
11 3     3   21 use warnings;
  3         7  
  3         207  
12              
13             package Config::MVP::Writer::INI;
14             # git description: v0.004-1-g89f27f0
15              
16             our $AUTHORITY = 'cpan:RWSTAUNER';
17             # ABSTRACT: Build an INI file for Config::MVP
18             $Config::MVP::Writer::INI::VERSION = '0.005';
19 3     3   18 use Moose;
  3         5  
  3         26  
20 3     3   22390 use Moose::Util::TypeConstraints;
  3         8  
  3         28  
21 3     3   7282 use List::Util ();
  3         9  
  3         2798  
22              
23              
24             has spacing => (
25             is => 'ro',
26             isa => enum([qw( none all payload )]),
27             default => 'payload',
28             );
29              
30              
31             has strip_bundle_prefix => (
32             is => 'ro',
33             isa => 'Bool',
34             default => 1,
35             );
36              
37              
38              
39             has _rewrite_package => (
40             is => 'ro',
41             isa => 'CodeRef',
42             traits => ['Code'],
43             init_arg => 'rewrite_package',
44             predicate => 'can_rewrite_package',
45             handles => {
46             rewrite_package => 'execute',
47             },
48             );
49              
50             sub ini_string {
51 11     11 1 503 my ($self, $sections) = @_;
52              
53             # TODO: @$sections = $self->_simplify_bundles(@$sections) if configured
54              
55 11         35 my @strings = map { $self->_ini_section($_) } @$sections;
  53         133  
56              
57 11         394 my $spacing = $self->spacing;
58              
59 11 100       56 if( $spacing eq 'all' ){
    100          
60             # put a blank line after each section
61 1         4 @strings = map { "$_\n" } @strings;
  8         16  
62             }
63             elsif( $spacing eq 'payload' ){
64             # put a blank line around any section with a payload
65 9 100       24 @strings = map { /\n.+/ ? "\n$_\n" : $_ } @strings;
  37         141  
66             }
67              
68 11         50 my $ini = join '', @strings;
69              
70             # don't need to start with a newline
71 11         37 $ini =~ s/\A\n+//;
72             # don't need more than two together (single blank line)
73 11         69 $ini =~ s/(?<=\n\n)\n+//g;
74             # one newline at the end is sufficient
75 11         169 $ini =~ s/\n*\z/\n/;
76              
77 11         275 return $ini;
78             }
79              
80             sub _ini_section {
81 53     53   111 my ($self, $section) = @_;
82              
83             # break the reference, make one if we don't have one
84 53 100       189 $section = ref($section) eq 'ARRAY' ? [@$section] : [$section];
85              
86 53 100       150 my $config = ref($section->[-1]) eq 'HASH' ? pop @$section : {};
87 53         92 my $name = shift @$section;
88 53   66     136 my $package = shift @$section || $name;
89              
90 53 100       1917 if( $self->can_rewrite_package ){
91             # copy the value and offer it as $_
92 26         49 local $_ = $package;
93             # only use if something was returned
94 26   66     1004 $package = $self->rewrite_package($_) || $package;
95             }
96              
97             # this name matching junk could be better
98             {
99             # make leading punctuation optional for this comparison
100 53         279 my ($prefix, $moniker) = ($package =~ m/^(\W*)(.+)$/);
  53         242  
101              
102             # Don't print the name if it's the same as the package moniker
103             # (ignoring possible bundle prefix and possible leading punctuation).
104 53 100       2398 if( $name =~ m{^([^/]+/)*(\Q$prefix\E)?\Q$moniker\E$} ){
    50          
105 19         66 $name = ''
106             }
107             # else (if configured) just strip the whole prefix regardless
108             elsif( $self->strip_bundle_prefix ){
109 34         93 $name =~ s{^\@.+/}{};
110             }
111             }
112              
113             # Only show the name if different from the package moniker
114 53 100       201 my $ini = "[$package" . ($name ? " / $name" : '') . "]\n";
115              
116 53         144 $ini .= $self->_ini_section_config($config);
117              
118 53         198 return $ini;
119             }
120              
121             # TODO: rewrite_package
122             # reverse RewritePrefix
123             #$package =~ s/Dist::Zilla::(Plugin(Bundle)?)::/$2 ? '@' : ''/e
124             #or $package = "=$package";
125              
126             sub _simplify_bundles {
127 0     0   0 my ($self, @sections) = @_;
128              
129 0         0 my @simplified;
130             # for specified bundles just show [@Bundle] instead of each plugin
131             #my %bundles = map { ($_ => 0) } @{ $opts->{bundles} || [] };
132             my %bundles;
133              
134 0         0 foreach my $section ( @sections ){
135 0         0 my ($name) = @$section;
136             # just list the bundle not each individual plugin
137 0         0 foreach my $bundle ( keys %bundles ){
138 0 0       0 if( $name =~ /\@${bundle}\b/ ){
139             push @simplified, '@' . $bundle
140 0 0       0 unless $bundles{ $bundle }++;
141 0         0 next;
142             }
143             else {
144 0         0 push @simplified, $section;
145             }
146             }
147             }
148              
149 0         0 return @simplified;
150             }
151              
152             sub _ini_section_config {
153 53     53   112 my ($self, $config) = @_;
154              
155 53 100 50     302 return ''
156             unless $config && scalar keys %$config;
157              
158 16         32 my @lines;
159 16         52 my $len = List::Util::max(map { length } keys %$config);
  23         84  
160              
161 16         66 foreach my $k ( sort keys %$config ){
162 23         62 my $v = $config->{ $k };
163 23 100       102 $v = '' if !defined $v;
164             push @lines,
165             # don't end a line with "=\x20" (when the value is '')
166 28 100       196 map { sprintf "%-*s =%s\n", $len, $k, (length($_) ? ' ' . $_ : '') }
167             # one k=v line per array item
168             ref $v eq 'ARRAY'
169             ? @$v
170             # if there are newlines, assume 1 k=v per line
171             : $v =~ /\n/
172             # but skip blanks
173 23 50       100 ? grep { $_ } split /\n/, $v
  0 100       0  
174             # just one plain k=v line
175             : $v
176             }
177              
178 16         95 return join '', @lines;
179             }
180              
181 3     3   24 no Moose;
  3         7  
  3         18  
182 3     3   758 no Moose::Util::TypeConstraints;
  3         9  
  3         18  
183             __PACKAGE__->meta->make_immutable;
184             1;
185              
186             __END__
187              
188             =pod
189              
190             =encoding UTF-8
191              
192             =for :stopwords Randy Stauner ACKNOWLEDGEMENTS TODO cpan testmatrix url annocpan anno
193             bugtracker rt cpants kwalitee diff irc mailto metadata placeholders
194             metacpan
195              
196             =head1 NAME
197              
198             Config::MVP::Writer::INI - Build an INI file for Config::MVP
199              
200             =head1 VERSION
201              
202             version 0.005
203              
204             =for test_synopsis my @sections;
205              
206             =head1 SYNOPSIS
207              
208             my $ini = Config::MVP::Writer::INI->new->ini_string(\@sections);
209              
210             =head1 DESCRIPTION
211              
212             This class takes a collection of L<Config::MVP> style data structures
213             and writes them to a string in INI format.
214              
215             One usage example would be to create a roughly equivalent INI file
216             from the output of a plugin bundle (L<Dist::Zilla>, L<Pod::Weaver>, etc.).
217              
218             The author makes no claim that this would actually be useful to anyone.
219              
220             =head1 ATTRIBUTES
221              
222             =head2 spacing
223              
224             Defines the spacing between sections.
225             Must be one of the following:
226              
227             =over 4
228              
229             =item payload
230              
231             (Default) Put blank lines around sections with a payload
232              
233             =item all
234              
235             Put a blank line between all sections
236              
237             =item none
238              
239             No blank lines
240              
241             =back
242              
243             =head2 strip_bundle_prefix
244              
245             Boolean: Always remove the leading C<@BundleName/> part of a section name.
246             This cuts down on the noise when the name is actually different
247             from the package moniker (but the prefix isn't desired). Defaults to true.
248              
249             =head2 rewrite_package
250              
251             This attribute is a coderef that will be used to munge the package name
252             of each section. The package will be passed as the only argument
253             (and also available as C<$_>) and should return the translation.
254             If nothing is returned the original package will be used.
255              
256             This can be used to flavor the INI for a particular application.
257             For example:
258              
259             rewrite_package => sub { s/^MyApp::Plugin::/-/r; }
260              
261             will transform an array ref of
262              
263             [ Stinky => 'MyApp::Plugin::Nickname' => {real_name => "Dexter"} ]
264              
265             into an INI string of
266              
267             [-Nickname / Stinky]
268             real_name = Dexter
269              
270             =head1 METHODS
271              
272             =head2 ini_string
273              
274             This takes an array ref of array refs,
275             each one being a C<Config::MVP> style section specification:
276              
277             [
278             [ $name, $package, \%payload ],
279             ]
280              
281             and returns a string.
282              
283             For convenience a few specification shortcuts are recognized:
284              
285             $name => [ $name, $name, {} ]
286             [ $name ] => [ $name, $name, {} ]
287             [ $name, $package ] => [ $name, $package, {} ]
288             [ $name, \%payload ] => [ $name, $name, \%payload ]
289              
290             =for comment has simplify_bundles => (
291             is => 'ro',
292             isa => union([qw( ArrayRef Bool )]),
293             );
294              
295             =head1 WARNING
296              
297             This code is very much in an alpha state and the API is likely to change.
298             As always, suggestions, bug reports, patches, and pull requests are welcome.
299              
300             =head1 TODO
301              
302             =over 4
303              
304             =item *
305              
306             Documentation
307              
308             =item *
309              
310             More tests
311              
312             =item *
313              
314             Allow payload to be an arrayref for explicit ordering
315              
316             =back
317              
318             =head1 SUPPORT
319              
320             =head2 Perldoc
321              
322             You can find documentation for this module with the perldoc command.
323              
324             perldoc Config::MVP::Writer::INI
325              
326             =head2 Websites
327              
328             The following websites have more information about this module, and may be of help to you. As always,
329             in addition to those websites please use your favorite search engine to discover more resources.
330              
331             =over 4
332              
333             =item *
334              
335             MetaCPAN
336              
337             A modern, open-source CPAN search engine, useful to view POD in HTML format.
338              
339             L<http://metacpan.org/release/Config-MVP-Writer-INI>
340              
341             =back
342              
343             =head2 Bugs / Feature Requests
344              
345             Please report any bugs or feature requests by email to C<bug-config-mvp-writer-ini at rt.cpan.org>, or through
346             the web interface at L<https://rt.cpan.org/Public/Bug/Report.html?Queue=Config-MVP-Writer-INI>. You will be automatically notified of any
347             progress on the request by the system.
348              
349             =head2 Source Code
350              
351              
352             L<https://github.com/rwstauner/Config-MVP-Writer-INI>
353              
354             git clone https://github.com/rwstauner/Config-MVP-Writer-INI.git
355              
356             =head1 AUTHOR
357              
358             Randy Stauner <rwstauner@cpan.org>
359              
360             =head1 CONTRIBUTOR
361              
362             =for stopwords Karen Etheridge
363              
364             Karen Etheridge <ether@cpan.org>
365              
366             =head1 COPYRIGHT AND LICENSE
367              
368             This software is copyright (c) 2012 by Randy Stauner.
369              
370             This is free software; you can redistribute it and/or modify it under
371             the same terms as the Perl 5 programming language system itself.
372              
373             =cut