line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DateTime::Event::Cron; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
1557865
|
use 5.006; |
|
6
|
|
|
|
|
25
|
|
|
6
|
|
|
|
|
244
|
|
4
|
6
|
|
|
6
|
|
35
|
use strict; |
|
6
|
|
|
|
|
16
|
|
|
6
|
|
|
|
|
224
|
|
5
|
6
|
|
|
6
|
|
32
|
use warnings; |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
212
|
|
6
|
6
|
|
|
6
|
|
33
|
use Carp; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
675
|
|
7
|
|
|
|
|
|
|
|
8
|
6
|
|
|
6
|
|
30
|
use vars qw($VERSION); |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
386
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
$VERSION = '0.08'; |
11
|
|
|
|
|
|
|
|
12
|
6
|
|
|
6
|
|
31
|
use constant DEBUG => 0; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
437
|
|
13
|
|
|
|
|
|
|
|
14
|
6
|
|
|
6
|
|
1561
|
use DateTime; |
|
6
|
|
|
|
|
183454
|
|
|
6
|
|
|
|
|
133
|
|
15
|
6
|
|
|
6
|
|
6531
|
use DateTime::Set; |
|
6
|
|
|
|
|
375723
|
|
|
6
|
|
|
|
|
213
|
|
16
|
6
|
|
|
6
|
|
5781
|
use Set::Crontab; |
|
6
|
|
|
|
|
6236
|
|
|
6
|
|
|
|
|
24943
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
my %Object_Attributes; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
### |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub from_cron { |
23
|
|
|
|
|
|
|
# Return cron as DateTime::Set |
24
|
29
|
|
|
29
|
1
|
34453
|
my $class = shift; |
25
|
29
|
50
|
|
|
|
173
|
my %sparms = @_ == 1 ? (cron => shift) : @_; |
26
|
29
|
|
|
|
|
46
|
my %parms; |
27
|
29
|
|
|
|
|
91
|
$parms{cron} = delete $sparms{cron}; |
28
|
29
|
|
|
|
|
95
|
$parms{user_mode} = delete $sparms{user_mode}; |
29
|
29
|
100
|
|
|
|
439
|
$parms{cron} or croak "Cron string parameter required.\n"; |
30
|
27
|
|
|
|
|
129
|
my $dtc = $class->new(%parms); |
31
|
22
|
|
|
|
|
93
|
$dtc->as_set(%sparms); |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub from_crontab { |
35
|
|
|
|
|
|
|
# Return list of DateTime::Sets based on entries from |
36
|
|
|
|
|
|
|
# a crontab file. |
37
|
1
|
|
|
1
|
1
|
18
|
my $class = shift; |
38
|
1
|
50
|
|
|
|
8
|
my %sparms = @_ == 1 ? (file => shift) : @_; |
39
|
1
|
|
|
|
|
4
|
my $file = delete $sparms{file}; |
40
|
1
|
|
|
|
|
3
|
delete $sparms{cron}; |
41
|
1
|
|
|
|
|
5
|
my $fh = $class->_prepare_fh($file); |
42
|
1
|
|
|
|
|
3
|
my @cronsets; |
43
|
1
|
|
|
|
|
5
|
while (<$fh>) { |
44
|
11
|
|
|
|
|
15
|
chomp; |
45
|
11
|
|
|
|
|
14
|
my $set; |
46
|
11
|
|
|
|
|
16
|
eval { $set = $class->from_cron(%sparms, cron => $_) }; |
|
11
|
|
|
|
|
35
|
|
47
|
11
|
100
|
66
|
|
|
1078
|
push(@cronsets, $set) if ref $set && !$@; |
48
|
|
|
|
|
|
|
} |
49
|
1
|
|
|
|
|
6
|
@cronsets; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub as_set { |
53
|
|
|
|
|
|
|
# Return self as DateTime::Set |
54
|
22
|
|
|
22
|
1
|
36
|
my $self = shift; |
55
|
22
|
|
|
|
|
48
|
my %sparms = @_; |
56
|
22
|
50
|
33
|
|
|
182
|
Carp::cluck "Recurrence callbacks overriden by ". ref $self . "\n" |
|
|
|
33
|
|
|
|
|
57
|
|
|
|
|
|
|
if $sparms{next} || $sparms{recurrence} || $sparms{previous}; |
58
|
22
|
|
|
|
|
37
|
delete $sparms{next}; |
59
|
22
|
|
|
|
|
37
|
delete $sparms{previous}; |
60
|
22
|
|
|
|
|
32
|
delete $sparms{recurrence}; |
61
|
22
|
|
|
83
|
|
127
|
$sparms{next} = sub { $self->next(@_) }; |
|
83
|
|
|
|
|
76307
|
|
62
|
22
|
|
|
65
|
|
88
|
$sparms{previous} = sub { $self->previous(@_) }; |
|
65
|
|
|
|
|
105529
|
|
63
|
22
|
|
|
|
|
196
|
DateTime::Set->from_recurrence(%sparms); |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
### |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub new { |
69
|
48
|
|
|
48
|
0
|
12502
|
my $class = shift; |
70
|
48
|
|
|
|
|
155
|
my $self = {}; |
71
|
48
|
|
|
|
|
255
|
bless $self, $class; |
72
|
48
|
100
|
|
|
|
313
|
my %parms = @_ == 1 ? (cron => shift) : @_; |
73
|
48
|
|
|
|
|
197
|
my $crontab = $self->_make_cronset(%parms); |
74
|
27
|
|
|
|
|
106
|
$self->_cronset($crontab); |
75
|
27
|
|
|
|
|
88
|
$self; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
1
|
|
|
1
|
1
|
1347
|
sub new_from_cron { new(@_) } |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub new_from_crontab { |
81
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
82
|
0
|
0
|
|
|
|
0
|
my %parms = @_ == 1 ? (file => shift()) : @_; |
83
|
0
|
|
|
|
|
0
|
my $fh = $class->_prepare_fh($parms{file}); |
84
|
0
|
|
|
|
|
0
|
delete $parms{file}; |
85
|
0
|
|
|
|
|
0
|
my @dtcrons; |
86
|
0
|
|
|
|
|
0
|
while (<$fh>) { |
87
|
0
|
|
|
|
|
0
|
my $dtc; |
88
|
0
|
|
|
|
|
0
|
eval { $dtc = $class->new(%parms, cron => $_) }; |
|
0
|
|
|
|
|
0
|
|
89
|
0
|
0
|
0
|
|
|
0
|
if (ref $dtc && !$@) { |
90
|
0
|
|
|
|
|
0
|
push(@dtcrons, $dtc); |
91
|
0
|
0
|
|
|
|
0
|
$parms{user_mode} = 1 if defined $dtc->user; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
} |
94
|
0
|
|
|
|
|
0
|
@dtcrons; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
### |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub _prepare_fh { |
100
|
1
|
|
|
1
|
|
2
|
my $class = shift; |
101
|
1
|
|
|
|
|
2
|
my $fh = shift; |
102
|
1
|
50
|
|
|
|
6
|
if (! ref $fh) { |
103
|
0
|
|
|
|
|
0
|
my $file = $fh; |
104
|
0
|
|
|
|
|
0
|
local(*FH); |
105
|
0
|
|
|
|
|
0
|
$fh = do { local *FH; *FH }; # doubled *FH avoids warning |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
106
|
0
|
0
|
|
|
|
0
|
open($fh, "<$file") |
107
|
|
|
|
|
|
|
or croak "Error opening $file for reading\n"; |
108
|
|
|
|
|
|
|
} |
109
|
1
|
|
|
|
|
23
|
$fh; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
### |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub valid { |
115
|
|
|
|
|
|
|
# Is the given date valid according the current cron settings? |
116
|
596
|
|
|
596
|
1
|
69839
|
my($self, $date) = @_; |
117
|
596
|
100
|
66
|
|
|
2038
|
return if !$date || $date->second; |
118
|
584
|
100
|
100
|
|
|
26921
|
$self->minute->contains($date->minute) && |
|
|
|
100
|
|
|
|
|
119
|
|
|
|
|
|
|
$self->hour->contains($date->hour) && |
120
|
|
|
|
|
|
|
$self->days_contain($date->day, $date->dow) && |
121
|
|
|
|
|
|
|
$self->month->contains($date->month); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub match { |
125
|
|
|
|
|
|
|
# Does the given date match the cron spec? |
126
|
0
|
|
|
0
|
1
|
0
|
my($self, $date) = @_; |
127
|
0
|
0
|
|
|
|
0
|
$date = DateTime->now unless $date; |
128
|
0
|
0
|
0
|
|
|
0
|
$self->minute->contains($date->minute) && |
|
|
|
0
|
|
|
|
|
129
|
|
|
|
|
|
|
$self->hour->contains($date->hour) && |
130
|
|
|
|
|
|
|
$self->days_contain($date->day, $date->dow) && |
131
|
|
|
|
|
|
|
$self->month->contains($date->month); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
### Return adjacent dates without altering original date |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub next { |
137
|
96
|
|
|
96
|
1
|
14508
|
my($self, $date) = @_; |
138
|
96
|
100
|
|
|
|
389
|
$date = DateTime->now unless $date; |
139
|
96
|
|
|
|
|
17245
|
$self->increment($date->clone); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub previous { |
143
|
78
|
|
|
78
|
1
|
19683
|
my($self, $date) = @_; |
144
|
78
|
100
|
|
|
|
1069
|
$date = DateTime->now unless $date; |
145
|
78
|
|
|
|
|
4470
|
$self->decrement($date->clone); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
### Change given date to adjacent dates |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub increment { |
151
|
96
|
|
|
96
|
1
|
1267
|
my($self, $date) = @_; |
152
|
96
|
50
|
|
|
|
302
|
$date = DateTime->now unless $date; |
153
|
96
|
100
|
|
|
|
4472
|
return $date if $date->is_infinite; |
154
|
74
|
|
|
|
|
387
|
do { |
155
|
74
|
|
|
|
|
340
|
$self->_attempt_increment($date); |
156
|
|
|
|
|
|
|
} until $self->valid($date); |
157
|
74
|
|
|
|
|
2023
|
$date; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub decrement { |
161
|
78
|
|
|
78
|
1
|
1199
|
my($self, $date) = @_; |
162
|
78
|
50
|
|
|
|
627
|
$date = DateTime->now unless $date; |
163
|
78
|
100
|
|
|
|
4463
|
return $date if $date->is_infinite; |
164
|
56
|
|
|
|
|
304
|
do { |
165
|
56
|
|
|
|
|
196
|
$self->_attempt_decrement($date); |
166
|
|
|
|
|
|
|
} until $self->valid($date); |
167
|
56
|
|
|
|
|
1191
|
$date; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
### |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub _attempt_increment { |
173
|
74
|
|
|
74
|
|
125
|
my($self, $date) = @_; |
174
|
74
|
50
|
|
|
|
236
|
ref $date or croak "Reference to datetime object reqired\n"; |
175
|
74
|
100
|
|
|
|
370
|
$self->valid($date) ? |
176
|
|
|
|
|
|
|
$self->_valid_incr($date) : |
177
|
|
|
|
|
|
|
$self->_invalid_incr($date); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _attempt_decrement { |
181
|
56
|
|
|
56
|
|
108
|
my($self, $date) = @_; |
182
|
56
|
50
|
|
|
|
187
|
ref $date or croak "Reference to datetime object reqired\n"; |
183
|
56
|
100
|
|
|
|
166
|
$self->valid($date) ? |
184
|
|
|
|
|
|
|
$self->_valid_decr($date) : |
185
|
|
|
|
|
|
|
$self->_invalid_decr($date); |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
47
|
|
|
47
|
|
916
|
sub _valid_incr { shift->_minute_incr(@_) } |
189
|
|
|
|
|
|
|
|
190
|
33
|
|
|
33
|
|
1329
|
sub _valid_decr { shift->_minute_decr(@_) } |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _invalid_incr { |
193
|
|
|
|
|
|
|
# If provided date is valid, return it. Otherwise return |
194
|
|
|
|
|
|
|
# nearest valid date after provided date. |
195
|
57
|
|
|
57
|
|
797
|
my($self, $date) = @_; |
196
|
57
|
50
|
|
|
|
180
|
ref $date or croak "Reference to datetime object reqired\n"; |
197
|
|
|
|
|
|
|
|
198
|
57
|
|
|
|
|
147
|
print STDERR "\nI GOT: ", $date->datetime, "\n" if DEBUG; |
199
|
|
|
|
|
|
|
|
200
|
57
|
100
|
|
|
|
186
|
$date->truncate(to => 'minute')->add(minutes => 1) |
201
|
|
|
|
|
|
|
if $date->second; |
202
|
|
|
|
|
|
|
|
203
|
57
|
|
|
|
|
8283
|
print STDERR "RND: ", $date->datetime, "\n" if DEBUG; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Find our greatest invalid unit and clip |
206
|
57
|
100
|
|
|
|
192
|
if (!$self->month->contains($date->month)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
207
|
5
|
|
|
|
|
78
|
$date->truncate(to => 'month'); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
elsif (!$self->days_contain($date->day, $date->dow)) { |
210
|
34
|
|
|
|
|
169
|
$date->truncate(to => 'day'); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
elsif (!$self->hour->contains($date->hour)) { |
213
|
5
|
|
|
|
|
80
|
$date->truncate(to => 'hour'); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
else { |
216
|
13
|
|
|
|
|
198
|
$date->truncate(to => 'minute'); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
57
|
|
|
|
|
22150
|
print STDERR "BBT: ", $date->datetime, "\n" if DEBUG; |
220
|
|
|
|
|
|
|
|
221
|
57
|
100
|
|
|
|
196
|
return $date if $self->valid($date); |
222
|
|
|
|
|
|
|
|
223
|
49
|
|
|
|
|
715
|
print STDERR "ZZT: ", $date->datetime, "\n" if DEBUG; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Extraneous durations clipped. Start searching. |
226
|
49
|
|
|
|
|
152
|
while (!$self->valid($date)) { |
227
|
74
|
|
|
|
|
1190
|
$date->add(months => 1) until $self->month->contains($date->month); |
228
|
74
|
|
|
|
|
965
|
print STDERR "MON: ", $date->datetime, "\n" if DEBUG; |
229
|
|
|
|
|
|
|
|
230
|
74
|
|
|
|
|
260
|
my $day_orig = $date->day; |
231
|
74
|
|
|
|
|
491
|
$date->add(days => 1) until $self->days_contain($date->day, $date->dow); |
232
|
74
|
100
|
50
|
|
|
314
|
$date->truncate(to => 'month') && next if $date->day < $day_orig; |
233
|
63
|
|
|
|
|
604
|
print STDERR "DAY: ", $date->datetime, "\n" if DEBUG; |
234
|
|
|
|
|
|
|
|
235
|
63
|
|
|
|
|
259
|
my $hour_orig = $date->hour; |
236
|
63
|
|
|
|
|
485
|
$date->add(hours => 1) until $self->hour->contains($date->hour); |
237
|
63
|
100
|
50
|
|
|
965
|
$date->truncate(to => 'day') && next if $date->hour < $hour_orig; |
238
|
58
|
|
|
|
|
414
|
print STDERR "HOR: ", $date->datetime, "\n" if DEBUG; |
239
|
|
|
|
|
|
|
|
240
|
58
|
|
|
|
|
244
|
my $min_orig = $date->minute; |
241
|
58
|
|
|
|
|
474
|
$date->add(minutes => 1) until $self->minute->contains($date->minute); |
242
|
58
|
100
|
50
|
|
|
1234
|
$date->truncate(to => 'hour') && next if $date->minute < $min_orig; |
243
|
53
|
|
|
|
|
564
|
print STDERR "MIN: ", $date->datetime, "\n" if DEBUG; |
244
|
|
|
|
|
|
|
} |
245
|
49
|
|
|
|
|
791
|
print STDERR "SET: ", $date->datetime, "\n" if DEBUG; |
246
|
49
|
|
|
|
|
413
|
$date; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub _invalid_decr { |
250
|
|
|
|
|
|
|
# If provided date is valid, return it. Otherwise |
251
|
|
|
|
|
|
|
# return the nearest previous valid date. |
252
|
47
|
|
|
47
|
|
1671
|
my($self, $date) = @_; |
253
|
47
|
50
|
|
|
|
177
|
ref $date or croak "Reference to datetime object reqired\n"; |
254
|
|
|
|
|
|
|
|
255
|
47
|
|
|
|
|
68
|
print STDERR "\nD GOT: ", $date->datetime, "\n" if DEBUG; |
256
|
|
|
|
|
|
|
|
257
|
47
|
100
|
|
|
|
167
|
if (!$self->month->contains($date->month)) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
258
|
3
|
|
|
|
|
47
|
$date->truncate(to => 'month'); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
elsif (!$self->days_contain($date->day, $date->dow)) { |
261
|
30
|
|
|
|
|
204
|
$date->truncate(to => 'day'); |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
elsif (!$self->hour->contains($date->hour)) { |
264
|
3
|
|
|
|
|
56
|
$date->truncate(to => 'hour'); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
else { |
267
|
11
|
|
|
|
|
210
|
$date->truncate(to => 'minute'); |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
47
|
|
|
|
|
40769
|
print STDERR "BBT: ", $date->datetime, "\n" if DEBUG; |
271
|
|
|
|
|
|
|
|
272
|
47
|
100
|
|
|
|
182
|
return $date if $self->valid($date); |
273
|
|
|
|
|
|
|
|
274
|
40
|
|
|
|
|
954
|
print STDERR "ZZT: ", $date->datetime, "\n" if DEBUG; |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Extraneous durations clipped. Start searching. |
277
|
40
|
|
|
|
|
122
|
while (!$self->valid($date)) { |
278
|
69
|
100
|
|
|
|
1148
|
if (!$self->month->contains($date->month)) { |
279
|
17
|
|
|
|
|
252
|
$date->subtract(months => 1) until $self->month->contains($date->month); |
280
|
17
|
|
|
|
|
268
|
$self->_unit_peak($date, 'month'); |
281
|
17
|
|
|
|
|
40694
|
print STDERR "MON: ", $date->datetime, "\n" if DEBUG; |
282
|
|
|
|
|
|
|
} |
283
|
69
|
100
|
|
|
|
851
|
if (!$self->days_contain($date->day, $date->dow)) { |
284
|
53
|
|
|
|
|
185
|
my $day_orig = $date->day; |
285
|
53
|
|
|
|
|
501
|
$date->subtract(days => 1) |
286
|
|
|
|
|
|
|
until $self->days_contain($date->day, $date->dow); |
287
|
53
|
100
|
50
|
|
|
208
|
$self->_unit_peak($date, 'month') && next if ($date->day > $day_orig); |
288
|
29
|
|
|
|
|
292
|
$self->_unit_peak($date, 'day'); |
289
|
29
|
|
|
|
|
57005
|
print STDERR "DAY: ", $date->datetime, "\n" if DEBUG; |
290
|
|
|
|
|
|
|
} |
291
|
45
|
100
|
|
|
|
397
|
if (!$self->hour->contains($date->hour)) { |
292
|
41
|
|
|
|
|
677
|
my $hour_orig = $date->hour; |
293
|
41
|
|
|
|
|
271
|
$date->subtract(hours => 1) until $self->hour->contains($date->hour); |
294
|
41
|
100
|
50
|
|
|
687
|
$self->_unit_peak($date, 'day') && next if ($date->hour > $hour_orig); |
295
|
38
|
|
|
|
|
349
|
$self->_unit_peak($date, 'hour'); |
296
|
38
|
|
|
|
|
93268
|
print STDERR "HOR: ", $date->datetime, "\n" if DEBUG; |
297
|
|
|
|
|
|
|
} |
298
|
42
|
50
|
|
|
|
373
|
if (!$self->minute->contains($date->minute)) { |
299
|
42
|
|
|
|
|
724
|
my $min_orig = $date->minute; |
300
|
42
|
|
|
|
|
260
|
$date->subtract(minutes => 1) |
301
|
|
|
|
|
|
|
until $self->minute->contains($date->minute); |
302
|
42
|
100
|
50
|
|
|
698
|
$self->_unit_peak($date, 'hour') && next if ($date->minute > $min_orig); |
303
|
40
|
|
|
|
|
527
|
print STDERR "MIN: ", $date->datetime, "\n" if DEBUG; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
} |
306
|
40
|
|
|
|
|
1541
|
print STDERR "SET: ", $date->datetime, "\n" if DEBUG; |
307
|
40
|
|
|
|
|
327
|
$date; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
### |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub _unit_peak { |
313
|
113
|
|
|
113
|
|
498
|
my($self, $date, $unit) = @_; |
314
|
113
|
50
|
33
|
|
|
453
|
$date && $unit or croak "DateTime ref and unit required.\n"; |
315
|
113
|
|
|
|
|
5510
|
$date->truncate(to => $unit) |
316
|
|
|
|
|
|
|
->add($unit . 's' => 1) |
317
|
|
|
|
|
|
|
->subtract(minutes => 1); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
### Unit cascades |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _minute_incr { |
323
|
47
|
|
|
47
|
|
92
|
my($self, $date) = @_; |
324
|
47
|
50
|
|
|
|
143
|
croak "datetime object required\n" unless $date; |
325
|
47
|
|
|
|
|
1959
|
my $cur = $date->minute; |
326
|
47
|
|
|
|
|
287
|
my $next = $self->minute->next($cur); |
327
|
47
|
|
|
|
|
297
|
$date->set(minute => $next); |
328
|
47
|
100
|
|
|
|
21165
|
$next <= $cur ? $self->_hour_incr($date) : $date; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub _hour_incr { |
332
|
34
|
|
|
34
|
|
76
|
my($self, $date) = @_; |
333
|
34
|
50
|
|
|
|
115
|
croak "datetime object required\n" unless $date; |
334
|
34
|
|
|
|
|
1370
|
my $cur = $date->hour; |
335
|
34
|
|
|
|
|
244
|
my $next = $self->hour->next($cur); |
336
|
34
|
|
|
|
|
135
|
$date->set(hour => $next); |
337
|
34
|
100
|
|
|
|
13803
|
$next <= $cur ? $self->_day_incr($date) : $date; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub _day_incr { |
341
|
30
|
|
|
30
|
|
79
|
my($self, $date) = @_; |
342
|
30
|
50
|
|
|
|
113
|
croak "datetime object required\n" unless $date; |
343
|
30
|
|
|
|
|
1330
|
$date->add(days => 1); |
344
|
30
|
|
|
|
|
22974
|
$self->_invalid_incr($date); |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub _minute_decr { |
348
|
33
|
|
|
33
|
|
59
|
my($self, $date) = @_; |
349
|
33
|
50
|
|
|
|
268
|
croak "datetime object required\n" unless $date; |
350
|
33
|
|
|
|
|
1533
|
my $cur = $date->minute; |
351
|
33
|
|
|
|
|
207
|
my $next = $self->minute->previous($cur); |
352
|
33
|
|
|
|
|
172
|
$date->set(minute => $next); |
353
|
33
|
100
|
|
|
|
26798
|
$next >= $cur ? $self->_hour_decr($date) : $date; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
sub _hour_decr { |
357
|
30
|
|
|
30
|
|
543
|
my($self, $date) = @_; |
358
|
30
|
50
|
|
|
|
113
|
croak "datetime object required\n" unless $date; |
359
|
30
|
|
|
|
|
1409
|
my $cur = $date->hour; |
360
|
30
|
|
|
|
|
336
|
my $next = $self->hour->previous($cur); |
361
|
30
|
|
|
|
|
117
|
$date->set(hour => $next); |
362
|
30
|
100
|
|
|
|
11106
|
$next >= $cur ? $self->_day_decr($date) : $date; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub _day_decr { |
366
|
24
|
|
|
24
|
|
147
|
my($self, $date) = @_; |
367
|
24
|
50
|
|
|
|
92
|
croak "datetime object required\n" unless $date; |
368
|
24
|
|
|
|
|
1139
|
$date->subtract(days => 1); |
369
|
24
|
|
|
|
|
24230
|
$self->_invalid_decr($date); |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
### Factories |
373
|
|
|
|
|
|
|
|
374
|
48
|
|
|
48
|
|
68
|
sub _make_cronset { shift; DateTime::Event::Cron::IntegratedSet->new(@_) } |
|
48
|
|
|
|
|
207
|
|
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
### Shortcuts |
377
|
|
|
|
|
|
|
|
378
|
2410
|
|
|
2410
|
0
|
1751503
|
sub days_contain { shift->_cronset->days_contain(@_) } |
379
|
|
|
|
|
|
|
|
380
|
3362
|
|
|
3362
|
0
|
2664077
|
sub minute { shift->_cronset->minute } |
381
|
1758
|
|
|
1758
|
0
|
1416503
|
sub hour { shift->_cronset->hour } |
382
|
0
|
|
|
0
|
0
|
0
|
sub day { shift->_cronset->day } |
383
|
858
|
|
|
858
|
0
|
295540
|
sub month { shift->_cronset->month } |
384
|
0
|
|
|
0
|
0
|
0
|
sub dow { shift->_cronset->dow } |
385
|
0
|
|
|
0
|
1
|
0
|
sub user { shift->_cronset->user } |
386
|
0
|
|
|
0
|
1
|
0
|
sub command { shift->_cronset->command } |
387
|
0
|
|
|
0
|
1
|
0
|
sub original { shift->_cronset->original } |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
### Static acessors/mutators |
390
|
|
|
|
|
|
|
|
391
|
8415
|
|
|
8415
|
|
23694
|
sub _cronset { shift->_attr('cronset', @_) } |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub _attr { |
394
|
8415
|
|
|
8415
|
|
12607
|
my $self = shift; |
395
|
8415
|
|
|
|
|
17245
|
my $name = shift; |
396
|
8415
|
100
|
|
|
|
21348
|
if (@_) { |
397
|
27
|
|
|
|
|
136
|
$Object_Attributes{$self}{$name} = shift; |
398
|
|
|
|
|
|
|
} |
399
|
8415
|
|
|
|
|
71417
|
$Object_Attributes{$self}{$name}; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
### debugging |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
sub _dump_sets { |
405
|
0
|
|
|
0
|
|
0
|
my($self, $date) = @_; |
406
|
0
|
|
|
|
|
0
|
foreach (qw(minute hour day month dow)) { |
407
|
0
|
|
|
|
|
0
|
print STDERR "$_: ", join(',',$self->$_->list), "\n"; |
408
|
|
|
|
|
|
|
} |
409
|
0
|
0
|
|
|
|
0
|
if (ref $date) { |
410
|
0
|
|
|
|
|
0
|
$date = $date->clone; |
411
|
0
|
|
|
|
|
0
|
my @mod; |
412
|
0
|
|
|
|
|
0
|
my $mon = $date->month; |
413
|
0
|
|
|
|
|
0
|
$date->truncate(to => 'month'); |
414
|
0
|
|
|
|
|
0
|
while ($date->month == $mon) { |
415
|
0
|
0
|
|
|
|
0
|
push(@mod, $date->day) if $self->days_contain($date->day, $date->dow); |
416
|
0
|
|
|
|
|
0
|
$date->add(days => 1); |
417
|
|
|
|
|
|
|
} |
418
|
0
|
|
|
|
|
0
|
print STDERR "mod for month($mon): ", join(',', @mod), "\n"; |
419
|
|
|
|
|
|
|
} |
420
|
0
|
|
|
|
|
0
|
print STDERR "day_squelch: ", $self->_cronset->day_squelch, " ", |
421
|
|
|
|
|
|
|
"dow_squelch: ", $self->_cronset->dow_squelch, "\n"; |
422
|
0
|
|
|
|
|
0
|
$self; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
### |
426
|
|
|
|
|
|
|
|
427
|
48
|
|
|
48
|
|
15695
|
sub DESTROY { delete $Object_Attributes{shift()} } |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
########## |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
{ |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
package DateTime::Event::Cron::IntegratedSet; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# IntegratedSet manages the collection of field sets for |
436
|
|
|
|
|
|
|
# each cron entry, including sanity checks. Individual |
437
|
|
|
|
|
|
|
# field sets are accessed through their respective names, |
438
|
|
|
|
|
|
|
# i.e., minute hour day month dow. |
439
|
|
|
|
|
|
|
# |
440
|
|
|
|
|
|
|
# Also implements some merged field logic for day/dow |
441
|
|
|
|
|
|
|
# interactions. |
442
|
|
|
|
|
|
|
|
443
|
6
|
|
|
6
|
|
183
|
use strict; |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
243
|
|
444
|
6
|
|
|
6
|
|
32
|
use Carp; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
9934
|
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
my %Range = ( |
447
|
|
|
|
|
|
|
minute => [0..59], |
448
|
|
|
|
|
|
|
hour => [0..23], |
449
|
|
|
|
|
|
|
day => [1..31], |
450
|
|
|
|
|
|
|
month => [1..12], |
451
|
|
|
|
|
|
|
dow => [1..7], |
452
|
|
|
|
|
|
|
); |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
my @Month_Max = qw( 31 29 31 30 31 30 31 31 30 31 30 31 ); |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
my %Object_Attributes; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
sub new { |
459
|
48
|
|
|
48
|
|
82
|
my $self = []; |
460
|
48
|
|
|
|
|
177
|
bless $self, shift; |
461
|
48
|
|
|
|
|
171
|
$self->_range(\%Range); |
462
|
48
|
|
|
|
|
160
|
$self->set_cron(@_); |
463
|
27
|
|
|
|
|
66
|
$self; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
sub set_cron { |
467
|
|
|
|
|
|
|
# Initialize |
468
|
48
|
|
|
48
|
|
82
|
my $self = shift; |
469
|
48
|
|
|
|
|
117
|
my %parms = @_; |
470
|
48
|
|
|
|
|
96
|
my $cron = $parms{cron}; |
471
|
48
|
|
|
|
|
70
|
my $user_mode = $parms{user_mode}; |
472
|
48
|
100
|
|
|
|
262
|
defined $cron or croak "Cron entry fields required\n"; |
473
|
47
|
|
|
|
|
141
|
$self->_attr('original', $cron); |
474
|
47
|
|
|
|
|
64
|
my @line; |
475
|
47
|
100
|
|
|
|
105
|
if (ref $cron) { |
476
|
1
|
|
|
|
|
8
|
@line = grep(!/^\s*$/, @$cron); |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
else { |
479
|
46
|
|
|
|
|
138
|
$cron =~ s/^\s+//; |
480
|
46
|
|
|
|
|
165
|
$cron =~ s/\s+$//; |
481
|
46
|
|
|
|
|
525
|
@line = split(/\s+/, $cron); |
482
|
|
|
|
|
|
|
} |
483
|
47
|
100
|
|
|
|
1483
|
@line >= 5 or croak "At least five cron entry fields required.\n"; |
484
|
38
|
|
|
|
|
405
|
my @entry = splice(@line, 0, 5); |
485
|
38
|
|
|
|
|
53
|
my($user, $command); |
486
|
38
|
50
|
|
|
|
106
|
unless (defined $user_mode) { |
487
|
|
|
|
|
|
|
# auto-detect |
488
|
38
|
100
|
66
|
|
|
227
|
if (@line > 1 && $line[0] =~ /^\w+$/) { |
489
|
4
|
|
|
|
|
5
|
$user_mode = 1; |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
} |
492
|
38
|
100
|
|
|
|
95
|
$user = shift @line if $user_mode; |
493
|
38
|
|
|
|
|
1208
|
$command = join(' ', @line); |
494
|
38
|
|
|
|
|
94
|
$self->_attr('command', $command); |
495
|
38
|
|
|
|
|
87
|
$self->_attr('user', $user); |
496
|
38
|
|
|
|
|
48
|
my $i = 0; |
497
|
38
|
|
|
|
|
125
|
foreach my $name (qw( minute hour day month dow )) { |
498
|
169
|
|
|
|
|
402
|
$self->_attr($name, $self->make_valid_set($name, $entry[$i])); |
499
|
160
|
|
|
|
|
372
|
++$i; |
500
|
|
|
|
|
|
|
} |
501
|
29
|
|
|
|
|
105
|
my @day_list = $self->day->list; |
502
|
29
|
|
|
|
|
323
|
my @dow_list = $self->dow->list; |
503
|
29
|
|
|
|
|
245
|
my $day_range = $self->range('day'); |
504
|
29
|
|
|
|
|
69
|
my $dow_range = $self->range('dow'); |
505
|
29
|
100
|
100
|
|
|
214
|
$self->day_squelch(scalar @day_list == scalar @$day_range && |
506
|
|
|
|
|
|
|
scalar @dow_list != scalar @$dow_range ? 1 : 0); |
507
|
29
|
100
|
100
|
|
|
204
|
$self->dow_squelch(scalar @dow_list == scalar @$dow_range && |
508
|
|
|
|
|
|
|
scalar @day_list != scalar @$day_range ? 1 : 0); |
509
|
29
|
100
|
|
|
|
91
|
unless ($self->day_squelch) { |
510
|
23
|
|
|
|
|
60
|
my @days = $self->day->list; |
511
|
23
|
|
|
|
|
239
|
my $pass = 0; |
512
|
23
|
|
|
|
|
67
|
MONTH: foreach my $month ($self->month->list) { |
513
|
27
|
|
|
|
|
181
|
foreach (@days) { |
514
|
27
|
100
|
50
|
|
|
167
|
++$pass && last MONTH if $_ <= $Month_Max[$month - 1]; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
} |
517
|
23
|
100
|
|
|
|
614
|
croak "Impossible last day for provided months.\n" unless $pass; |
518
|
|
|
|
|
|
|
} |
519
|
27
|
|
|
|
|
177
|
$self; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# Field range queries |
523
|
|
|
|
|
|
|
sub range { |
524
|
227
|
|
|
227
|
|
287
|
my($self, $name) = @_; |
525
|
227
|
50
|
|
|
|
415
|
my $val = $self->_range->{$name} or croak "Unknown field '$name'\n"; |
526
|
227
|
|
|
|
|
402
|
$val; |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# Perform sanity checks when setting up each field set. |
530
|
|
|
|
|
|
|
sub make_valid_set { |
531
|
169
|
|
|
169
|
|
327
|
my($self, $name, $str) = @_; |
532
|
169
|
|
|
|
|
399
|
my $range = $self->range($name); |
533
|
169
|
|
|
|
|
361
|
my $set = $self->make_set($str, $range); |
534
|
169
|
|
|
|
|
504
|
my @list = $set->list; |
535
|
169
|
100
|
|
|
|
1889
|
croak "Malformed cron field '$str'\n" unless @list; |
536
|
167
|
100
|
|
|
|
1248
|
croak "Field value ($list[-1]) out of range ($range->[0]-$range->[-1])\n" |
537
|
|
|
|
|
|
|
if $list[-1] > $range->[-1]; |
538
|
162
|
100
|
100
|
|
|
519
|
if ($name eq 'dow' && $set->contains(0)) { |
539
|
3
|
|
|
|
|
39
|
shift(@list); |
540
|
3
|
50
|
|
|
|
10
|
push(@list, 7) unless $set->contains(7); |
541
|
3
|
|
|
|
|
35
|
$set = $self->make_set(join(',',@list), $range); |
542
|
|
|
|
|
|
|
} |
543
|
162
|
100
|
|
|
|
878
|
croak "Field value ($list[0]) out of range ($range->[0]-$range->[-1])\n" |
544
|
|
|
|
|
|
|
if $list[0] < $range->[0]; |
545
|
160
|
|
|
|
|
587
|
$set; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# No sanity checks |
549
|
172
|
|
|
172
|
|
181
|
sub make_set { shift; DateTime::Event::Cron::OrderedSet->new(@_) } |
|
172
|
|
|
|
|
466
|
|
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# Flags for when day/dow are applied. |
552
|
2468
|
|
|
2468
|
|
9724
|
sub day_squelch { shift->_attr('day_squelch', @_ ) } |
553
|
2239
|
|
|
2239
|
|
4337
|
sub dow_squelch { shift->_attr('dow_squelch', @_ ) } |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# Merged logic for day/dow |
556
|
|
|
|
|
|
|
sub days_contain { |
557
|
2410
|
|
|
2410
|
|
5256
|
my($self, $day, $dow) = @_; |
558
|
2410
|
50
|
33
|
|
|
15496
|
defined $day && defined $dow |
559
|
|
|
|
|
|
|
or croak "Day of month and day of week required.\n"; |
560
|
2410
|
|
|
|
|
16960
|
my $day_c = $self->day->contains($day); |
561
|
2410
|
|
|
|
|
20756
|
my $dow_c = $self->dow->contains($dow); |
562
|
2410
|
100
|
|
|
|
31207
|
return $dow_c if $self->day_squelch; |
563
|
2210
|
100
|
|
|
|
4825
|
return $day_c if $self->dow_squelch; |
564
|
294
|
100
|
|
|
|
5682
|
$day_c || $dow_c; |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
# Set Accessors |
568
|
3362
|
|
|
3362
|
|
12394
|
sub minute { shift->_attr('minute' ) } |
569
|
1758
|
|
|
1758
|
|
10413
|
sub hour { shift->_attr('hour' ) } |
570
|
2462
|
|
|
2462
|
|
5157
|
sub day { shift->_attr('day' ) } |
571
|
881
|
|
|
881
|
|
1862
|
sub month { shift->_attr('month' ) } |
572
|
2439
|
|
|
2439
|
|
5470
|
sub dow { shift->_attr('dow' ) } |
573
|
0
|
|
|
0
|
|
0
|
sub user { shift->_attr('user' ) } |
574
|
0
|
|
|
0
|
|
0
|
sub command { shift->_attr('command') } |
575
|
0
|
|
|
0
|
|
0
|
sub original { shift->_attr('original') } |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# Accessors/mutators |
578
|
275
|
|
|
275
|
|
554
|
sub _range { shift->_attr('range', @_) } |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
sub _attr { |
581
|
16167
|
|
|
16167
|
|
25807
|
my $self = shift; |
582
|
16167
|
|
|
|
|
29777
|
my $name = shift; |
583
|
16167
|
100
|
|
|
|
45250
|
if (@_) { |
584
|
389
|
|
|
|
|
1317
|
$Object_Attributes{$self}{$name} = shift; |
585
|
|
|
|
|
|
|
} |
586
|
16167
|
|
|
|
|
132023
|
$Object_Attributes{$self}{$name}; |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
48
|
|
|
48
|
|
281
|
sub DESTROY { delete $Object_Attributes{shift()} } |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
########## |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
{ |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
package DateTime::Event::Cron::OrderedSet; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# Extends Set::Crontab with some progression logic (next/prev) |
600
|
|
|
|
|
|
|
|
601
|
6
|
|
|
6
|
|
61
|
use strict; |
|
6
|
|
|
|
|
32
|
|
|
6
|
|
|
|
|
206
|
|
602
|
6
|
|
|
6
|
|
27
|
use Carp; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
467
|
|
603
|
6
|
|
|
6
|
|
45
|
use base 'Set::Crontab'; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
3476
|
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
my %Object_Attributes; |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub new { |
608
|
172
|
|
|
172
|
|
209
|
my $class = shift; |
609
|
172
|
|
|
|
|
234
|
my($string, $range) = @_; |
610
|
172
|
50
|
33
|
|
|
1095
|
defined $string && ref $range |
611
|
|
|
|
|
|
|
or croak "Cron field and range ref required.\n"; |
612
|
172
|
|
|
|
|
637
|
my $self = Set::Crontab->new($string, $range); |
613
|
172
|
|
|
|
|
21870
|
bless $self, $class; |
614
|
172
|
|
|
|
|
510
|
my @list = $self->list; |
615
|
172
|
|
|
|
|
1866
|
my(%next, %prev); |
616
|
172
|
|
|
|
|
398
|
foreach (0 .. $#list) { |
617
|
2038
|
|
|
|
|
4555
|
$next{$list[$_]} = $list[($_+1)%@list]; |
618
|
2038
|
|
|
|
|
4941
|
$prev{$list[$_]} = $list[($_-1)%@list]; |
619
|
|
|
|
|
|
|
} |
620
|
172
|
|
|
|
|
491
|
$self->_attr('next', \%next); |
621
|
172
|
|
|
|
|
384
|
$self->_attr('previous', \%prev); |
622
|
172
|
|
|
|
|
825
|
$self; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
sub next { |
626
|
81
|
|
|
81
|
|
215
|
my($self, $entry) = @_; |
627
|
81
|
|
|
|
|
213
|
my $hash = $self->_attr('next'); |
628
|
81
|
50
|
|
|
|
299
|
croak "Missing entry($entry) in set\n" unless exists $hash->{$entry}; |
629
|
81
|
|
|
|
|
167
|
my $next = $hash->{$entry}; |
630
|
81
|
50
|
|
|
|
413
|
wantarray ? ($next, $next <= $entry) : $next; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub previous { |
634
|
63
|
|
|
63
|
|
106
|
my($self, $entry) = @_; |
635
|
63
|
|
|
|
|
166
|
my $hash = $self->_attr('previous'); |
636
|
63
|
50
|
|
|
|
211
|
croak "Missing entry($entry) in set\n" unless exists $hash->{$entry}; |
637
|
63
|
|
|
|
|
141
|
my $prev = $hash->{$entry}; |
638
|
63
|
50
|
|
|
|
220
|
wantarray ? ($prev, $prev >= $entry) : $prev; |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
sub _attr { |
642
|
488
|
|
|
488
|
|
683
|
my $self = shift; |
643
|
488
|
|
|
|
|
564
|
my $name = shift; |
644
|
488
|
100
|
|
|
|
988
|
if (@_) { |
645
|
344
|
|
|
|
|
2594
|
$Object_Attributes{$self}{$name} = shift; |
646
|
|
|
|
|
|
|
} |
647
|
488
|
|
|
|
|
1557
|
$Object_Attributes{$self}{$name}; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
172
|
|
|
172
|
|
3143
|
sub DESTROY { delete $Object_Attributes{shift()} } |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
### |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
1; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
__END__ |