File Coverage

blib/lib/ExtJS/AutoForm/Moose/Util.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package ExtJS::AutoForm::Moose::Util;
2              
3 1     1   1361 use warnings;
  1         2  
  1         25  
4 1     1   5 use strict;
  1         2  
  1         28  
5              
6 1     1   5 use Carp qw(carp);
  1         1  
  1         56  
7 1     1   37 use ExtJS::AutoForm::Moose::Types;
  0            
  0            
8              
9             #
10             # This module contains pure functional code that recurses the mop hierarchy
11             # to generate the output.
12             #
13              
14             #IDEA: We should allow memoizing this shit since in most cases, classes do not dynamically change
15             # at runtime, and recursion cost is really unnecessary.
16             # Yet, check memory usage and consider implementing some kind of specific caching instead
17             sub _recursive_reflect {
18             my ($meta, $obj, $options, $done, $tlmeta) = @_;
19             my @extjs = ();
20             $done ||= {pkgs => {}, attributes => {}};
21             $tlmeta ||= $meta;
22              
23             $done->{pkgs}{$meta->name} = 1;
24              
25             # Recurse superclasses when possible
26             if($meta->isa("Class::MOP::Class")) {
27             foreach my $pkg ($meta->superclasses) {
28             my $meta = Class::MOP::Class->initialize($pkg);
29             next if $done->{pkgs}{$meta->name};
30             push @extjs, _recursive_reflect($meta, $obj, $options, $done, $tlmeta);
31             $done->{pkgs}{$meta->name} = 1;
32             }
33             }
34              
35             # Recurse roles
36             foreach my $pkg ($meta->calculate_all_roles) {
37             next if $done->{pkgs}{$pkg->name};
38             push @extjs, _recursive_reflect($pkg, $obj, $options, $done, $tlmeta);
39             $done->{pkgs}{$pkg->name} = 1;
40             }
41              
42             # Parse class attributes
43             my @atts = ();
44             foreach my $at ( sort { $a->insertion_order <=> $b->insertion_order } map { $meta->get_attribute($_) } $meta->get_attribute_list ) {
45             next if $done->{attributes}{$at->name};
46             my $final_attribute = $tlmeta->find_attribute_by_name($at->name);
47             push @atts, _attribute_to_extjs($final_attribute, $obj, $options);
48             $done->{attributes}{$at->name} = 1;
49             }
50              
51             if( $options->{hierarchy} ) {
52             # If any, wrap them up in a field group with class/role name
53             if(@atts) {
54             push @extjs, {
55             xtype => "fieldset",
56             title => _cleanup_package_name($meta->name, $options),
57             items => [@atts],
58             };
59             }
60             } else {
61             push @extjs, @atts;
62             }
63              
64             return @extjs;
65             }
66              
67             sub _recursive_reflect_type {
68             my $type_constraint = shift;
69              
70             # Registered type
71             return $ExtJS::AutoForm::Moose::Types::REGISTRY{$type_constraint->name}
72             if defined $ExtJS::AutoForm::Moose::Types::REGISTRY{$type_constraint->name};
73              
74             # Enum hack
75             return $ExtJS::AutoForm::Moose::Types::REGISTRY{__ENUM__}
76             if $type_constraint->isa("Moose::Meta::TypeConstraint::Enum");
77              
78             # TypeConstraint recursion
79             return _recursive_reflect_type($type_constraint->parent) if $type_constraint->has_parent;
80              
81             # Unknown TypeConstraint
82             return sub { {} };
83             }
84              
85             sub _attribute_to_extjs {
86             my ($attribute, $obj, $options) = @_;
87             my $extjs;
88              
89             if($attribute->has_type_constraint) {
90             $extjs = _recursive_reflect_type($attribute->type_constraint)->();
91              
92             $extjs->{fieldLabel} = _cleanup_attribute_name($attribute->name, $options)
93             unless exists $extjs->{fieldLabel};
94              
95             $extjs->{name} = $attribute->name
96             unless exists $extjs->{name};
97              
98             # Set attribute to readonly when invoked on an object instance and the attribute has no writer
99             $extjs->{readOnly} = JSON::Any::true
100             if( (!exists $extjs->{readOnly}) and !$options->{no_readonly} and !$attribute->has_write_method );
101            
102             # Execute any subs on the template.. IDEA: DEEP recursion!
103             for ( grep { ref($extjs->{$_}) eq "CODE" } keys(%$extjs) ) {
104             my $val = $extjs->{$_}->($obj,$attribute);
105              
106             if ( $val ) { $extjs->{$_} = $val; }
107             else { delete $extjs->{$_}; }
108             }
109             } else {
110             carp("Attribute has no type constraint to use for reflection");
111             }
112              
113             return $extjs;
114             }
115              
116             sub _cleanup_package_name($$) {
117             my ($name, $options) = @_;
118             my $n = $name; # Does this really affect on inlining decision?
119              
120             if($options->{strip}) {
121             $n =~ s#^$options->{strip}##;
122             }
123              
124             if($options->{classname_cleanup} && $options->{classname_cleanup} eq "cute") {
125             $n =~ s#::# #g;
126             } elsif(ref($options->{cleanup}) eq "CODE") {
127             $n = $options->{cleanup}->($n);
128             }
129              
130             return $n;
131             }
132              
133             sub _cleanup_attribute_name($$) {
134             my ($name, $options) = @_;
135             my $n = $name;
136              
137             if($options->{attribute_cleanup} && $options->{attribute_cleanup} eq "cute") {
138             $n =~ s#_# #g;
139             } elsif(ref($options->{cleanup}) eq "CODE") {
140             $n = $options->{cleanup}->($n);
141             }
142              
143             return $n;
144             }
145              
146             1; # End of ExtJS::AutoForm::Moose::Util