File Coverage

blib/lib/Form/Factory/Processor.pm
Criterion Covered Total %
statement 60 60 100.0
branch 8 12 66.6
condition 14 16 87.5
subroutine 17 17 100.0
pod 8 8 100.0
total 107 113 94.6


line stmt bran cond sub pod time code
1             package Form::Factory::Processor;
2             $Form::Factory::Processor::VERSION = '0.022';
3 1     1   2666 use Moose;
  1         1  
  1         6  
4 1     1   4782 use Moose::Exporter;
  1         2  
  1         5  
5              
6 1     1   35 use Carp ();
  1         1  
  1         12  
7 1     1   408 use Form::Factory::Action;
  1         3  
  1         36  
8 1     1   475 use Form::Factory::Action::Meta::Class;
  1         2  
  1         31  
9 1     1   441 use Form::Factory::Action::Meta::Attribute::Control;
  1         4  
  1         49  
10 1     1   503 use Form::Factory::Processor::DeferredValue;
  1         260  
  1         517  
11              
12             Moose::Exporter->setup_import_methods(
13             as_is => [ qw( deferred_value ) ],
14             with_meta => [ qw(
15             has_control use_feature
16             has_cleaner has_checker has_pre_processor has_post_processor
17             ) ],
18             also => 'Moose',
19             );
20              
21             # ABSTRACT: Moos-ish helper for action classes
22              
23              
24             sub init_meta {
25 10     10 1 22909 my $package = shift;
26 10         41 my %options = @_;
27              
28 10         53 Moose->init_meta(%options);
29              
30 10         34844 my $meta = Moose::Util::MetaRole::apply_metaroles(
31             for => $options{for_class},
32             class_metaroles => {
33             class => [ 'Form::Factory::Action::Meta::Class' ],
34             },
35             );
36              
37 10         13284 Moose::Util::apply_all_roles(
38             $options{for_class}, 'Form::Factory::Action',
39             );
40              
41 10         48330 return $meta;
42             }
43              
44              
45             sub _setup_control_defaults {
46 32     32   55 my $meta = shift;
47 32         52 my $name = shift;
48 32 50       153 my $args = @_ == 1 ? shift : { @_ };
49              
50             # Setup default unless this is an inherited control attribute
51 32 100       134 unless ($name =~ /^\+/) {
52 31   100     88 $args->{control} ||= 'text';
53 31   100     148 $args->{options} ||= {};
54 31   100     118 $args->{features} ||= {};
55 31   50     125 $args->{traits} ||= [];
56              
57 31   100     125 $args->{is} ||= 'ro';
58 31   66     233 $args->{isa} ||= Form::Factory->control_class($args->{control})->default_isa;
59              
60 31         57 unshift @{ $args->{traits} }, 'Form::Control';
  31         94  
61              
62             }
63              
64 32 50       90 Carp::croak(qq{the "required" setting is used on $name, but is forbidden on controls})
65             if $args->{required};
66              
67 32         54 for my $value (values %{ $args->{features} }) {
  32         117  
68 21 100 100     113 $value = {} if $value and not ref $value;
69             }
70              
71 32         81 $args->{__meta} = $meta;
72              
73 32         78 return ($meta, $name, $args);
74             }
75              
76             sub has_control {
77 30     30 1 67476 my ($meta, $name, $args) = _setup_control_defaults(@_);
78 30         126 $meta->add_attribute( $name => $args );
79             }
80              
81              
82             sub use_feature {
83 1     1 1 2063 my $meta = shift;
84 1         2 my $name = shift;
85 1 50       4 my $args = @_ == 1 ? shift : { @_ };
86              
87 1         39 $meta->features->{$name} = $args;
88             }
89              
90              
91             sub deferred_value(&) {
92 1     1 1 4380 my $code = shift;
93              
94 1         41 return Form::Factory::Processor::DeferredValue->new(
95             code => $code,
96             );
97             }
98              
99              
100             sub _add_function {
101 7     7   12 my ($type, $meta, $name, $code) = @_;
102 7 50       18 Carp::croak("bad code given for $type $name") unless defined $code;
103 7         226 $meta->features->{functional}{$type . '_code'}{$name} = $code;
104             }
105              
106 1     1 1 14 sub has_cleaner { _add_function('cleaner', @_) }
107 4     4 1 11801 sub has_checker { _add_function('checker', @_) }
108 1     1 1 9 sub has_pre_processor { _add_function('pre_processor', @_) }
109 1     1 1 10 sub has_post_processor { _add_function('post_processor', @_) }
110              
111              
112             __PACKAGE__->meta->make_immutable;
113              
114             __END__
115              
116             =pod
117              
118             =encoding UTF-8
119              
120             =head1 NAME
121              
122             Form::Factory::Processor - Moos-ish helper for action classes
123              
124             =head1 VERSION
125              
126             version 0.022
127              
128             =head1 SYNOPSIS
129              
130             package MyApp::Action::Foo;
131             use Form::Factory::Processor;
132              
133             has_control name => (
134             control => 'text',
135             options => {
136             label => 'Your Name',
137             },
138             features => {
139             trim => 1,
140             required => 1,
141             length => {
142             minimum => 3,
143             maximum => 15,
144             },
145             },
146             );
147              
148             has_cleaner convert_to_underscores => sub {
149             my $self = shift;
150             my $name = $self->controls->{name}->current_value;
151             $name =~ s/\W+/_/g;
152             $self->controls->{name}->current_value($name);
153             };
154              
155             has_checker do_not_car_for_names_start_with_r => sub {
156             my $self = shift;
157             my $value = $self->controls->{name}->current_value;
158              
159             if ($value =~ /^R/i) {
160             $self->error('i do not like names start with "R," get a new name');
161             $self->result->is_valid(0);
162             }
163             };
164              
165             has_pre_processor log_start => sub {
166             my $self = shift;
167             MyApp->logger->debug("START Foo " . Time::HiRes::time());
168             };
169              
170             has_post_processor log_stop => sub {
171             my $self = shift;
172             MyApp->logger->debug("STOP Foo " . Time::HiRes::time());
173             };
174              
175             sub run {
176             my $self = shift;
177             MyApp->do_something_to_your_name($self->name);
178             }
179              
180             =head1 DESCRIPTION
181              
182             This is the helper class used to define actions. This class automatically imports the subroutines described in this documentaiton as well as any defined in L<Moose>. It also grants your action class and its meta-class the correct roles.
183              
184             =head1 METHODS
185              
186             =head2 init_meta
187              
188             Sets up the roles and meta-class information for your action class.
189              
190             =head2 has_control
191              
192             has_control $name => (
193             %usual_has_options,
194              
195             control => $control_short_name,
196             options => \%control_options,
197             features => \%control_features,
198             );
199              
200             This works very similar to L<Moose/has>. This applies the L<Form::Factory::Action::Meta::Attribute::Control> trait to the attribute and sets up other defaults.
201              
202             The following defaults are set:
203              
204             =over
205              
206             =item is
207              
208             Control attributes are read-only by default.
209              
210             =item isa
211              
212             Control attributes are string by default. Be careful about using an C<isa> setting that differs from the control's value. If you do, make sure you use features to make certain the type is the correct kind of thing or that a coercion to the correct type of thing is also setup.
213              
214             =item control
215              
216             This will default to "text" if not set.
217              
218             =item options
219              
220             An empty hash reference is used by default.
221              
222             =item features
223              
224             An empty hash references is used by default.
225              
226             =back
227              
228             You may pass any options you could pass to C<has> as well as the additional options for features, control options, etc. This also supports the C<'+name'> syntax for altering attributes that are inherited from a parent class. Currently, only the C<features> option is supported for this, which allows you to add new features or even to turn off features from the parent class. For example, if a control is setup in a parent like this:
229              
230             has_control name => (
231             control => 'text',
232             features => {
233             trim => 1,
234             required => 1,
235             length => {
236             maximum => 20,
237             minimum => 3,
238             },
239             },
240             );
241              
242             A child class may choose to turn the required off and change the length checks by placing this in the subclass definition:
243              
244             has_control '+name' => (
245             features => {
246             required => 0,
247             length => {
248             maximum => 20,
249             minimum => 10,
250             },
251             },
252             );
253              
254             The C<trim> feature in the parent would remain in place as originally defined, the required feature is now turned off in the child class, and the length feature options have been replaced. This is done with a shallow merge, so top-level keys in the child class will replace top-level keys in the parent, but any listed in the parent, but not the child remain unchanged.
255              
256             B<DO NOT> use the C<required> attribute option on controls. If you try to do so, the call to C<has_control> will croak because this will not work with how attributes are setup. If you need an attribute to be required, do not use a control or use the required feature instead.
257              
258             =head2 use_feature
259              
260             This function is used to make an action use a particular form feature. It's usage is as follows:
261              
262             use_feature $name => \%options;
263              
264             The C<%options> are optional. So,
265              
266             use_feature $name;
267              
268             will also work if you do not need to pass any features.
269              
270             The C<$name> is a short name for the feature class. For example, the name "require_none_or_all" will load the feature defined in L<Form::Factory::Feature::RequireNoneOrAll>.
271              
272             =head2 deferred_value
273              
274             has_control publish_on => (
275             control => 'text',
276             options => {
277             default_value => deferred_value {
278             my ($action, $control_name) = @_;
279             DateTime->now->iso8601,
280             },
281             },
282             );
283              
284             This is a helper for deferring the calculation of a value. This works similar to L<Scalar::Defer> to defer the calculation, but with an important difference. The subroutine is passed the action object (such as it exists while the controls are being constructed) and the control's name. The control itself doesn't exist yet when the subroutine is called.
285              
286             =head2 has_cleaner
287              
288             has_cleaner $name => sub { ... }
289              
290             Adds some code called during the clean phase.
291              
292             =head2 has_checker
293              
294             has_checker $name => sub { ... }
295              
296             Adds some code called during the check phase.
297              
298             =head2 has_pre_processor
299              
300             has_pre_processor $name => sub { ... }
301              
302             Adds some code called during the pre-process phase.
303              
304             =head2 has_post_processor
305              
306             has_post_processor $name => sub { ... }
307              
308             Adds some code called during the post-process phase.
309              
310             =head1 SEE ALSO
311              
312             L<Form::Factory::Action>
313              
314             =head1 AUTHOR
315              
316             Andrew Sterling Hanenkamp <hanenkamp@cpan.org>
317              
318             =head1 COPYRIGHT AND LICENSE
319              
320             This software is copyright (c) 2015 by Qubling Software LLC.
321              
322             This is free software; you can redistribute it and/or modify it under
323             the same terms as the Perl 5 programming language system itself.
324              
325             =cut