File Coverage

blib/lib/Form/Factory.pm
Criterion Covered Total %
statement 9 39 23.0
branch 0 10 0.0
condition 0 12 0.0
subroutine 3 10 30.0
pod 5 5 100.0
total 17 76 22.3


line stmt bran cond sub pod time code
1             package Form::Factory;
2             $Form::Factory::VERSION = '0.021';
3 1     1   1244 use Moose;
  1         418328  
  1         10  
4              
5 1     1   7920 use Carp ();
  1         2  
  1         23  
6 1     1   5 use Class::Load;
  1         2  
  1         789  
7              
8             =head1 NAME
9              
10             Form::Factory - a general-purpose form handling API
11              
12             =head1 VERSION
13              
14             version 0.021
15              
16             =head1 SYNOPSIS
17              
18             ### CGI, HTML example
19             my $interface = Form::Factory->new_interface('HTML');
20             my $action = $interface->new_action('MyApp::Action::Login');
21              
22             ### Drawing the form contents
23             $action->unstash('login');
24             $action->globals->{after_login} = '/index.html';
25             $action->stash('login');
26             $action->render;
27             $action->render_control(button => {
28             name => 'submit',
29             label => 'Login',
30             });
31             $action->results->clear_all;
32            
33             ### Processing the form result
34             my $q = CGI->new;
35             $action->unstash('login');
36             $action->consume_and_clean_and_check_and_process( request => $q->Vars );
37              
38             if ($action->is_valid and $action->is_success) {
39             $action->stash('login');
40             print $q->redirect($action->globals->{after_login});
41             }
42             else {
43             print q{<p class="errors">};
44             print $action->error_messages;
45             print q{</p>};
46             }
47              
48             =head1 DESCRIPTION
49              
50             B<ALPHA API>. This code is not fully tested (if you look in the test files you will see a long list of tests planned, but no yet implemented). It is currently being employed on a non-production project. The API I<will> change. See L</TODO> for more.
51              
52             This API is designed to be a general purpose API for showing and processing forms. This has been done before. I know. However, I believe this provides some distinct advantages.
53              
54             You should check out the alternatives because this might be more complex than you really need. That said, why would you want this?
55              
56             =head2 MODULAR AND EXTENSIBLE
57              
58             This forms processor makes heavy use of L<Moose>. Nearly every class is replaceable or extensible in case it does not work the way you need it to. It is initially implemented to support HTML forms and command-line interfaces, but I would like to see it support XForms, XUL, PDF forms, GUI forms via Wx or Curses, etc.
59              
60             =head2 ENCAPSULATED ACTIONS
61              
62             The centerpiece of this API is the way an action is encapsulated in a single object. In a way, a form object is a glorified functor with a C<run> method responsible for taking the action. Wrapped around that is the ability to describe what kind of inputs are expected, how to clean up and verify the inputs, how to report errors so that they can be used, how entered values can be sent back to the orignal user, etc.
63              
64             The goal here is to create self-contained actions that specify what they are in fairly generic terms, take specific action when the input checks out, to handle exceptions in a way that is convenient in forms processing (where exceptions are often more common than not) and send back output cleanly.
65              
66             =head2 MULTIPLE IMPLEMENTATIONS
67              
68             An action presents a blueprint for the data it needs to run. A form interface takes that blueprint and builds the UI to present to the user and consume input from the user and notify the action.
69              
70             A form interface could be any kind of UI. The way the form interface and action is used will depend on the form interface implementation. The action itself should not need to care (much) about what interface it is used in.
71              
72             =head2 CONTROLS VERSUS WIDGETS
73              
74             So far, the attempt has been made to keep controls pretty generic. A control specifies the kind of inputs an action expects for some input, but the interface is responsible for rendering that control as a suitable widget and consuming data from that widget.
75              
76             =head2 FORM AND CONTROL FEATURES
77              
78             Forms and controls can be extended with common features. These features can clean up the input, check the input for errors, and provide additional processing to forms. Features can be added to an action class or even to a specific instance to modify the form on the fly.
79              
80             =head1 METHODS
81              
82             =head2 new_interface
83              
84             my $interface = Form::Factory->new_interface($name, \%options);
85              
86             This creates a L<Form::Factory::Interface> object with the given options. This is, more or less, a shortcut for:
87              
88             my $interface_class = Form::Factory->interface_class($name);
89             my $interface = $interface_class->new(\%options);
90              
91             =cut
92              
93             sub new_interface {
94 0     0 1   my $class = shift;
95 0           my $name = shift;
96 0           my $class_name = $class->interface_class($name);
97 0           return $class_name->new(@_);
98             }
99              
100             =head2 interface_class
101              
102             my $class_name = Form::Factory->interface_class('HTML');
103              
104             Returns the interface class for the named interface. This loads the interface class from the L<Form::Factory::Interface> namespace.
105              
106             See L</CLASS LOADING>.
107              
108             =cut
109              
110 0     0 1   sub interface_class { _load_class_from_name(Interface => $_[1]) }
111              
112             =head2 control_class
113              
114             my $class_name = Form::Factory->control_class('full_text');
115              
116             Returns the control class for the named control. This loads the control class from the L<Form::Factory::Control> namespace.
117              
118             See L</CLASS LOADING>.
119              
120             =cut
121              
122 0     0 1   sub control_class { _load_class_from_name(Control => $_[1]) }
123              
124             =head2 feature_class
125              
126             my $class_name = Form::Factory->feature_class('functional');
127              
128             Returns the feature class for the named feature. This loads the feature class from the L<Form::Factory::Feature> namespace.
129              
130             See L</CLASS LOADING>.
131              
132             =cut
133              
134 0     0 1   sub feature_class { _load_class_from_name(Feature => $_[1]) }
135              
136             =head2 control_feature_class
137              
138             my $class_name = Form::Factory->control_feature_class('required');
139              
140             Returns the control feature class for the named control feature. This loads the control feature class from the L<Form::Factory::Feature::Control> namespace.
141              
142             See L</CLASS LOADING>.
143              
144             =cut
145              
146 0     0 1   sub control_feature_class { _load_class_from_name('Feature::Control' => $_[1]) }
147              
148             =head1 CLASS LOADING
149              
150             This package features a few class loading methods. These methods each load a type of class. The type of class depends on the namespace they are based upon (which is mentioned in the documentation for each class loading method).
151              
152             Each namespace is divided into two segments: the reserved namespace and the custom namespace. The reserved namespace is reserved for use by the L<Form::Factory> library itself. These will be any class directly under the namespace given.
153              
154             For example, interface classes will always be directly under L<Form::Factory::Interface>, such as L<Form::Factory::Interface::HTML> and L<Form::Factory::Interface::CLI>.
155              
156             The custom namespaces are implemented as an alias under the C<Custom> package namespace. You first define a custom package, which contains a C<register_implementation>, which returns the name of a package that actually implements that class.
157              
158             For example, you might create an interface class specific to your app. You might define a class as follows:
159              
160             package Form::Factory::Interface::Custom::MyAppHTML;
161             sub register_implementation { 'MyApp::Form::Factory::Interface::HTML' }
162              
163             package MyApp::Form::Factory::Interface::HTML;
164             use Moose;
165              
166             extends qw( Form::Factory::Interface::HTML );
167              
168             # implementation here...
169              
170             Any custom name is similar. You could then retrieve your custom name via:
171              
172             my $class = Form::Factory->interface_class('MyAppHTML');
173              
174             Though, you probably actually want:
175              
176             my $interface = Form::Factory->new_interface('MyAppHTML');
177              
178             =cut
179              
180             sub _class_name_from_name {
181 0     0     my ($prefix, $name) = @_;
182              
183             # Remove anything like #Foo, which is used to differentiate between features
184             # added by different classes in get_all_features()
185 0           $name =~ s/\#(.*)$//;
186              
187             # Turn a foo_bar_baz name into FooBarBaz
188 0           $name =~ s/(?:[^A-Za-z]+|^)([A-Za-z])/\U$1/g;
189              
190 0           return join('::', 'Form::Factory', $prefix, ucfirst $name);
191             }
192              
193             sub _load_class_from_name {
194 0     0     my ($given_type, $name) = @_;
195 0           my $ERROR;
196              
197 0           my $custom_type = join('::', $given_type, 'Custom');
198 0           for my $type ($given_type, $custom_type) {
199 0           my $class_name = _class_name_from_name($type, $name);
200              
201 0 0         if (not eval { Class::Load::load_class($class_name) }) {
  0 0          
202 0 0 0       $ERROR ||= $@ if $@;
203 0   0       $ERROR ||= "failed to load $type class named $name";;
204             }
205             elsif ($type eq $custom_type) {
206 0           $class_name = $class_name->register_implementation;
207              
208 0 0         if (eval { Class::Load::load_class($class_name) }) {
  0            
209 0           return $class_name;
210             }
211             else {
212 0           undef $ERROR;
213 0 0 0       $ERROR ||= $@ if $@;
214 0   0       $ERROR ||= "failed to load $type class named $name";;
215             }
216             }
217             else {
218 0           return $class_name;
219             }
220             }
221              
222 0           Carp::croak($ERROR);
223             }
224              
225             =head1 TODO
226              
227             This is not definite, but some things I know as of right now I'm not happy with:
228              
229             =over
230              
231             =item *
232              
233             There are lots of tweaks coming to controls.
234              
235             =item *
236              
237             Features do not do very much yet, but they must do more, especially control features. I want features to be able to modify control construction, add interface-specific functionality for rendering and consuming, etc. They will be bigger and badder, but this might mean who knows what needs to change elsewhere.
238              
239             =item *
240              
241             The interfaces are kind of stupid at this point. They probably need a place to put their brains so they can some more interesting work.
242              
243             =back
244              
245             =head1 CODE REPOSITORY
246              
247             If you would like to take a look at the latest progress on this software, please see the Github repository: L<http://github.com/zostay/FormFactory>
248              
249             =head1 BUGS
250              
251             Please report any bugs you find to the Github issue tracker: L<http://github.com/zostay/FormFactory/issues>
252              
253             If you need help getting started or something (the documentation was originally thrown together over my recent vacation, so it's probably lacking and wonky), you can also contact me on Twitter (L<http://twitter.com/zostay>) or by L<email|/AUTHOR>.
254              
255             =head1 SEE ALSO
256              
257             L<Form::Factory::Interface::CLI>, L<Form::Factory::Interface::HTML>
258              
259             =head1 AUTHOR
260              
261             Andrew Sterling Hanenkamp C<< <hanenkamp@cpan.org> >>
262              
263             =head1 COPYRIGHT AND LICENSE
264              
265             Copyright 2009 Qubling Software LLC.
266              
267             This library is free software. You can redistribute it and/or modify
268             it under the same terms as Perl itself.
269              
270             =cut
271              
272             1;