File Coverage

blib/lib/ExtJS/AutoForm/Moose.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package ExtJS::AutoForm::Moose;
2              
3 1     1   22612 use warnings;
  1         2  
  1         29  
4 1     1   5 use strict;
  1         2  
  1         31  
5              
6 1     1   388 use Moose::Role;
  0            
  0            
7             use JSON::Any qw();
8             use ExtJS::AutoForm::Moose::Util;
9              
10             =head1 NAME
11              
12             ExtJS::AutoForm::Moose - Moose role for ExtJS form autogeneration
13              
14             =head1 VERSION
15              
16             Version 0.01
17              
18             =cut
19              
20             our $VERSION = "0.01";
21              
22             =head1 SYNOPSIS
23              
24             B<WARNING:> This module is mostly on it's bare bones, and still under heavy development. This means
25             it is subject to all kinds of refactoring, renaming, incompatible API changes, not to mention the
26             bugs or lack of support for really nice Moose features and extensions (like L<MongoDBx::Class>).
27             See L</DESCRIPTION> and L</TODO> for more details.
28              
29             On the class you want to generate an ExtJS form for:
30              
31             package MyApp::Model::MyEntity;
32              
33             use Moose;
34              
35             extends 'MyApp::Model::MyParentEntity';
36             with 'ExtJS::AutoForm::Moose';
37              
38             has 'some_attribute' => { is => "rw", isa => "Str" };
39             has 'some_other' => { is => "ro", isa => "Num" };
40              
41             Then, somewhere else on your code:
42              
43             # Simple usage on an object instance
44             $entity->extjs_fields;
45             $entity->extjs_fields_json;
46             $entity->extjs_formpanel;
47             $entity->extjs_formpanel_json;
48              
49             # Static usage
50             MyApp::Model::MyParentEntity->extjs_fields;
51             MyApp::Model::MyParentEntity->extjs_formpanel;
52              
53             # Use options to control generation
54             $entity->extjs_form( hierarchy => 1, strip => "MyApp::Model::", cleanup => "cute" );
55              
56             =cut
57              
58             =head1 DESCRIPTION
59              
60             This moose role adds a couple of methods to any Moose class that use introspection to try to generate
61             an array of ExtJS form field descriptions or a formpanel description. If you do not yet know of ExtJS,
62             you can visit it's product page here: L<http://www.sencha.com/products/extjs>.
63              
64             Once a class implements this role, you can call I<extjs_*> methods on an object instance or statically.
65              
66             =head2 Supported moose types
67              
68             So far, just a few base attribute types are supported, namely: B<Str>, B<Num>, B<Int>, B<Bool>, and B<enums>.
69             Any other field will inherit the extjs template definition from the common antecessor B<Any>, which creates a
70             DisplayField control showing an "Unsupported type" message.
71              
72             Anyway, you can define custom templates for any moose types using L<ExtJS::AutoForm::Moose::Types>,
73             which provides a syntax curry very similar to the one used to declare types or attributes.
74              
75             =head2 How it works
76              
77             L<ExtJS::AutoForm::Moose> basically gets the associated Moose::Meta::Class and uses
78             introspection to recursively find the entity attributes.
79              
80             For each attribute, it does a few simple checks on the attribute type to find the matching
81             ExtJS control. Such search will recursively check parent types to provide a default control,
82             so that if you create an I<Str> subtype and do not declare a way to reflect that into an ExtJS
83             control, the default for I<Str> will be used instead.
84              
85             When an association is found, the definition is loaded, and a few common config options are
86             set (unless they have been previously defined on the provided template):
87              
88             =over 4
89              
90             =item - fieldLabel
91              
92             Set to the attribute name, as returned by the cleanup callback (see autoform options)
93              
94             =item - readOnly
95              
96             Set to true for "ro" declared attributes
97              
98             =item - name
99              
100             Set to the attribute name (unparsed)
101              
102             =back
103              
104             =head3 Attribute values
105              
106             Attribute values are a bit special, since the default behaviour changes when the form is generated
107             through a static call to ext_js or as a method call.
108              
109             On the first case, this module will try to fetch the default value for that attribute. There is a
110             caveat, though, since default values can be provided as callbacks and a static call has no object
111             to pass to that callback. This means it will probably crash, but so far, this behaviour is left
112             undefined.
113              
114             When called as a method, though, the actual attribute value of that instance will be used.
115              
116             =head1 METHODS
117              
118             =over
119              
120             =item extjs_fields ( I<options> )
121              
122             Generate fields structure for a given class.
123              
124             This method can be both invoked on an object or directly, passing a valid moose classname as the first parameter.
125              
126             Available options:
127              
128             =over
129              
130             =item hierarchy
131              
132             When set to a true value, this flag will cause attributes to be grouped in ExtJS fieldgroups using the class or role names that declared them.
133              
134             =item strip
135              
136             Used together with I<hierarchy>, will strip off a given string from class names. See L</SYNOPSIS>.
137              
138             =item classname_cleanup
139              
140             Used together with I<hierarchy>, allows to format classnames using a callback. Specifying "cute" will C<s#::# #>.
141              
142             =item attribute_cleanup
143              
144             Allows to format attribute using a callback. Specifying "cute" will C<s#_# #>.
145              
146             =item no_readonly
147              
148             Do not set readOnly config parameters on fields.
149              
150             =back
151              
152             =item extjs_fields_json ( I<options> )
153              
154             This method does exactly the same thing as I<extjs_fields>, but returns the output encoded as JSON using L<JSON::Any>.
155              
156             =item extjs_formpanel ( I<options> )
157              
158             Generate a formpanel containing the reflection for a given object instance or class.
159              
160             All parameters are passed directly to extjs_fields, so it's usage is identical.
161              
162             =item extjs_formpanel_json ( I<options> )
163              
164             This method does exactly the same thing as I<extjs_formpanel>, but returns the output encoded as JSON using L<JSON::Any>.
165              
166             =back
167              
168             =cut
169              
170             sub extjs_fields {
171             my $self = shift;
172             my $meta = Class::MOP::Class->initialize(ref($self) || $self);
173             my $options = {@_};
174              
175             return ExtJS::AutoForm::Moose::Util::_recursive_reflect( $meta, ref($self) ? $self : undef, $options );
176             }
177              
178             sub extjs_fields_json
179             { return JSON::Any->objToJson([shift->extjs_fields(@_)]); }
180              
181             sub extjs_formpanel {
182             my $self = shift;
183             my @fields = $self->extjs_fields(@_);
184              
185             return {
186             xtype => "form",
187             items => [@fields],
188             };
189             }
190              
191             sub extjs_formpanel_json
192             { return JSON::Any->objToJson([shift->extjs_formpanel(@_)]); }
193              
194             =head1 TODO
195              
196             There are still a lot of features to implement and a few dirty hacks to fix before this module is actually useful.
197              
198             This is a list of random ideas to be considered for future versions:
199              
200             =over
201              
202             =item Support for Moose class-typed attributes
203              
204             This must be thoroughly analized to get to a simple and flexible solution
205              
206             =item Support array and hash parametrized types
207              
208             I think there must be a pretty smart way to handle this
209              
210             =item Support MongoDBx::Class attributes
211              
212             This one is even more interesting, and includes referenced and embedded documents.
213              
214             =back
215              
216             =cut
217              
218             =head1 AUTHOR
219              
220             Quim Rovira, C<< met at cpan.org >>
221              
222             =head1 BUGS
223              
224             Please report any bugs or feature requests to C<bug-moosex-extjs-reflection at rt.cpan.org>, or through
225             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=ExtJS-AutoForm-Moose>. I will be notified, and then you'll
226             automatically be notified of progress on your bug as I make changes.
227              
228             =head1 SUPPORT
229              
230             You can find documentation for this module with the perldoc command.
231              
232             perldoc ExtJS::AutoForm::Moose
233              
234              
235             You can also look for information at:
236              
237             =over 4
238              
239             =item * RT: CPAN's request tracker
240              
241             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ExtJS-AutoForm-Moose>
242              
243             =item * AnnoCPAN: Annotated CPAN documentation
244              
245             L<http://annocpan.org/dist/ExtJS-AutoForm-Moose>
246              
247             =item * CPAN Ratings
248              
249             L<http://cpanratings.perl.org/d/ExtJS-AutoForm-Moose>
250              
251             =item * Search CPAN
252              
253             L<http://search.cpan.org/dist/ExtJS-AutoForm-Moose/>
254              
255             =back
256              
257              
258             =head1 LICENSE AND COPYRIGHT
259              
260             Copyright 2011 Quim Rovira.
261              
262             This program is free software; you can redistribute it and/or modify it
263             under the terms of either: the GNU General Public License as published
264             by the Free Software Foundation; or the Artistic License.
265              
266             See http://dev.perl.org/licenses/ for more information.
267              
268             ExtJS trademarks are property of Sencha Labs L<http://www.sencha.com>
269              
270             =cut
271              
272             1; # End of ExtJS::AutoForm::Moose