/
INI.pm
187 lines (141 loc) · 4.2 KB
/
INI.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# vim: set ts=2 sts=2 sw=2 expandtab smarttab:
use strict;
use warnings;
package Config::MVP::Writer::INI;
# ABSTRACT: Build an INI file for Config::MVP
use Moose;
use Moose::Util::TypeConstraints;
use List::AllUtils ();
has spacing => (
is => 'ro',
isa => enum([qw( none all config )]),
default => 'config',
);
=for comment
has simplify_bundles => (
is => 'ro',
isa => union([qw( ArrayRef Bool )]),
);
=cut
has _rewrite_package => (
is => 'ro',
isa => 'CodeRef',
traits => ['Code'],
init_arg => 'rewrite_package',
predicate => 'can_rewrite_package',
handles => {
rewrite_package => 'execute',
},
);
sub ini_string {
my ($self, $sections) = @_;
# TODO: @$sections = $self->_simplify_bundles(@$sections) if configured
my @strings = map { $self->_ini_section($_) } @$sections;
my $spacing = $self->spacing;
if( $spacing eq 'all' ){
# put a blank line after each section
@strings = map { "$_\n" } @strings;
}
elsif( $spacing eq 'config' ){
# put a blank line around any section with a config
@strings = map { /\n.+/ ? "\n$_\n" : $_ } @strings;
}
my $ini = join '', @strings;
# don't need to start with a newline
$ini =~ s/\A\n+//;
# don't need more than two together (single blank line)
$ini =~ s/(?<=\n\n)\n+//g;
# one newline at the end is sufficient
$ini =~ s/\n*\z/\n/;
return $ini;
}
sub _ini_section {
my ($self, $section) = @_;
# break the reference, make one if we don't have one
$section = ref($section) eq 'ARRAY' ? [@$section] : [$section];
my $config = ref($section->[-1]) eq 'HASH' ? pop @$section : {};
my $name = shift @$section;
my $package = shift @$section || $name;
if( $self->can_rewrite_package ){
$package = $self->rewrite_package($package);
}
# FIXME: this handles the bundle prefix but not the whole moniker (class suffix)
my $ini = "[$package" . ($name =~ /^(.+?\/)?$package$/ ? '' : " / $name") . "]\n";
$ini .= $self->_ini_section_config($config);
return $ini;
}
# TODO: rewrite_package
# reverse RewritePrefix
#$package =~ s/Dist::Zilla::(Plugin(Bundle)?)::/$2 ? '@' : ''/e
#or $package = "=$package";
sub _simplify_bundles {
my ($self, @sections) = @_;
my @simplified;
# for specified bundles just show [@Bundle] instead of each plugin
#my %bundles = map { ($_ => 0) } @{ $opts->{bundles} || [] };
my %bundles;
foreach my $section ( @sections ){
my ($name) = @$section;
# just list the bundle not each individual plugin
foreach my $bundle ( keys %bundles ){
if( $name =~ /\@${bundle}\b/ ){
push @simplified, '@' . $bundle
unless $bundles{ $bundle }++;
next;
}
else {
push @simplified, $section;
}
}
}
return @simplified;
}
sub _ini_section_config {
my ($self, $config) = @_;
return ''
unless $config && scalar keys %$config;
my @lines;
my $len = List::AllUtils::max(map { length } keys %$config);
foreach my $k ( sort keys %$config ){
my $v = $config->{ $k };
push @lines,
map { sprintf "%-*s = %s\n", $len, $k, $_ }
# one k=v line per array item
ref $v eq 'ARRAY'
? @$v
# if there are newlines, assume 1 k=v per line
: $v =~ /\n/
# but skip blanks
? grep { $_ } split /\n/, $v
# just one plain k=v line
: $v
}
return join '', @lines;
}
no Moose;
no Moose::Util::TypeConstraints;
__PACKAGE__->meta->make_immutable;
1;
=for test_synopsis
my @sections;
=head1 SYNOPSIS
my $ini = Config::MVP::Writer::INI->new->ini_string(\@sections);
=head1 DESCRIPTION
This class takes a collection of L<Config::MVP> style data structures
and writes them to a string in INI format.
=head1 WARNING
This code is very much in an alpha state and the API is likely to change.
As always, suggestions, bug reports, patches, and pull requests are welcome.
=method ini_string
This takes an array ref of array refs,
each one being a C<Config::MVP> style section specification:
[
[ $name, $package, \%config ],
]
and returns a string.
=for :stopwords TODO
=head1 TODO
=for :list
* Documentation
* A lot more tests
=cut