line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Prophet::CLI::Command; |
2
|
39
|
|
|
39
|
|
189
|
use Any::Moose; |
|
39
|
|
|
|
|
57
|
|
|
39
|
|
|
|
|
306
|
|
3
|
|
|
|
|
|
|
|
4
|
39
|
|
|
39
|
|
20598
|
use Prophet::CLI; |
|
39
|
|
|
|
|
69
|
|
|
39
|
|
|
|
|
1280
|
|
5
|
39
|
|
|
39
|
|
192
|
use Params::Validate qw(validate); |
|
39
|
|
|
|
|
63
|
|
|
39
|
|
|
|
|
31889
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
has cli => ( |
8
|
|
|
|
|
|
|
is => 'rw', |
9
|
|
|
|
|
|
|
isa => 'Prophet::CLI', |
10
|
|
|
|
|
|
|
weak_ref => 1, |
11
|
|
|
|
|
|
|
handles => [ |
12
|
|
|
|
|
|
|
qw/app_handle handle config/, |
13
|
|
|
|
|
|
|
], |
14
|
|
|
|
|
|
|
); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
has context => ( |
17
|
|
|
|
|
|
|
is => 'rw', |
18
|
|
|
|
|
|
|
isa => 'Prophet::CLIContext', |
19
|
|
|
|
|
|
|
handles => [ |
20
|
|
|
|
|
|
|
qw/args set_arg arg has_arg delete_arg arg_names/, |
21
|
|
|
|
|
|
|
qw/props set_prop prop has_prop delete_prop prop_names/, |
22
|
|
|
|
|
|
|
'add_to_prop_set', 'prop_set', |
23
|
|
|
|
|
|
|
], |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub ARG_TRANSLATIONS { |
28
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
29
|
0
|
|
|
|
|
|
return ( 'v' => 'verbose', |
30
|
|
|
|
|
|
|
'a' => 'all' ); |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 Registering argument translations |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
This is the Prophet CLI's way of supporting short forms for arguments, |
36
|
|
|
|
|
|
|
e.g. you want to let '-v' be able to used for the same purpose as |
37
|
|
|
|
|
|
|
'--verbose' without dirtying your code checking both or manually |
38
|
|
|
|
|
|
|
setting them if they exist. We want it to be as easy as possible |
39
|
|
|
|
|
|
|
to have short commands. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
To use, have your command subclass do: |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub ARG_TRANSLATIONS { shift->SUPER::ARG_TRANSLATIONS(), f => 'file' }; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
You can register as many translations at a time as you want. |
46
|
|
|
|
|
|
|
The arguments will be translated when the command object is |
47
|
|
|
|
|
|
|
instantiated. If an arg already exists in the arg translation |
48
|
|
|
|
|
|
|
table, it is overwritten with the new value. |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=cut |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub _translate_args { |
53
|
0
|
|
|
0
|
|
|
my $self = shift; |
54
|
0
|
|
|
|
|
|
my %translations = $self->ARG_TRANSLATIONS; |
55
|
|
|
|
|
|
|
|
56
|
0
|
|
|
|
|
|
for my $arg (keys %translations) { |
57
|
0
|
0
|
|
|
|
|
$self->set_arg($translations{$arg}, $self->arg($arg)) |
58
|
|
|
|
|
|
|
if $self->has_arg($arg); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# run arg translations on object instantiation |
63
|
|
|
|
|
|
|
sub BUILD { |
64
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
65
|
|
|
|
|
|
|
|
66
|
0
|
|
|
|
|
|
$self->_translate_args(); |
67
|
|
|
|
|
|
|
|
68
|
0
|
|
|
|
|
|
return $self; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub fatal_error { |
72
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
73
|
0
|
|
|
|
|
|
my $reason = shift; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# always skip this fatal_error function when generating a stack trace |
76
|
0
|
|
|
|
|
|
local $Carp::CarpLevel = $Carp::CarpLevel + 1; |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
|
die $reason . "\n"; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head2 require_uuid |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Checks to make sure the uuid attribute is set. Prints an error and dies |
84
|
|
|
|
|
|
|
with the command's usage string if it is not set. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub require_uuid { |
89
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
90
|
|
|
|
|
|
|
|
91
|
0
|
0
|
|
|
|
|
if (!$self->has_uuid) { |
92
|
0
|
|
|
|
|
|
my $type = $self->type; |
93
|
0
|
|
|
|
|
|
my $name = (split /::/, $self->meta->name)[-1]; |
94
|
0
|
|
|
|
|
|
warn "No UUID or LUID given!\n"; |
95
|
0
|
|
|
|
|
|
$self->print_usage; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head2 edit_text [text] -> text |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Filters the given text through the user's C<$EDITOR> using |
102
|
|
|
|
|
|
|
L. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=cut |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub edit_text { |
107
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
108
|
0
|
|
|
|
|
|
my $text = shift; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# don't invoke the editor in a script, the test will appear to hang |
111
|
|
|
|
|
|
|
#die "Tried to invoke an editor in a test script!" if $ENV{IN_PROPHET_TEST_COMMAND}; |
112
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
require Proc::InvokeEditor; |
114
|
0
|
|
|
|
|
|
return scalar Proc::InvokeEditor->edit($text); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head2 edit_hash hash => hashref, ordering => arrayref |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Filters the hash through the user's C<$EDITOR> using L. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
No validation is done on the input or output. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
If the optional ordering argument is specified, hash keys will be presented |
127
|
|
|
|
|
|
|
in that order (with unspecified elements following) for edit. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
If the record class for the current type defines a C |
130
|
|
|
|
|
|
|
routine, those props will not be presented for editing. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
False values are not returned unless a prop is removed from the output. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=cut |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub edit_hash { |
137
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
138
|
0
|
|
|
|
|
|
validate( @_, { hash => 1, ordering => 0 } ); |
139
|
0
|
|
|
|
|
|
my %args = @_; |
140
|
0
|
|
|
|
|
|
my $hash = $args{'hash'}; |
141
|
0
|
0
|
|
|
|
|
my @ordering = @{ $args{'ordering'} || [] }; |
|
0
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
|
my $record = $self->_get_record_object; |
143
|
0
|
0
|
|
|
|
|
my @do_not_edit = $record->can('immutable_props') ? $record->immutable_props : (); |
144
|
|
|
|
|
|
|
|
145
|
0
|
0
|
|
|
|
|
if (@ordering) { |
146
|
|
|
|
|
|
|
# add any keys not in @ordering to the end of it |
147
|
0
|
|
|
|
|
|
my %keys_in_ordering; |
148
|
0
|
0
|
|
|
|
|
map { $keys_in_ordering{$_} = 1 if exists($hash->{$_}) } @ordering; |
|
0
|
|
|
|
|
|
|
149
|
0
|
0
|
|
|
|
|
map { push @ordering, $_ if !exists($keys_in_ordering{$_}) } keys %$hash; |
|
0
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
} else { |
151
|
0
|
|
|
|
|
|
@ordering = sort keys %$hash; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# filter out props we don't want to present for editing |
155
|
0
|
|
|
|
|
|
my %do_not_edit = map { $_ => 1 } @do_not_edit; |
|
0
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
|
@ordering = grep { !$do_not_edit{$_} } @ordering; |
|
0
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
0
|
|
|
|
|
|
my $input = join "\n", map { "$_: $hash->{$_}" } @ordering; |
|
0
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
my $output = $self->edit_text($input); |
161
|
|
|
|
|
|
|
|
162
|
0
|
0
|
|
|
|
|
die "Aborted.\n" if $input eq $output; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# parse the output |
165
|
0
|
|
|
|
|
|
my $filtered = {}; |
166
|
0
|
|
|
|
|
|
for my $line (split "\n", $output) { |
167
|
0
|
0
|
|
|
|
|
if ($line =~ m/^([^:]+):\s*(.*)$/) { |
168
|
0
|
|
|
|
|
|
my $prop = $1; |
169
|
0
|
|
|
|
|
|
my $val = $2; |
170
|
|
|
|
|
|
|
# don't return empty values |
171
|
0
|
0
|
|
|
|
|
$filtered->{$prop} = $val unless !($val); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
39
|
|
|
39
|
|
257
|
no warnings 'uninitialized'; |
|
39
|
|
|
|
|
67
|
|
|
39
|
|
|
|
|
31417
|
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# if a key is deleted intentionally, set its value to '' |
177
|
0
|
|
|
|
|
|
for my $prop (keys %$hash) { |
178
|
0
|
0
|
0
|
|
|
|
if (!exists $filtered->{$prop} and ! exists $do_not_edit{$prop}) { |
179
|
0
|
|
|
|
|
|
$filtered->{$prop} = ''; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# filter out unchanged keys as they clutter changesets if they're set again |
184
|
0
|
0
|
|
|
|
|
map { delete $filtered->{$_} if $hash->{$_} eq $filtered->{$_} } keys %$filtered; |
|
0
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
|
return $filtered; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 edit_props arg => str, defaults => hashref, ordering => arrayref |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Returns a hashref of the command's props mixed in with any default props. |
192
|
|
|
|
|
|
|
If the "arg" argument is specified, (default "edit", use C if you only |
193
|
|
|
|
|
|
|
want default arguments), then L is invoked on the property list. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
If the C argument is specified, properties will be presented in that |
196
|
|
|
|
|
|
|
order (with unspecified props following) if filtered through L. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub edit_props { |
201
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
202
|
0
|
|
|
|
|
|
my %args = @_; |
203
|
0
|
|
0
|
|
|
|
my $arg = $args{'arg'} || 'edit'; |
204
|
0
|
|
|
|
|
|
my $defaults = $args{'defaults'}; |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
my %props; |
207
|
0
|
0
|
|
|
|
|
if ($defaults) { |
208
|
0
|
|
|
|
|
|
%props = (%{ $defaults }, %{ $self->props }); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
} else { |
210
|
0
|
|
|
|
|
|
%props = %{$self->props}; |
|
0
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
0
|
0
|
|
|
|
|
if ($self->has_arg($arg)) { |
214
|
0
|
|
|
|
|
|
return $self->edit_hash(hash => \%props, ordering => $args{'ordering'}); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
0
|
|
|
|
|
|
return \%props; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head2 prompt_choices question |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Asks user the question and returns 0 if answer was the second choice, |
223
|
|
|
|
|
|
|
1 otherwise. (First choice is the default.) |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub prompt_choices { |
228
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
229
|
0
|
|
|
|
|
|
my ($choice1, $choice2, $question) = @_; |
230
|
|
|
|
|
|
|
|
231
|
0
|
|
|
|
|
|
$choice1 = uc $choice1; # default is capsed |
232
|
0
|
|
|
|
|
|
$choice2 = lc $choice2; # non-default is lowercased |
233
|
|
|
|
|
|
|
|
234
|
0
|
|
|
|
|
|
Prophet::CLI->end_pager(); |
235
|
0
|
|
|
|
|
|
print "$question [$choice1/$choice2]: "; |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
chomp( my $answer = ); |
238
|
|
|
|
|
|
|
|
239
|
0
|
|
|
|
|
|
Prophet::CLI->start_pager(); |
240
|
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
return $answer !~ /^$choice2$/i; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head2 prompt_Yn question |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
Asks user the question and returns true if answer was positive or false |
247
|
|
|
|
|
|
|
otherwise. Default answer is 'Yes' (returns true). |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=cut |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub prompt_Yn { |
252
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
253
|
0
|
|
|
|
|
|
my $msg = shift; |
254
|
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
return $self->prompt_choices( 'y', 'n', $msg ); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Create a new [replica] config file section for the given replica if |
259
|
|
|
|
|
|
|
# it hasn't been seen before (config section doesn't already exist) |
260
|
|
|
|
|
|
|
sub record_replica_in_config { |
261
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
262
|
0
|
|
|
|
|
|
my $replica_url = shift; |
263
|
0
|
|
|
|
|
|
my $replica_uuid = shift; |
264
|
0
|
|
0
|
|
|
|
my $url_variable = shift || 'url'; |
265
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
|
my %previous_sources_by_uuid |
267
|
|
|
|
|
|
|
= $self->app_handle->config->sources( |
268
|
|
|
|
|
|
|
by_variable => 1, |
269
|
|
|
|
|
|
|
variable => 'uuid', |
270
|
|
|
|
|
|
|
); |
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
my $found_prev_replica = $previous_sources_by_uuid{$replica_uuid}; |
273
|
|
|
|
|
|
|
|
274
|
0
|
0
|
|
|
|
|
if ( !$found_prev_replica ) { |
|
|
0
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# replica section doesn't exist at all; create a new one |
276
|
0
|
|
|
|
|
|
my $url = $replica_url; |
277
|
0
|
|
|
|
|
|
$self->app_handle->config->group_set( |
278
|
|
|
|
|
|
|
$self->app_handle->config->replica_config_file, |
279
|
|
|
|
|
|
|
[ |
280
|
|
|
|
|
|
|
{ |
281
|
|
|
|
|
|
|
key => "replica.$url.$url_variable", |
282
|
|
|
|
|
|
|
value => $replica_url, |
283
|
|
|
|
|
|
|
}, |
284
|
|
|
|
|
|
|
{ |
285
|
|
|
|
|
|
|
key => "replica.$url.uuid", |
286
|
|
|
|
|
|
|
value => $replica_uuid, |
287
|
|
|
|
|
|
|
}, |
288
|
|
|
|
|
|
|
], |
289
|
|
|
|
|
|
|
); |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
elsif ( $found_prev_replica ne $replica_url ) { |
292
|
|
|
|
|
|
|
# We're publishing to a different place than where it was published |
293
|
|
|
|
|
|
|
# to previously--we don't want to end up with a multivalue in the |
294
|
|
|
|
|
|
|
# config file, so just replace the old value. |
295
|
0
|
|
|
|
|
|
my $name = $self->app_handle->display_name_for_replica($replica_uuid); |
296
|
0
|
|
|
|
|
|
$self->app_handle->config->set( |
297
|
|
|
|
|
|
|
filename => $self->app_handle->config->replica_config_file, |
298
|
|
|
|
|
|
|
key => "replica.$name.$url_variable", |
299
|
|
|
|
|
|
|
value => $replica_url, |
300
|
|
|
|
|
|
|
); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head2 print_usage |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Print the command's usage message to STDERR and die. Commands should |
307
|
|
|
|
|
|
|
implement C, which returns the usage message. |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
If the usage message method needs arguments passed in, use a closure. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub print_usage { |
314
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
315
|
|
|
|
|
|
|
my %args = ( |
316
|
0
|
|
|
0
|
|
|
usage_method => sub { $self->usage_msg }, |
317
|
0
|
|
|
|
|
|
@_, |
318
|
|
|
|
|
|
|
); |
319
|
|
|
|
|
|
|
|
320
|
0
|
|
|
|
|
|
die $args{usage_method}(); |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head2 get_cmd_and_subcmd_names [no_type => 1] |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Gets the name of the script that was run and the primary commands that were |
326
|
|
|
|
|
|
|
specified on the command-line. If a true boolean is passed in as C, |
327
|
|
|
|
|
|
|
won't add '' to the subcmd if no type was passed in via the |
328
|
|
|
|
|
|
|
primary commands. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=cut |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub get_cmd_and_subcmd_names { |
333
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
334
|
0
|
|
|
|
|
|
my %args = @_; |
335
|
|
|
|
|
|
|
|
336
|
0
|
|
|
|
|
|
my $cmd = $self->cli->get_script_name; |
337
|
0
|
|
|
|
|
|
my @primary_commands = @{ $self->context->primary_commands }; |
|
0
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# if primary commands was only length 1, the type was not specified |
340
|
|
|
|
|
|
|
# and we should indicate that a type is expected |
341
|
|
|
|
|
|
|
push @primary_commands, '' |
342
|
0
|
0
|
0
|
|
|
|
if @primary_commands <= 1 && !$args{no_type}; |
343
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
my $type_and_subcmd = join( q{ }, @primary_commands ); |
345
|
|
|
|
|
|
|
|
346
|
0
|
|
|
|
|
|
return ($cmd, $type_and_subcmd); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
350
|
39
|
|
|
39
|
|
249
|
no Any::Moose; |
|
39
|
|
|
|
|
67
|
|
|
39
|
|
|
|
|
195
|
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
1; |
353
|
|
|
|
|
|
|
|