File Coverage

blib/lib/String/Format.pm
Criterion Covered Total %
statement 40 42 95.2
branch 18 22 81.8
condition 8 13 61.5
subroutine 8 8 100.0
pod 1 2 50.0
total 75 87 86.2


line stmt bran cond sub pod time code
1             package String::Format;
2              
3             # ----------------------------------------------------------------------
4             # Copyright (C) 2002,2009 darren chamberlain <darren@cpan.org>
5             #
6             # This program is free software; you can redistribute it and/or
7             # modify it under the terms of the GNU General Public License as
8             # published by the Free Software Foundation; version 2.
9             #
10             # This program is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # General Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License
16             # along with this program; if not, write to the Free Software
17             # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18             # 02110-1301 USA.
19             # -------------------------------------------------------------------
20              
21 6     6   189107 use strict;
  6         17  
  6         279  
22 6     6   36 use vars qw($VERSION @EXPORT);
  6         12  
  6         467  
23 6     6   48 use Exporter;
  6         11  
  6         298  
24 6     6   35 use base qw(Exporter);
  6         11  
  6         4703  
25              
26             $VERSION = '1.17';
27             @EXPORT = qw(stringf);
28              
29             sub _replace {
30 31     31   115 my ($args, $orig, $alignment, $min_width,
31             $max_width, $passme, $formchar) = @_;
32              
33             # For unknown escapes, return the orignial
34 31 100       93 return $orig unless defined $args->{$formchar};
35              
36 27 50       72 $alignment = '+' unless defined $alignment;
37              
38 27         39 my $replacement = $args->{$formchar};
39 27 100       68 if (ref $replacement eq 'CODE') {
40             # $passme gets passed to subrefs.
41 3   100     20 $passme ||= "";
42 3         8 $passme =~ tr/{}//d;
43 3         11 $replacement = $replacement->($passme);
44             }
45              
46 27         150 my $replength = length $replacement;
47 27   66     95 $min_width ||= $replength;
48 27   66     89 $max_width ||= $replength;
49              
50             # length of replacement is between min and max
51 27 50 33     69 if (($replength > $min_width) && ($replength < $max_width)) {
52 0         0 return $replacement;
53             }
54              
55             # length of replacement is longer than max; truncate
56 27 100       73 if ($replength > $max_width) {
57 1         5 return substr($replacement, 0, $max_width);
58             }
59            
60             # length of replacement is less than min: pad
61 26 50       65 if ($alignment eq '-') {
62             # left align; pad in front
63 0         0 return $replacement . " " x ($min_width - $replength);
64             }
65              
66             # right align, pad at end
67 26         159 return " " x ($min_width - $replength) . $replacement;
68             }
69              
70             my $regex = qr/
71             (% # leading '%'
72             (-)? # left-align, rather than right
73             (\d*)? # (optional) minimum field width
74             (?:\.(\d*))? # (optional) maximum field width
75             ({.*?})? # (optional) stuff inside
76             (\S) # actual format character
77             )/x;
78             sub stringf {
79 19   50 19 1 44704 my $format = shift || return;
80 19 100       183 my $args = UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
81 19 100       76 $args->{'n'} = "\n" unless exists $args->{'n'};
82 19 100       72 $args->{'t'} = "\t" unless exists $args->{'t'};
83 19 100       60 $args->{'%'} = "%" unless exists $args->{'%'};
84              
85 19         193 $format =~ s/$regex/_replace($args, $1, $2, $3, $4, $5, $6)/ge;
  31         84  
86              
87 19         88 return $format;
88             }
89              
90             sub stringfactory {
91 1     1 0 78 shift; # It's a class method, but we don't actually want the class
92 1 50       6 my $args = UNIVERSAL::isa($_[0], "HASH") ? shift : { @_ };
93 1     1   7 return sub { stringf($_[0], $args) };
  1         20  
94             }
95              
96             1;
97             __END__
98              
99             =head1 NAME
100              
101             String::Format - sprintf-like string formatting capabilities with
102             arbitrary format definitions
103              
104             =head1 ABSTRACT
105              
106             String::Format allows for sprintf-style formatting capabilities with
107             arbitrary format definitions
108              
109             =head1 SYNOPSIS
110              
111             use String::Format;
112              
113             my %fruit = (
114             'a' => "apples",
115             'b' => "bannanas",
116             'g' => "grapefruits",
117             'm' => "melons",
118             'w' => "watermelons",
119             );
120              
121             my $format = "I like %a, %b, and %g, but not %m or %w.";
122              
123             print stringf($format, %fruit);
124            
125             # prints:
126             # I like apples, bannanas, and grapefruits, but not melons or watermelons.
127              
128             =head1 DESCRIPTION
129              
130             String::Format lets you define arbitrary printf-like format sequences
131             to be expanded. This module would be most useful in configuration
132             files and reporting tools, where the results of a query need to be
133             formatted in a particular way. It was inspired by mutt's index_format
134             and related directives (see <URL:http://www.mutt.org/doc/manual/manual-6.html#index_format>).
135              
136             =head1 FUNCTIONS
137              
138             =head2 stringf
139              
140             String::Format exports a single function called stringf. stringf
141             takes two arguments: a format string (see FORMAT STRINGS, below) and
142             a reference to a hash of name => value pairs. These name => value
143             pairs are what will be expanded in the format string.
144              
145             =head1 FORMAT STRINGS
146              
147             Format strings must match the following regular expression:
148              
149             qr/
150             (% # leading '%'
151             (-)? # left-align, rather than right
152             (\d*)? # (optional) minimum field width
153             (?:\.(\d*))? # (optional) maximum field width
154             ({.*?})? # (optional) stuff inside
155             (\S) # actual format character
156             )/x;
157              
158             If the escape character specified does not exist in %args, then the
159             original string is used. The alignment, minimum width, and maximum
160             width options function identically to how they are defined in
161             sprintf(3) (any variation is a bug, and should be reported).
162              
163             Note that Perl's sprintf definition is a little more liberal than the
164             above regex; the deviations were intentional, and all deal with
165             numeric formatting (the #, 0, and + leaders were specifically left
166             out).
167              
168             The value attached to the key can be a scalar value or a subroutine
169             reference; if it is a subroutine reference, then anything between the
170             '{' and '}' ($5 in the above regex) will be passed as $_[0] to the
171             subroutine reference. This allows for entries such as this:
172              
173             %args = (
174             d => sub { POSIX::strftime($_[0], localtime) },
175             );
176              
177             Which can be invoked with this format string:
178              
179             "It is %{%M:%S}d right now, on %{%A, %B %e}d."
180              
181             And result in (for example):
182              
183             It is 17:45 right now, on Monday, February 4.
184              
185             Note that since the string is passed unmolested to the subroutine
186             reference, and strftime would Do The Right Thing with this data, the
187             above format string could be written as:
188              
189             "It is %{%M:%S right now, on %A, %B %e}d."
190              
191             By default, the formats 'n', 't', and '%' are defined to be a newline,
192             tab, and '%', respectively, if they are not already defined in the
193             hashref of arguments that gets passed it. So we can add carriage
194             returns simply:
195              
196             "It is %{%M:%S right now, on %A, %B %e}d.%n"
197              
198             Because of how the string is parsed, the normal "\n" and "\t" are
199             turned into two characters each, and are not treated as a newline and
200             tab. This is a bug.
201              
202             =head1 FACTORY METHOD
203              
204             String::Format also supports a class method, named B<stringfactory>,
205             which will return reference to a "primed" subroutine. stringfatory
206             should be passed a reference to a hash of value; the returned
207             subroutine will use these values as the %args hash.
208              
209             my $self = Some::Groovy::Package->new($$, $<, $^T);
210             my %formats = (
211             'i' => sub { $self->id },
212             'd' => sub { $self->date },
213             's' => sub { $self->subject },
214             'b' => sub { $self->body },
215             );
216             my $index_format = String::Format->stringfactory(\%formats);
217              
218             print $index_format->($format1);
219             print $index_format->($format2);
220              
221             This subroutine reference can be assigned to a local symbol table
222             entry, and called normally, of course:
223              
224             *reformat = String::Format->stringfactory(\%formats);
225              
226             my $reformed = reformat($format_string);
227              
228             =head1 LICENSE
229              
230             C<String::Format> is free software; you can redistribute it and/or
231             modify it under the terms of the GNU General Public License as
232             published by the Free Software Foundation; version 2.
233              
234              
235             =head1 AUTHOR
236              
237             darren chamberlain <darren@cpan.org>