File Coverage

lib/UR/Namespace/Command/Show/Subclasses.pm
Criterion Covered Total %
statement 12 129 9.3
branch 0 38 0.0
condition 0 12 0.0
subroutine 4 13 30.7
pod 0 7 0.0
total 16 199 8.0


line stmt bran cond sub pod time code
1             package UR::Namespace::Command::Show::Subclasses;
2              
3 1     1   23 use strict;
  1         1  
  1         28  
4 1     1   3 use warnings;
  1         1  
  1         23  
5 1     1   4 use UR;
  1         1  
  1         5  
6 1     1   382 use YAML;
  1         2980  
  1         1027  
7              
8             my $spacing = '';
9              
10             class UR::Namespace::Command::Show::Subclasses {
11             is => 'Command::V2',
12             has=> [
13             superclass => {
14             is => 'Text',
15             shell_args_position => 1,
16             doc => 'Only show subclasses of this class.',
17             },
18             color => {
19             is => 'Boolean',
20             is_optional => 1,
21             default_value => 1,
22             doc => 'Display in color.',
23             },
24             maximum_depth => {
25             is => 'Int',
26             is_optional => 1,
27             default_value => -1,
28             doc => 'Maximum subclass depth. Negative means infinite.',
29             },
30             recalculate => {
31             is => 'Boolean',
32             is_optional => 1,
33             default_value => 0,
34             doc => 'Recreate the cache instead of using the results of a previous run.',
35             },
36             flat => {
37             is => 'Boolean',
38             is_optional => 1,
39             doc => 'Simply prints the subclass names with no other formatting or coloring.',
40             }
41             ],
42             doc => 'Display subclasses of a given class.',
43             };
44              
45             sub help_synopsis {
46 0     0 0   my $self = shift;
47 0           my $result .= <
48             Displays a tree of subclasses of a given class.
49             EOP
50 0           return $result;
51             }
52              
53             sub help_detail {
54 0     0 0   my $self = shift;
55 0           my $result .= <
56             Displays a tree containing the names (and optionally other info) of the subclasses
57             of a given class.
58              
59             ur show subclasses
60             ur show subclasses
61              
62             EOP
63 0           return $result;
64             }
65              
66             sub _mine_tree_for_class {
67 0     0     my ($tree, $name, $result) = @_;
68              
69 0 0         if(ref($tree) eq 'HASH') {
    0          
70 0           for my $key (keys %{$tree}) {
  0            
71 0 0         if($key eq $name) {
72 0           push(@{$result}, 1);
  0            
73             } else {
74 0           _mine_tree_for_class($tree->{$key}, $name, $result);
75             }
76             }
77             } elsif(ref($tree) eq 'ARRAY') {
78 0           for my $item (@{$tree}) {
  0            
79 0 0         if($item eq $name) {
80 0           push(@{$result}, 1);
  0            
81             }
82 0           _mine_tree_for_class($item, $name, $result);
83             }
84             }
85 0           return;
86             }
87              
88             sub execute {
89 0     0     my ($self) = @_;
90 0           my $indexfile = '/tmp/.ur_class_index';
91              
92 0           my $subclass_index_ref;
93 0 0 0       if($self->recalculate or (not -e $indexfile)) {
94 0           my $test_use_cmd = UR::Namespace::Command::Test::Use->create();
95 0           $test_use_cmd->execute();
96              
97 0           $subclass_index_ref = {};
98 0           create_subclass_index('UR::Object', $subclass_index_ref);
99              
100 0           my %subclass_index = %{$subclass_index_ref};
  0            
101 0           open(my $output_fh, '>', $indexfile);
102 0           for my $key (keys %subclass_index) {
103             print $output_fh sprintf("%s %s\n", $key,
104 0           join("\t", @{$subclass_index{$key}}));
  0            
105             }
106 0           close($output_fh);
107             } else {
108 0           $subclass_index_ref = parse_subclass_index_file($indexfile);
109             }
110              
111             # check to see if superclass is even in the subclass_index
112 0           my @result;
113 0           _mine_tree_for_class($subclass_index_ref, $self->superclass, \@result);
114 0 0         unless(@result) {
115 0 0         my $class_name = $self->color ?
116             Term::ANSIColor::colored($self->superclass, 'red') :
117             $self->superclass;
118 0           printf "%s is not a valid class, check your spelling or " .
119             "see --help (recalculate).\n", $class_name;
120 0           return;
121             }
122              
123 0 0         if($self->flat) {
124 0           $self->display_subclasses_flat($subclass_index_ref,
125             $self->superclass, 0)
126             } else {
127 0           $self->display_subclasses($subclass_index_ref,
128             $self->superclass, '', ' ', 0);
129             }
130              
131 0           return 1;
132             }
133              
134             sub create_subclass_index {
135 0     0 0   my ($seed, $index_ref) = @_;
136              
137 0           my @children = $seed->__meta__->subclasses_loaded;
138 0           for my $child (@children) {
139 0           my @parents = @{$child->__meta__->{is}};
  0            
140 0           for my $parent (@parents) {
141 0 0         if($index_ref->{$parent}) {
142 0           push(@{$index_ref->{$parent}}, $child);
  0            
143             } else {
144 0           $index_ref->{$parent} = [$child];
145             }
146             }
147             }
148             }
149              
150             sub parse_subclass_index_file {
151 0     0 0   my ($indexfile) = @_;
152              
153 0           open(IN, '<', $indexfile);
154 0           my %index;
155 0           while(my $line = ) {
156 0           chomp($line);
157 0 0         if($line) {
158 0           my ($parent, $rest) = split(/ /, $line);
159 0 0         if($rest) {
160 0           my @children = split('\t', $rest);
161 0           $index{$parent} = \@children;
162             } else {
163 0           $index{$parent} = [];
164             }
165             }
166             }
167 0           return \%index
168             }
169              
170             sub display_subclasses_flat {
171 0     0 0   my ($self, $index_ref, $name, $depth) = @_;
172 0           my $maximum_depth = $self->maximum_depth;
173 0 0 0       if($depth == $maximum_depth + 1 and $maximum_depth != -1) {
174 0           return;
175             }
176 0           print "$name\n";
177              
178             # get the children
179 0           my $children_ref = $index_ref->{$name};
180 0           my @children;
181 0 0         if($children_ref) {
182 0           @children = @{$index_ref->{$name}};
  0            
183             } else { # if it isn't in index it has no children.
184 0           @children = ();
185             }
186              
187             # loop over children
188 0           for my $child (@children) {
189 0           $self->display_subclasses_flat($index_ref, $child, $depth+1);
190             }
191             }
192              
193             sub display_subclasses {
194 0     0 0   my ($self, $index_ref, $name, $global_prefix, $personal_prefix, $depth) = @_;
195 0           my $maximum_depth = $self->maximum_depth;
196              
197 0           my ($dgp, $dpp, $dn) = ($global_prefix, $personal_prefix, $name);
198 0 0         if($self->color) {
199 0           ($dgp, $dpp, $dn) = colorize_output($global_prefix, $personal_prefix,
200             $name, $self->superclass);
201             }
202 0           print join('', $dgp, $dpp, $spacing, $dn);
203              
204 0 0         my $o = ($personal_prefix =~ /^\|/ ) ? '|' : ' ';
205 0           my $child_global_prefix = sprintf("%s%s %s", $global_prefix, $o, $spacing);
206              
207             # get the children
208 0           my $children_ref = $index_ref->{$name};
209 0           my @children;
210 0 0         if($children_ref) {
211 0           @children = @{$index_ref->{$name}};
  0            
212             } else { # if it isn't in index it has no children.
213 0           @children = ();
214             }
215              
216             # loop over children
217 0           my $len_children = scalar(@children);
218 0 0 0       if($len_children and $depth == $maximum_depth and $maximum_depth != -1) {
      0        
219 0           print " ...\n";
220 0           return;
221             }
222 0           print "\n";
223              
224 0           my $i = 1;
225 0           for my $child (@children) {
226 0 0         my $child_personal_prefix = ($len_children == $i) ? '`-' : '|-';
227              
228 0           $self->display_subclasses($index_ref, $child, $child_global_prefix,
229             $child_personal_prefix, $depth+1);
230 0           $i += 1;
231             }
232             }
233              
234             sub colorize_output {
235 0     0 0   my ($global_prefix, $personal_prefix, $name, $superclass) = @_;
236              
237 0           my $dgp = Term::ANSIColor::colored($global_prefix, 'white');
238 0           my $dpp = Term::ANSIColor::colored($personal_prefix, 'white');
239 0           my $name_prefix = $name;
240 0 0         if($name_prefix =~ /^($superclass)/) {
241 0           $name_prefix = $superclass;
242             } else {
243 0           $name_prefix = '';
244             }
245 0           my $name_suffix = $name;
246 0           $name_suffix =~ s/^($superclass)//;
247 0           my $dn = sprintf("%s%s", Term::ANSIColor::colored($name_prefix, 'white'), $name_suffix );
248              
249 0           return ($dgp, $dpp, $dn);
250             }
251              
252             1;