File Coverage

lib/UR/Namespace/Command/RunsOnModulesInTree.pm
Criterion Covered Total %
statement 26 108 24.0
branch 5 50 10.0
condition 0 8 0.0
subroutine 7 14 50.0
pod 0 8 0.0
total 38 188 20.2


line stmt bran cond sub pod time code
1              
2             # Abstract base command for commands which run on all or part of a class tree.
3              
4             package UR::Namespace::Command::RunsOnModulesInTree;
5              
6 3     3   48 use strict;
  3         3  
  3         80  
7 3     3   9 use warnings;
  3         3  
  3         72  
8              
9 3     3   13 use UR;
  3         4  
  3         20  
10             our $VERSION = "0.46"; # UR $VERSION;
11              
12             UR::Object::Type->define(
13             class_name => __PACKAGE__,
14             is => 'UR::Namespace::Command::Base',
15             has => [
16             classes_or_modules => {
17             is_many => 1,
18             is_optional => 1,
19             shell_args_position => 99
20             }
21             ]
22             );
23              
24              
25             sub is_abstract
26             {
27 0     0 0 0 my $self = shift;
28 0   0     0 my $class = ref($self) || $self;
29 0 0       0 return 1 if $class eq __PACKAGE__;
30 0         0 return;
31             }
32              
33             sub _help_detail_footer
34             {
35 0     0   0 my $text =
36             return <
37             This command requires that the current working directory be under a namespace module.
38              
39             If no modules or class names are specified as parameters, it runs on all modules in the namespace.
40              
41             If modules or class names ARE listed, it will operate only on those.
42              
43             Words containing double-colons will be interpreted as absolute class names.
44              
45             All other words will be interpreted as relative file paths to modules.
46             EOS
47             }
48              
49              
50              
51             sub execute
52             {
53 1     1   2 my $self = shift;
54              
55 1         2 my $params = shift;
56              
57 1         3 my $namespace = $self->namespace_name;
58 1 50       2 unless ($namespace) {
59 0         0 die "This command can only be run from a directory tree under a UR namespace module.\n";
60             }
61              
62 1         4 my @subject_list = $self->classes_or_modules;
63              
64 1 50       16 if ($self->can("for_each_class_object") ne __PACKAGE__->can("for_each_class_object")) {
    0          
    0          
    0          
65              
66 1         29 my @classes = $self->_class_objects_in_tree(@subject_list);
67              
68 1 50       18 unless ($self->before(\@classes)) {
69 0         0 print STDERR "Terminating.\n";
70 0         0 return;
71             }
72 1         3 for my $class (@classes) {
73 1 50       6 unless ($self->for_each_class_object($class)) {
74 0         0 print STDERR "Terminating...\n";
75 0         0 return;
76             }
77             }
78             }
79             elsif ($self->can("for_each_class_name") ne __PACKAGE__->can("for_each_class_name")) {
80 0         0 my @class_names = $self->_class_names_in_tree(@subject_list);
81 0 0       0 unless ($self->before(\@class_names)) {
82 0         0 print STDERR "Terminating.\n";
83 0         0 return;
84             }
85 0         0 for my $class (@class_names) {
86 0 0       0 unless ($self->for_each_class_name($class)) {
87 0         0 print STDERR "Terminating...\n";
88 0         0 return;
89             }
90             }
91             }
92             elsif ($self->can("for_each_module_file") ne __PACKAGE__->can("for_each_module_file")) {
93 0         0 my @modules = $self->_modules_in_tree(@subject_list);
94 0 0       0 unless ($self->before(\@modules)) {
95 0         0 print STDERR "Terminating.\n";
96 0         0 return;
97             }
98 0         0 for my $module (@modules) {
99 0 0       0 unless ($self->for_each_module_file($module)) {
100 0         0 print STDERR "Terminating...\n";
101 0         0 return;
102             }
103             }
104             }
105             elsif ($self->can("for_each_module_file_in_parallel") ne __PACKAGE__->can("for_each_module_file_in_parallel")) {
106 0         0 my @modules = $self->_modules_in_tree(@subject_list);
107 0 0       0 unless ($self->before(\@modules)) {
108 0         0 print STDERR "Terminating.\n";
109 0         0 return;
110             }
111 0         0 my $bucket_count = 10;
112 0         0 my @buckets;
113             my %child_processes;
114 0         0 for my $bucket_number (0..$bucket_count-1) {
115 0   0     0 $buckets[$bucket_number] ||= [];
116             }
117 0         0 while (@modules) {
118 0         0 for my $bucket_number (0..$bucket_count-1) {
119 0         0 my $module = shift @modules;
120 0 0       0 last if not $module;
121 0         0 push @{ $buckets[$bucket_number] }, $module;
  0         0  
122             }
123             }
124              
125 0         0 for my $bucket (@buckets) {
126 0         0 my $child_pid = fork();
127 0 0       0 if ($child_pid) {
128             # the parent process continues forking...
129 0         0 $child_processes{$child_pid} = 1;
130             }
131             else {
132             # the child process does handles its bucket
133 0         0 for my $module (@$bucket) {
134 0 0       0 unless ($self->for_each_module_file_in_parallel($module)) {
135 0         0 exit 1;
136             }
137             }
138             # and then exits quietly
139 0         0 exit 0;
140             }
141             }
142             #$DB::single = 1;
143 0         0 while (keys %child_processes) {
144 0         0 my $child_pid = wait();
145 0 0       0 if ($child_pid == -1) {
146 0         0 print "lost children? " . join(" ", keys %child_processes);
147             }
148 0         0 delete $child_processes{$child_pid};
149             }
150             }
151             else {
152 0         0 die "$self does not implement: for_each_[class_object|class_name|module_file]!";
153             }
154              
155 1 50       34 unless ($self->after()) {
156 0         0 print STDERR "Terminating.\n";
157 0         0 return;
158             }
159              
160 1         5 return 1;
161             }
162              
163             sub before {
164 1     1 0 4 return 1;
165             }
166              
167             sub for_each_module_file {
168 0     0 0 0 die "The for_each_module_file method is not defined by/in " . shift;
169             }
170              
171              
172             sub for_each_class_name {
173 0     0 0 0 die "The for_each_class_name method is not defined by/in " . shift;
174             }
175              
176             sub for_each_class_object {
177 0     0 0 0 Carp::confess "The for_each_class_object method is not defined by/in " . shift;
178             }
179              
180             sub after {
181 1     1 0 3 return 1;
182             }
183              
184             sub loop_methods
185             {
186 0     0 0   my $self = shift;
187 0           my @methods;
188 0           for my $method (qw/
189             for_each_class_object
190             for_each_class_name
191             for_each_module_file
192             for_each_module_file_in_parallel
193             /) {
194 3     3   17 no warnings;
  3         3  
  3         810  
195 0 0         if ($self->can($method) ne __PACKAGE__->can($method)) {
196 0           push @methods, $method;
197             }
198             }
199 0           return @methods;
200             }
201              
202             sub shell_args_description
203             {
204 0     0 0   my $self = shift;
205              
206 0           my @loop_methods = $self->loop_methods;
207 0 0         my $takes_classes = 1 if grep { /class/ } @loop_methods;
  0            
208 0 0         my $takes_modules = 1 if grep { /modul/ } @loop_methods;
  0            
209              
210 0           my $text;
211 0 0 0       if ($takes_classes and $takes_modules) {
    0          
    0          
212 0           $text = "[CLASS|MODULE] [CLASS|MODULE] ...";
213             }
214             elsif ($takes_classes) {
215 0           $text = "[CLASS] [CLASS]..";
216             }
217             elsif ($takes_modules) {
218 0           $text = "[MODULE] [MODULE] ...";
219             }
220             else {
221 0           $text = "";
222             }
223              
224 0           $text .= " " . $self->SUPER::shell_args_description(@_);
225              
226 0 0         if ($self->is_sub_command_delegator) {
227 0           my @names = $self->sub_command_names;
228 0           return "[" . join("|",@names) . "] $text"
229             }
230 0           return $text;
231             }
232              
233              
234             1;