File Coverage

lib/Command/SubCommandFactory.pm
Criterion Covered Total %
statement 69 80 86.2
branch 13 20 65.0
condition 1 3 33.3
subroutine 9 13 69.2
pod n/a
total 92 116 79.3


line stmt bran cond sub pod time code
1             package Command::SubCommandFactory;
2              
3 1     1   454 use strict;
  1         1  
  1         23  
4 1     1   3 use warnings;
  1         1  
  1         20  
5 1     1   3 use UR;
  1         1  
  1         8  
6              
7             class Command::SubCommandFactory {
8             is => 'Command::Tree',
9             is_abstract => 1,
10             doc => 'Base class for commands that delegate to sub-commands that may need to be dynamically created',
11             };
12              
13             sub _init_subclass {
14 2     2   3 my $subclass = shift;
15 2         6 my $meta = $subclass->__meta__;
16 2 100       7 if (grep { $_ eq __PACKAGE__ } $meta->parent_class_names) {
  2         9  
17 1         2 my $delegating_class_name = $subclass;
18 1     0   40 eval "sub ${subclass}::_delegating_class_name { '$delegating_class_name' }";
  0         0  
19             }
20              
21 2         6 return 1;
22             }
23              
24             sub _build_sub_command_mapping {
25 1     1   2 my ($class) = @_;
26              
27 1 50       4 unless ($class->can('_sub_commands_from')) {
28 0         0 die "Class $class does not implement _sub_commands_from()!\n"
29             . "This method should return the namespace to use a reference "
30             . "for defining sub-commands."
31             }
32 1         9 my $ref_class = $class->_sub_commands_from;
33              
34 1         3 my @inheritance;
35 1 50 33     3 if ($class->can('_sub_commands_inherit_from') and defined $class->_sub_commands_inherit_from) {
36 0         0 @inheritance = $class->_sub_commands_inherit_from();
37             }
38             else {
39 1         2 @inheritance = $class;
40             }
41              
42 1         1 my $module = $ref_class;
43 1         4 $module =~ s/::/\//g;
44 1         2 $module .= '.pm';
45 1         2 my $base_path = $INC{$module};
46 1 50       5 unless ($base_path) {
47 1 50       4 if (UR::Object::Type->get($ref_class)) {
48 1         2 $base_path = $INC{$module};
49             }
50 1 50       3 unless ($base_path) {
51 0         0 die "Failed to find the path for ref class $ref_class!";
52             }
53             }
54 1         14 $base_path =~ s/$module//;
55              
56 1         1 my $ref_path = $ref_class;
57 1         5 $ref_path =~ s/::/\//g;
58 1         3 my $full_ref_path = $base_path . '/' . $ref_path;
59              
60 1         163 my @target_paths = glob("$full_ref_path/*.pm");
61 1         2 my @target_class_names;
62 1         3 for my $target_path (@target_paths) {
63 3         3 my $target = $target_path;
64 3         42 $target =~ s#$base_path\/$ref_path/##;
65 3         9 $target =~ s/\.pm//;
66              
67 3         17 my $target_base_class = $class->_target_base_class;
68 3         11 my $target_class_name = $target_base_class . '::' . $target;
69              
70 3         8 my $target_meta = UR::Object::Type->get($target_class_name);
71 3 50       9 next unless $target_meta;
72 3 100       12 next unless $target_class_name->isa($target_base_class);
73              
74 2         7 push @target_class_names, $target => $target_class_name;
75             }
76 1         3 my %target_classes = @target_class_names;
77              
78             # Create a mapping of command names to command classes, and either find or
79             # create those command classes
80 1         2 my $mapping;
81 1         4 for my $target (sort keys %target_classes) {
82 2         4 my $target_class_name = $target_classes{$target};
83              
84 2         5 my $command_class_name = $class . '::' . $target;
85 2         2 my $command_module_name = $command_class_name;
86 2         7 $command_module_name =~ s|::|/|g;
87 2         3 $command_module_name .= '.pm';
88              
89             # If the command class already exists, load it. Otherwise, create one.
90 2 50       3 if (grep { -e $_ . '/' . $command_module_name } @INC) {
  24         235  
91 0         0 UR::Object::Type->get($command_class_name);
92             }
93             else {
94 2 100       10 next if not $class->_build_sub_command($command_class_name, @inheritance);
95             }
96              
97             # Created commands need to know where their parameters came from
98 1     1   5 no warnings 'redefine';
  1         1  
  1         46  
99 1     0   36 eval "sub ${command_class_name}::_target_class_name { '$target_class_name' }";
  0         0  
100 1     1   4 use warnings;
  1         1  
  1         141  
101              
102 1         11 my $command_name = $class->_command_name_for_class_word($target);
103 1         3 $mapping->{$command_name} = $command_class_name;
104             }
105              
106 1         16 return $mapping;
107             }
108              
109             sub _build_sub_command {
110 0     0   0 my ($self, $class_name, @inheritance) = @_;
111 0         0 class {$class_name} {
  0         0  
112             is => \@inheritance,
113             doc => '',
114             };
115 0         0 return $class_name;
116             }
117              
118 3     3   9 sub _target_base_class { return $_[0]->_sub_commands_from; }
119 0     0   0 sub _target_class_name { undef }
120 1     1   12 sub _sub_commands_inherit_from { undef }
121              
122             1;
123