File Coverage

blib/lib/CGI/FormBuilder/Source/YAML.pm
Criterion Covered Total %
statement 57 73 78.0
branch 15 28 53.5
condition 3 9 33.3
subroutine 7 7 100.0
pod 2 2 100.0
total 84 119 70.5


line stmt bran cond sub pod time code
1             package CGI::FormBuilder::Source::YAML;
2              
3 1     1   2259 use strict;
  1         4  
  1         49  
4 1     1   7 use warnings;
  1         3  
  1         36  
5              
6 1     1   1275 use YAML::Syck;
  1         2945  
  1         99  
7              
8 1     1   11 use CGI::FormBuilder::Util;
  1         2  
  1         1452  
9              
10             our $VERSION = '1.0008';
11              
12             sub new {
13 1     1 1 17 my $self = shift;
14 1   33     9 my $class = ref($self) || $self;
15 1         5 my %opt = @_;
16 1         6 return bless \%opt, $class;
17             }
18              
19             sub parse {
20 1     1 1 9 my $self = shift;
21 1   33     56 my $file = shift || $self->{source};
22              
23 1         3 local $YAML::Syck::LoadCode = 1;
24 1         2 local $YAML::Syck::UseCode = 1;
25 1         2 local $YAML::Syck::DumpCode = 1;
26              
27 1 50 33     9 $CGI::FormBuilder::Util::DEBUG ||= $self->{debug} if ref $self;
28              
29 1 50       3 puke("file must be only one scalar file name") if ref $file;
30              
31 1         7 my $formopt = LoadFile($file);
32 1 50       491 puke("loaded file '$file' is not hashref") if ref $formopt ne 'HASH';
33              
34 1         7 debug 1, "processing YAML::Syck file '$file' as input source";
35              
36             # add in top-level options:
37 1 50       5 map { $formopt->{$_} = $self->{$_} if !exists $formopt->{$_} } keys %{$self};
  2         14  
  1         5  
38              
39             # whork in the function refs:
40 1 50       9 $self->_assign_references($formopt, 1) if ref $self;
41              
42 1         2 my %lame = ( %{$formopt} );
  1         19  
43 1         10 debug 1, "YAML form definition is:", Dump(\%lame);
44              
45 1 50       2847 return wantarray ? %{$formopt} : $formopt;
  0         0  
46             }
47              
48             sub _assign_references {
49 8     8   24 my ($self, $hashref, $stacklevel) = @_;
50 8         10 $stacklevel++;
51              
52 8         136 NODE:
53 8         13 foreach my $node (values %{$hashref}) {
54 31         48 my $ref = ref $node;
55              
56 31 100       1419 if ($ref eq 'HASH') {
    100          
57 7         3017 $self->_assign_references($node, $stacklevel);
58             }
59             elsif (!$ref) {
60              
61 20         80 debug 1, "node is '$node'\n";
62              
63 20 100       140 if ( $node =~ m{ \A \\ ([&\$%@]) (.*) \z }xms ) {
    50          
64              
65 1         7 my ($reftype, $refstr) = ($1, $2);
66            
67 1 50       6 if ($refstr =~ m{ :: }xms) {
68             # already know where it is. assign it.
69 0         0 my $subref = undef;
70 0         0 debug 1, "assigning direct pkg ref for '$reftype$refstr'";
71 0         0 eval "\$subref = \\$reftype$refstr";
72 0         0 my $err = $@;
73 0 0       0 debug 1, "eval error '$err'" if $err;
74 0         0 debug 1, "subref is '$subref'";
75 0         0 $node = $subref;
76             }
77             else {
78            
79 1         2 my $l = $stacklevel;
80 1         3 my $subref = undef;
81             LEVELUP:
82 1         9 while (my $pkg = caller($l++)) {
83 1         9 debug 1, "looking up at lev $l for ref '$refstr' in '$pkg'";
84 1         6 my $evalstr = "\$subref = \\$reftype$pkg\::$refstr";
85 1         6 debug 1, "eval '$evalstr'";
86 1         122 eval $evalstr;
87 1 50       6 if (!$@) {
88 1         3 $node = $subref;
89 1         4 last LEVELUP;
90             }
91             }
92             }
93 1         8 debug 1, "assgnd ref '$node' for '$reftype$refstr'";
94             }
95             elsif ( $node =~ m/ \A eval \s* { (.*) } \s* \z /xms ) {
96 0         0 my $evalstr = $1;
97 0         0 debug 1, "eval '$evalstr'";
98 0         0 my $result = eval $evalstr;
99 0         0 my $err = $@;
100 0 0       0 if ($err) {
101 0         0 debug 1, "eval error '$err'";
102             }
103             else {
104 0         0 debug 1, "assgnd ref '$node' for eval";
105 0         0 $node = $result;
106             }
107             }
108              
109             }
110             }
111 8         25 return;
112             }
113              
114             1;
115              
116             =head1 NAME
117              
118             CGI::FormBuilder::Source::YAML - Initialize FormBuilder from YAML file
119              
120             =head1 SYNOPSIS
121              
122             use CGI::FormBuilder;
123              
124             my $form = CGI::FormBuilder->new(
125             source => {
126             source => 'form.fb',
127             type => 'YAML',
128             },
129             );
130              
131             my $lname = $form->field('lname'); # like normal
132              
133             =head1 DESCRIPTION
134              
135             This reads a YAML (YAML::Syck) file that contains B
136             config options and returns a hash to be fed to CGI::FormBuilder->new().
137              
138             Instead of the syntax read by CGI::FormBuilder::Source::File,
139             it uses YAML syntax as read by YAML::Syck. That means you
140             fully specify the entire data structure.
141              
142             LoadCode is enabled, so you can use YAML syntax for defining subroutines.
143             This is convenient if you have a function that generates validation
144             subrefs, for example, I have one that can check profanity using Regexp::Common.
145              
146             validate:
147             myfield:
148             javascript: /^[\s\S]{2,50}$/
149             perl: !!perl/code: >-
150             { My::Funk::fb_perl_validate({
151             min => 2,
152             max => 50,
153             profanity => 'check'
154             })->(shift);
155             }
156              
157             =head1 POST PROCESSING
158              
159             There are two exceptions to "pure YAML syntax" where this module
160             does some post-processing of the result.
161              
162             =head2 REFERENCES (ala CGI::FormBuilder::Source::File)
163              
164             You can specify references as string values that start with
165             \&, \$, \@, or \% in the
166             same way you can with CGI::FormBuilder::Source::File. If you have
167             a full direct package reference, it will look there, otherwise
168             it will traverse up the caller stack and take the first it finds.
169              
170             For example, say your code serves multiple sites, and a menu
171             gets different options depending on the server name requested:
172              
173             # in My::Funk:
174             our $food_options = {
175             www.meats.com => [qw( beef chicken horta fish )],
176             www.veggies.com => [qw( carrot apple quorn radish )],
177             };
178              
179             # in source file:
180             options: \@{ $My::Funk::food_options->{ $ENV{SERVER_NAME} } }
181              
182             =head2 EVAL STRINGS
183              
184             You can specify an eval statement. You could achieve the same
185             example a different way:
186              
187             options: eval { $My::Funk::food_options->{ $ENV{SERVER_NAME} }; }
188              
189             The cost either way is about the same -- the string is eval'd.
190              
191             =head1 EXAMPLE
192              
193             method: GET
194             header: 0
195             title: test
196             name: test
197             action: /test
198             submit: test it
199             linebreaks: 1
200              
201             required:
202             - test1
203             - test2
204              
205             fields:
206             - test1
207             - test2
208             - test3
209             - test4
210              
211             fieldopts:
212             test1:
213             type: text
214             size: 10
215             maxlength: 32
216              
217             test2:
218             type: text
219             size: 10
220             maxlength: 32
221              
222             test3:
223             type: radio
224             options:
225             -
226             - 1
227             - Yes
228             -
229             - 0
230             - No
231              
232             test4:
233             options: \@test4opts
234             sort: \&Someother::Package::sortopts
235              
236             validate:
237             test1: /^\w{3,10}$/
238             test2:
239             javascript: EMAIL
240             perl: eq 'test@test.foo'
241             test3:
242             - 0
243             - 1
244             test4: \@test4opts
245              
246             You get the idea. A bit more whitespace, but it works in a
247             standardized way.
248              
249             =head1 METHODS
250              
251             =head2 new()
252              
253             Normally not used directly; it is called from CGI::FormBuilder.
254             Creates the C object. Arguments
255             from the 'source' hash passed to CGI::FormBuilder->new() will
256             become defaults, unless specified in the file.
257              
258             =head2 parse($source)
259              
260             Normally not used directly; it is called from CGI::FormBuilder.
261             Parses the specified source file. No fancy params --
262             just a single filename is accepted. If the file isn't
263             acceptable to YAML::Syck, I suppose it will die.
264              
265             =head1 SEE ALSO
266              
267             L, L
268              
269             =head1 AUTHOR
270              
271             Copyright (c) 2006 Mark Hedges . All rights reserved.
272              
273             =head1 LICENSE
274              
275             This module is free software; you may copy it under terms of
276             the Perl license (GNU General Public License or Artistic License.)
277             http://www.opensource.org/licenses/index.html
278              
279             =cut