File Coverage

lib/CGI/FormBuilder/Source/File.pm
Criterion Covered Total %
statement 120 138 86.9
branch 48 70 68.5
condition 22 41 53.6
subroutine 9 10 90.0
pod 3 3 100.0
total 202 262 77.1


line stmt bran cond sub pod time code
1              
2             ###########################################################################
3             # Copyright (c) Nate Wiger http://nateware.com. All Rights Reserved.
4             # Please visit http://formbuilder.org for tutorials, support, and examples.
5             ###########################################################################
6              
7             package CGI::FormBuilder::Source::File;
8              
9             =head1 NAME
10              
11             CGI::FormBuilder::Source::File - Initialize FormBuilder from external file
12              
13             =head1 SYNOPSIS
14              
15             # use the main module
16             use CGI::FormBuilder;
17              
18             my $form = CGI::FormBuilder->new(source => 'form.conf');
19              
20             my $lname = $form->field('lname'); # like normal
21              
22             =cut
23              
24 2     2   13 use Carp;
  2         5  
  2         184  
25 2     2   9 use strict;
  2         3  
  2         58  
26 2     2   7 use warnings;
  2         2  
  2         108  
27 2     2   6 no warnings 'uninitialized';
  2         2  
  2         81  
28              
29 2     2   33 use 5.006; # or later
  2         5  
30 2     2   24 use CGI::FormBuilder::Util;
  2         3  
  2         202  
31 2     2   457 use utf8;
  2         215  
  2         10  
32              
33              
34             our $VERSION = '3.20';
35              
36             # Begin "real" code
37             sub new {
38 23     23 1 58 my $mod = shift;
39 23   33     127 my $class = ref($mod) || $mod;
40 23         130 my %opt = arghash(@_);
41 23         103 return bless \%opt, $class;
42             }
43              
44             sub parse {
45 23     23 1 107 local $^W = 0; # -w sucks so hard
46 23         51 my $self = shift;
47 23   33     141 my $file = shift || $self->{source};
48              
49 23 50 33     182 $CGI::FormBuilder::Util::DEBUG ||= $self->{debug} if ref $self;
50              
51 23         53 my $ret = {}; # top level
52 23         47 my $ptr = $ret; # curr ptr
53 23         79 my @lvl = (); # previous levels
54              
55 23         46 my $s = 0; # curr spaces
56 23         46 my $lsp = 0; # level spaces
57 23         44 my $psp = 0; # prev spaces
58              
59 23         48 my $refield = 0;
60 23         40 my @file;
61 23         43 my $utf8 = 0; # parse file as utf8
62              
63 23         156 debug 1, "parsing $file as input source";
64 23 50       101 if (ref $file eq 'SCALAR') {
    0          
65 23         335 @file = split /[\r\n]+/, $$file;
66             } elsif (ref $file eq 'ARRAY') {
67 0         0 @file = @$file;
68             } else {
69 0 0       0 open(F, "<$file") || puke "Cannot read $file: $!";
70 0         0 @file = ;
71 0         0 close F;
72             }
73              
74 23         74 my($lterm, $here); # level term, here string
75 23         48 my $inval = 0;
76 23         68 for (@file) {
77 239 100 100     1353 next if /^\s*$/ || /^\s*#/; # blanks and comments
78 214 50       1578 next if /^\s*\[\%\s*\#|^\s*-*\%\]/; # TT comments too
79 214         424 chomp;
80 214         1748 my($term, $line) = split /\s*:\s*/, $_, 2;
81 214 50 33     622 $utf8 = 1 if $term eq 'charset' && $line =~ /^utf/; # key off charset to decode value
82 214 50       544 $line = utf8::decode($line) if $utf8;
83              
84             # here string term-inator (har)
85 214 100       477 if ($here) {
86 3 100       9 if ($term eq $here) {
87 1         3 undef $here;
88 1         3 next;
89             } else {
90 2         6 $line = $term;
91 2         4 $term = $lterm;
92             }
93             } else {
94             # count leading space if it's there
95 211         357 $s = 1; # reset
96 211 100       868 $s += length($1) if $term =~ s/^(\s+)//;
97 211         517 $line =~ s/\s+$//; # trailing space
98              
99             # uplevel pre-check (may have a value below)
100 211 100       561 if ($s == 1) {
    100          
101 91         145 $ptr = $ret;
102 91         202 @lvl = ();
103 91         156 $lsp = 1; # set to zero for next pass
104 91         155 $refield = 0;
105 91         163 $inval = 0;
106             } elsif ($s <= $lsp) {
107 24   33     62 $ptr = pop(@lvl) || $ret;
108 24         43 $lsp = $s; # uplevel term indent
109 24         37 $inval = 0;
110             }
111              
112             # special catch for continued (indented) line
113 211 100 100     790 if ($s >= $psp && $inval && ! length $line) {
      100        
114 1         3 $line = $term;
115 1         27 $term = $lterm;
116             }
117 211         810 debug 2, "[$s >= $psp, inval=$inval] term=$term; line=$line";
118             }
119 213         386 $psp = $s;
120              
121             # has a value
122 213 100       513 if (length $line) {
123 132         471 debug 2, "$term = $line ($s < $lsp)";
124              
125 132   33     309 $lsp ||= $s; # first valid term indent
126              
127             # <
128 132 100       395 if ($line =~ /^<<(.+)/) {
    100          
129 1         3 $lterm = $term;
130 1         5 $here = $1;
131 1         4 next;
132             } elsif ($here) {
133 2         7 $ptr->{$term} .= "$line\n";
134 2         5 next;
135             }
136              
137 129         216 my @val;
138 129 100 66     998 if ($term =~ /^js/ || $term =~ /^on[a-z]/ || $term eq 'messages' || $term eq 'comment') {
    100 66        
      66        
139 8         26 @val = $line; # verbatim
140             } elsif ($line =~ s/^\\(.)//) {
141             # Reference - this is tricky. Go all the way up to
142             # the top to make sure, or use $self->{caller} if
143             # we were given a place to go.
144 2         7 my $r = $1;
145 2         4 my $l = 0;
146 2         5 my @p;
147 2 50       7 if ($self->{caller}) {
148 0         0 @p = $self->{caller};
149             } else {
150 2         12 while (my $pkg = caller($l++)) {
151 4         16 push @p, $pkg;
152             }
153             }
154 2 50       11 $line = "$r$p[-1]\::$line" unless $line =~ /::/;
155 2         10 debug 2, qq{eval "\@val = (\\$line)"};
156 2         226 eval "\@val = (\\$line)";
157 2 50       15 belch "Loading $line failed: $@" if $@;
158             } else {
159             # split commas
160 119         474 @val = split /\s*,\s*/, $line;
161              
162             # m=Male, f=Female -> [m,Male], [f,Female]
163 119         347 for (my $i=0; $i < @val; $i++) {
164 221 100       828 $val[$i] = [ split /\s*=\s*/, $val[$i], 2 ] if $val[$i] =~ /=/;
165             }
166             }
167              
168             # only arrayref on multi values b/c FB is "smart"
169 129 100       318 if ($ptr->{$term}) {
170             $ptr->{$term} = (ref $ptr->{$term})
171 1 0       6 ? [ @{$ptr->{$term}}, @val ] : @val > 1 ? \@val :
  1 0       6  
    50          
172             ref($val[0]) eq 'ARRAY' ? \@val : $val[0];
173             } else {
174 128 100       571 $ptr->{$term} = @val > 1 ? \@val : ref($val[0]) eq 'ARRAY' ? \@val : $val[0];
    100          
175             }
176 129         271 $inval = 1;
177             } else {
178 81         286 debug 2, "$term: new level ($s < $lsp)";
179              
180             # term:\n -> nest with bracket
181 81 100       253 if ($term eq 'fields') {
    100          
182 15         30 $refield = 1;
183 15         31 $term = 'fieldopts';
184             } elsif ($refield) {
185 41         66 push @{$ret->{fields}}, $term;
  41         161  
186             }
187              
188 81   50     474 $ptr->{$term} ||= {};
189 81         147 push @lvl, $ptr;
190 81         160 $ptr = $ptr->{$term};
191              
192 81         135 $lsp = $s; # reset spaces
193 81         159 $inval = 0;
194             }
195 210         463 $lterm = $term;
196             }
197              
198 23 50       68 if (ref $self) {
199             # add in any top-level options
200 23         115 while (my($k,$v) = each %$self) {
201 23 50       143 $ret->{$k} = $v unless exists $ret->{$k};
202             }
203              
204             # in FB, this is a class (not object) for speed
205 23         63 $self->{data} = $ret;
206 23         64 $self->{source} = $file;
207             }
208              
209 23 50       252 return wantarray ? %$ret : $ret;
210             }
211              
212             sub write_module {
213 0     0 1   my $self = shift;
214 0   0       my $mod = shift || puke "Missing required Module::Name";
215 0           (my $out = $mod) =~ s/.+:://;
216 0           $out .= '.pm';
217              
218 0 0         open(M, ">$out") || puke "Can't write $out: $!";
219              
220 0           print M "\n# Generated ".localtime()." by ".__PACKAGE__." $VERSION\n";
221 0           print M <
222             #
223             # To use this, you must write a script and then use this module.
224             # In your script, get this form with "my \$form = $mod->new()"
225              
226             package $mod;
227              
228             use CGI::FormBuilder;
229             use strict;
230              
231             sub new {
232             # $mod->new() calling format
233             my \$self = shift if \@_ && \@_ % 2 != 0;
234              
235             # data structure from '$self->{source}'
236             EOH
237              
238 0           require Data::Dumper;
239 0           local $Data::Dumper::Varname = 'form';
240 0           print M " my ". Data::Dumper::Dumper($self->{data});
241              
242 0           print M <<'EOV';
243              
244             # allow overriding of individual parameters
245             while (@_) {
246             $form1->{shift()} = shift;
247             }
248              
249             # return a new form object
250             return CGI::FormBuilder->new(%$form1);
251             }
252              
253             1;
254             # End of module
255             EOV
256              
257 0           close M;
258 0           print STDERR "Wrote $out\n"; # send to stderr in case of httpd
259             }
260              
261             1;
262             __END__