fugit 1.11.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/CREDITS.md +5 -1
- data/LICENSE.txt +1 -1
- data/README.md +196 -22
- data/fugit.gemspec +2 -1
- data/lib/fugit/at.rb +9 -4
- data/lib/fugit/cron.rb +100 -57
- data/lib/fugit/nat.rb +9 -5
- data/lib/fugit/parse.rb +13 -11
- data/lib/fugit.rb +1 -1
- metadata +18 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15c7da961f6616177b4837e7db2b8d2cbef8a73af0e39d482bbde88ca927d420
|
|
4
|
+
data.tar.gz: bdb787682ba39f3bd873c935378d7097291ff275c9c2443a4bf42990e71dfa92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c920de689a07b8e5b082afa2d67ff26a2ad3422e0a35afc0c02fee71d92d6a76da391d7ec6d03e55d62cbc1a46cbf1296bccb4a37ec9df9d72f35d3e4ecaf0ad
|
|
7
|
+
data.tar.gz: 11694200215f7506f64f862d7f63e86b3fc79be5ad4a4ddfe9278143c40991bcd2c89356e9e314d5caad125b0b26623daf3da64ee2e92b49ca1480b59fc220f3
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
# CHANGELOG.md
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## fugit 1.13.0 released 2026-07-10
|
|
6
|
+
|
|
7
|
+
* Random values support in cron string `"10 ~ * * *"` gh-121 Nicols Bachschmidt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## fugit 1.12.3 released 2026-07-01
|
|
11
|
+
|
|
12
|
+
* Wrap-around for weekday ranges with step value, gh-119 Nicolas Bachschmidt
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## fugit 1.12.2 released 2026-05-28
|
|
16
|
+
|
|
17
|
+
* Fix "divide by zero" gh-117
|
|
18
|
+
* Let at parse 'now', gh-111
|
|
19
|
+
* Let Fugit::At pass options to Chronic, gh-116
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## fugit 1.12.1 released 2025-10-14
|
|
23
|
+
|
|
24
|
+
* Fix Fugit::Cron#to_cron_s to include & if @day_and
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## fugit 1.12.0 released 2025-09-30
|
|
28
|
+
|
|
29
|
+
* Upgrade et-orbi to ~> 1.4.0 for EtOrbi.rweek_ref=, gh-114
|
|
30
|
+
this changes the rweek reference point to 2018-12-31 (Monday)
|
|
31
|
+
* Fix `12 0 * * wed%4+1,wed%4` issue where 1 wed was ignored, gh-114
|
|
32
|
+
|
|
33
|
+
|
|
5
34
|
## fugit 1.11.2 released 2025-08-22
|
|
6
35
|
|
|
7
36
|
* Fix "every day at midnight America/Los_Angeles", gh-113, Mark R. James
|
data/CREDITS.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
# fugit credits
|
|
3
3
|
|
|
4
|
+
* Nicolas Bachschmidt https://github.com/baarde gh-120 weekday wrap fix
|
|
5
|
+
* JebeenLee https://github.com/JebeenLee gh-117 divide by zero
|
|
6
|
+
* Eric Claerhout https://github.com/swebra gh-114 rweek readme clarity
|
|
7
|
+
* Hugh Kelsey https://github.com/hughkelsey gh-114 rweek/rday / wed%4+1,wed%4
|
|
4
8
|
* Mark R. James, https://github.com/mrj, AM vs America/Los_Angeles, gh-113
|
|
5
9
|
* Tejas Bubane, https://github.com/tejasbubane, r3.4 in test matrix, gh-109
|
|
6
10
|
* Luis Castillo, https://github.com/lekastillo, nice_hash gh-108
|
|
7
|
-
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107
|
|
11
|
+
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107 gh-122
|
|
8
12
|
* https://github.com/personnumber3377, gh-104 Fugit.parse choke on long input
|
|
9
13
|
* Michael Scrivo, https://github.com/mscrivo, gh-103
|
|
10
14
|
* Benjamin Darcet, https://github.com/bdarcet gh-95 gh-96 et-orbi #rweek
|
data/LICENSE.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
Copyright (c) 2017-
|
|
2
|
+
Copyright (c) 2017-2026, John Mettraux, jmettraux+flor@gmail.com
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
# fugit
|
|
3
3
|
|
|
4
|
+
> Sed fugit interea, fugit irreparabile tempus
|
|
5
|
+
|
|
6
|
+
Virgil, _Georgics_ Book III
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
4
10
|
[](https://github.com/floraison/fugit/actions)
|
|
5
11
|
[](http://badge.fury.io/rb/fugit)
|
|
6
12
|
|
|
@@ -296,43 +302,211 @@ The hash extension can only be used in the day-of-week field.
|
|
|
296
302
|
|
|
297
303
|
### the modulo extension
|
|
298
304
|
|
|
299
|
-
|
|
305
|
+
Since 1.1.10, fugit also understands cron strings like `9 0 * * sun%2` which can be read as "every other Sunday at 9am" or `12 0 * * mon%4` for "every fourth monday at noon".
|
|
300
306
|
|
|
301
307
|
The modulo extension can only be used in the day-of-week field.
|
|
302
308
|
|
|
303
309
|
For odd Sundays, one can write `9 0 * * sun%2+1`.
|
|
304
310
|
|
|
305
|
-
It can be combined, as in `9 0 * * sun%2,tue%3+2
|
|
311
|
+
It can be combined, as in `9 0 * * sun%2,tue%3+2`, which will match every other Sunday and 1 in 3 Tuesdays (with an offset of 2).
|
|
312
|
+
|
|
313
|
+
What does `sun%2` actually mean?
|
|
314
|
+
```ruby
|
|
315
|
+
t.wday == 0 && t.rweek % 2 == 0
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
What does `tue%3+2` mean?
|
|
319
|
+
```ruby
|
|
320
|
+
t.wday == 2 && t.rweek % 3 == 2
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### et-orbi < 1.4.0 : reference set on Tuesday 2019-01-01
|
|
306
324
|
|
|
307
|
-
|
|
325
|
+
The original implementation of `#rweek` (and `#rday`) found in [et-orbi](https://github.com/floraison/et-orbi) was initially pointing to "Tuesday 2019-01-01" and it was set as rday 1 and rweek 1.
|
|
308
326
|
|
|
327
|
+
Consider this iteration through the days around 2019-01-01.
|
|
309
328
|
```ruby
|
|
310
|
-
require '
|
|
329
|
+
require 'fugit'
|
|
330
|
+
|
|
331
|
+
t = EtOrbi.parse('2018-12-28 12:00')
|
|
311
332
|
|
|
312
|
-
|
|
313
|
-
p EtOrbi.parse('2019-01-01').wday # => 2
|
|
314
|
-
p EtOrbi.parse('2019-01-01').rweek # => 1
|
|
315
|
-
p EtOrbi.parse('2019-01-01').rweek % 2 # => 1
|
|
333
|
+
15.times do |i|
|
|
316
334
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
p EtOrbi.parse('2019-04-11').rweek # => 15
|
|
320
|
-
p EtOrbi.parse('2019-04-11').rweek % 2 # => 1
|
|
335
|
+
puts " * %14s / rday: %5d / rweek: %5d" % [
|
|
336
|
+
t.strftime('%F %a'), t.rday, t.rweek ]
|
|
321
337
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
338
|
+
w = t.rweek
|
|
339
|
+
t = t.add(24 * 3600)
|
|
340
|
+
puts if t.rweek != w
|
|
341
|
+
|
|
342
|
+
if i == 7
|
|
343
|
+
puts "\n (...)\n\n"
|
|
344
|
+
t = EtOrbi.parse('2025-10-04 12:00')
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
For et-orbi 1.2.11, it yields:
|
|
350
|
+
```
|
|
351
|
+
* 2018-12-28 Fri / rday: -3 / rweek: 0
|
|
352
|
+
* 2018-12-29 Sat / rday: -2 / rweek: 0
|
|
353
|
+
* 2018-12-30 Sun / rday: -1 / rweek: 0
|
|
354
|
+
* 2018-12-31 Mon / rday: 0 / rweek: 0
|
|
355
|
+
|
|
356
|
+
* 2019-01-01 Tue / rday: 1 / rweek: 1
|
|
357
|
+
* 2019-01-02 Wed / rday: 2 / rweek: 1
|
|
358
|
+
* 2019-01-03 Thu / rday: 3 / rweek: 1
|
|
359
|
+
* 2019-01-04 Fri / rday: 4 / rweek: 1
|
|
360
|
+
* 2019-01-05 Sat / rday: 5 / rweek: 1
|
|
361
|
+
|
|
362
|
+
(...)
|
|
363
|
+
|
|
364
|
+
* 2025-10-04 Sat / rday: 2469 / rweek: 353
|
|
365
|
+
* 2025-10-05 Sun / rday: 2470 / rweek: 353
|
|
366
|
+
* 2025-10-06 Mon / rday: 2471 / rweek: 353
|
|
367
|
+
|
|
368
|
+
* 2025-10-07 Tue / rday: 2472 / rweek: 354
|
|
369
|
+
* 2025-10-08 Wed / rday: 2473 / rweek: 354
|
|
370
|
+
* 2025-10-09 Thu / rday: 2474 / rweek: 354
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
This [was problematic](https://github.com/floraison/fugit/issues/114), since the week started on, well, Tuesday.
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
#### et-orbi >= 1.4.0 : reference set on Monday 2018-12-31
|
|
377
|
+
|
|
378
|
+
Since 1.4.0, et-orbi starts by default on Monday (2018-12-31), as rday 0 with rweek 0.
|
|
379
|
+
|
|
380
|
+
Thus, the above code yields:
|
|
381
|
+
```
|
|
382
|
+
* 2018-12-28 Fri / rday: -3 / rweek: -1
|
|
383
|
+
* 2018-12-29 Sat / rday: -2 / rweek: -1
|
|
384
|
+
* 2018-12-30 Sun / rday: -1 / rweek: -1
|
|
385
|
+
|
|
386
|
+
* 2018-12-31 Mon / rday: 0 / rweek: 0
|
|
387
|
+
* 2019-01-01 Tue / rday: 1 / rweek: 0
|
|
388
|
+
* 2019-01-02 Wed / rday: 2 / rweek: 0
|
|
389
|
+
* 2019-01-03 Thu / rday: 3 / rweek: 0
|
|
390
|
+
* 2019-01-04 Fri / rday: 4 / rweek: 0
|
|
391
|
+
* 2019-01-05 Sat / rday: 5 / rweek: 0
|
|
392
|
+
|
|
393
|
+
(...)
|
|
394
|
+
|
|
395
|
+
* 2025-10-04 Sat / rday: 2469 / rweek: 352
|
|
396
|
+
* 2025-10-05 Sun / rday: 2470 / rweek: 352
|
|
397
|
+
|
|
398
|
+
* 2025-10-06 Mon / rday: 2471 / rweek: 353
|
|
399
|
+
* 2025-10-07 Tue / rday: 2472 / rweek: 353
|
|
400
|
+
* 2025-10-08 Wed / rday: 2473 / rweek: 353
|
|
401
|
+
* 2025-10-09 Thu / rday: 2474 / rweek: 353
|
|
402
|
+
```
|
|
325
403
|
|
|
326
|
-
c = Fugit.parse('* * * * tue%2+1')
|
|
327
|
-
c.match?('2019-01-01') # => true, since (rweek + 1) % 2 == 0
|
|
328
|
-
c.match?('2019-01-08') # => false, since (rweek + 1) % 2 == 1
|
|
329
404
|
|
|
330
|
-
|
|
405
|
+
#### modulo and et-orbi >= 1.4.0 sanity check
|
|
406
|
+
|
|
407
|
+
Given the cron `"0 12 * * mon%2,wed%3+1"`, here is a piece of code that considers a range of 44 days and tells in its last column if YES or no if each of the days matches the cron.
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
require 'fugit'
|
|
411
|
+
|
|
412
|
+
c = Fugit.parse_cron('0 12 * * mon%2,wed%3+1')
|
|
413
|
+
|
|
414
|
+
t = EtOrbi.parse('2025-09-20 12:00')
|
|
415
|
+
|
|
416
|
+
44.times do
|
|
417
|
+
|
|
418
|
+
wd = t.strftime('%a')
|
|
419
|
+
wd = %w[ Mon Wed ].include?(wd) ? '*' + wd.upcase : ' ' + wd.downcase
|
|
420
|
+
|
|
421
|
+
puts "%14s | rweek: %3d | %%2: %d == 0 | %%3: %d == 1 | ? %3s" % [
|
|
422
|
+
t.strftime('%F') + ' ' + wd,
|
|
423
|
+
t.rweek,
|
|
424
|
+
t.rweek % 2, t.rweek % 3,
|
|
425
|
+
c.match?(t)
|
|
426
|
+
].map { |e| e == true ? 'YES' : e == false ? 'no' : e }
|
|
427
|
+
|
|
428
|
+
w = t.rweek
|
|
429
|
+
t = t.add(24 * 3600)
|
|
430
|
+
puts if t.rweek != w
|
|
431
|
+
end
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Here's the output:
|
|
435
|
+
```
|
|
436
|
+
2025-09-20 sat | rweek: 350 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
437
|
+
2025-09-21 sun | rweek: 350 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
438
|
+
|
|
439
|
+
2025-09-22 *MON | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
440
|
+
2025-09-23 tue | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
441
|
+
2025-09-24 *WED | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
442
|
+
2025-09-25 thu | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
443
|
+
2025-09-26 fri | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
444
|
+
2025-09-27 sat | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
445
|
+
2025-09-28 sun | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
|
|
446
|
+
|
|
447
|
+
2025-09-29 *MON | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? YES
|
|
448
|
+
2025-09-30 tue | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
|
|
449
|
+
2025-10-01 *WED | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? YES
|
|
450
|
+
2025-10-02 thu | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
|
|
451
|
+
2025-10-03 fri | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
|
|
452
|
+
2025-10-04 sat | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
|
|
453
|
+
2025-10-05 sun | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
|
|
454
|
+
|
|
455
|
+
2025-10-06 *MON | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
456
|
+
2025-10-07 tue | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
457
|
+
2025-10-08 *WED | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
458
|
+
2025-10-09 thu | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
459
|
+
2025-10-10 fri | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
460
|
+
2025-10-11 sat | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
461
|
+
2025-10-12 sun | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
|
|
462
|
+
|
|
463
|
+
2025-10-13 *MON | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? YES
|
|
464
|
+
2025-10-14 tue | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
465
|
+
2025-10-15 *WED | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
466
|
+
2025-10-16 thu | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
467
|
+
2025-10-17 fri | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
468
|
+
2025-10-18 sat | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
469
|
+
2025-10-19 sun | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
|
|
470
|
+
|
|
471
|
+
2025-10-20 *MON | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
472
|
+
2025-10-21 tue | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
473
|
+
2025-10-22 *WED | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? YES
|
|
474
|
+
2025-10-23 thu | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
475
|
+
2025-10-24 fri | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
476
|
+
2025-10-25 sat | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
477
|
+
2025-10-26 sun | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
|
|
478
|
+
|
|
479
|
+
2025-10-27 *MON | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? YES
|
|
480
|
+
2025-10-28 tue | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
481
|
+
2025-10-29 *WED | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
482
|
+
2025-10-30 thu | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
483
|
+
2025-10-31 fri | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
484
|
+
2025-11-01 sat | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
485
|
+
2025-11-02 sun | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
### the random extension
|
|
490
|
+
|
|
491
|
+
Fugit accepts the `~` character to specify a range from which a random value is picked. Either or both the start and the end of the range may be omitted.
|
|
492
|
+
|
|
493
|
+
```ruby
|
|
494
|
+
'~ * * * *' # every hour on a random minute
|
|
495
|
+
'~29 * * * *' # every hour on a random minute between 0 and 29
|
|
496
|
+
'30~ * * * *' # every hour on a random minute between 30 and 59
|
|
497
|
+
'~/10 * * * *' # every 10 minutes starting on a random minute between 0 and 9
|
|
498
|
+
'0 12 * * 1~5' # every week at noon on a random workday
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
You may customize how random values are generated by adding the `:random` option with a boolean or an object that implements the `#rand` method.
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
Fugit.parse('~ * * * *', random: false) # on a non-random minute ==> '0 * * * *'
|
|
505
|
+
Fugit.parse('~ * * * *', random: true) # on a random minute (default)
|
|
506
|
+
Fugit.parse('~ * * * *', random: SecureRandom) # on a cryptographically secure random minute
|
|
331
507
|
```
|
|
332
508
|
|
|
333
|
-
`
|
|
334
|
-
`tue%3+2` matches if Tuesday and `current_date.rweek + 2 % 3 == 0`
|
|
335
|
-
`tue%x+y` matches if Tuesday and `current_date.rweek + y % x == 0`
|
|
509
|
+
Please note that the random values are determined at parse time. So `"~29 * * * *"` at parse time could become `"17 * * * *"` for example and go on triggering every hour at minute 17, until a new parsing occurs and another (or the same) minute is picked. This aligns with the OpenBSD [`man 5 crontab`](https://man.openbsd.org/crontab.5), OpenBSD `crond` rolling the dice at load time.
|
|
336
510
|
|
|
337
511
|
|
|
338
512
|
### the second extension
|
data/fugit.gemspec
CHANGED
|
@@ -41,10 +41,11 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
|
|
|
41
41
|
# this dependency appears in 'et-orbi'
|
|
42
42
|
|
|
43
43
|
s.add_runtime_dependency 'raabro', '~> 1.4'
|
|
44
|
-
s.add_runtime_dependency 'et-orbi', '~> 1
|
|
44
|
+
s.add_runtime_dependency 'et-orbi', '~> 1.4'
|
|
45
45
|
|
|
46
46
|
s.add_development_dependency 'rspec', '~> 3.8'
|
|
47
47
|
s.add_development_dependency 'chronic', '~> 0.10'
|
|
48
|
+
s.add_development_dependency 'ostruct'
|
|
48
49
|
|
|
49
50
|
s.require_path = 'lib'
|
|
50
51
|
end
|
data/lib/fugit/at.rb
CHANGED
|
@@ -6,14 +6,19 @@ module Fugit
|
|
|
6
6
|
|
|
7
7
|
class << self
|
|
8
8
|
|
|
9
|
-
def parse(s)
|
|
9
|
+
def parse(s, opts={})
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
do_parse(s, opts || {}) rescue nil
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def do_parse(s)
|
|
14
|
+
def do_parse(s, opts={})
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
case s
|
|
17
|
+
when /^now$/i then ::EtOrbi::EoTime.now
|
|
18
|
+
when String then ::EtOrbi.parse(s, opts || {})
|
|
19
|
+
when ::EtOrbi::EoTime then s
|
|
20
|
+
else ::EtOrbi.make_time(s)
|
|
21
|
+
end
|
|
17
22
|
end
|
|
18
23
|
end
|
|
19
24
|
end
|
data/lib/fugit/cron.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Fugit
|
|
|
29
29
|
parse(original)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def parse(s)
|
|
32
|
+
def parse(s, opts={})
|
|
33
33
|
|
|
34
34
|
return s if s.is_a?(self)
|
|
35
35
|
return nil unless s.is_a?(String)
|
|
@@ -48,12 +48,12 @@ module Fugit
|
|
|
48
48
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colors: true)
|
|
49
49
|
h = Parser.parse(s)
|
|
50
50
|
|
|
51
|
-
self.allocate.send(:init, s0, h)
|
|
51
|
+
self.allocate.send(:init, s0, h, opts)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def do_parse(s)
|
|
54
|
+
def do_parse(s, opts={})
|
|
55
55
|
|
|
56
|
-
parse(s) ||
|
|
56
|
+
parse(s, opts) ||
|
|
57
57
|
fail(ArgumentError.new("invalid cron string #{trunc(s)}"))
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -175,34 +175,30 @@ module Fugit
|
|
|
175
175
|
|
|
176
176
|
def weekday_hash_match?(nt, hsh)
|
|
177
177
|
|
|
178
|
+
return false unless hsh.is_a?(Integer)
|
|
179
|
+
|
|
178
180
|
phsh, nhsh = nt.wday_in_month
|
|
181
|
+
#
|
|
182
|
+
# positive wday, from the beginning of the month
|
|
183
|
+
# negative wday, from the end of the month, -1 == last
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
hsh == phsh # positive wday, from the beginning of the month
|
|
182
|
-
else
|
|
183
|
-
hsh == nhsh # negative wday, from the end of the month, -1 == last
|
|
184
|
-
end
|
|
185
|
+
(hsh == phsh) || (hsh == nhsh)
|
|
185
186
|
end
|
|
186
187
|
|
|
187
188
|
def weekday_modulo_match?(nt, mod)
|
|
188
189
|
|
|
189
|
-
(
|
|
190
|
+
mod.is_a?(Array) &&
|
|
191
|
+
((nt.rweek % mod[0]) == (mod[1] % mod[0]))
|
|
190
192
|
end
|
|
191
193
|
|
|
192
194
|
def weekday_match?(nt)
|
|
193
195
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if hom.is_a?(Array)
|
|
202
|
-
weekday_modulo_match?(nt, hom)
|
|
203
|
-
else
|
|
204
|
-
weekday_hash_match?(nt, hom)
|
|
205
|
-
end
|
|
196
|
+
@weekdays.nil? ||
|
|
197
|
+
@weekdays.find { |wd, hom|
|
|
198
|
+
(nt.wday == wd) &&
|
|
199
|
+
(hom.nil? ||
|
|
200
|
+
weekday_modulo_match?(nt, hom) ||
|
|
201
|
+
weekday_hash_match?(nt, hom)) }
|
|
206
202
|
end
|
|
207
203
|
|
|
208
204
|
def monthday_match?(nt)
|
|
@@ -563,7 +559,7 @@ module Fugit
|
|
|
563
559
|
|
|
564
560
|
FREQUENCY_CACHE = {}
|
|
565
561
|
|
|
566
|
-
def init(original, h)
|
|
562
|
+
def init(original, h, opts)
|
|
567
563
|
|
|
568
564
|
return nil unless h
|
|
569
565
|
|
|
@@ -572,12 +568,12 @@ module Fugit
|
|
|
572
568
|
@day_and = h[:&]
|
|
573
569
|
|
|
574
570
|
valid =
|
|
575
|
-
determine_seconds(h[:sec]) &&
|
|
576
|
-
determine_minutes(h[:min]) &&
|
|
577
|
-
determine_hours(h[:hou]) &&
|
|
578
|
-
determine_monthdays(h[:dom]) &&
|
|
579
|
-
determine_months(h[:mon]) &&
|
|
580
|
-
determine_weekdays(h[:dow]) &&
|
|
571
|
+
determine_seconds(h[:sec], opts) &&
|
|
572
|
+
determine_minutes(h[:min], opts) &&
|
|
573
|
+
determine_hours(h[:hou], opts) &&
|
|
574
|
+
determine_monthdays(h[:dom], opts) &&
|
|
575
|
+
determine_months(h[:mon], opts) &&
|
|
576
|
+
determine_weekdays(h[:dow], opts) &&
|
|
581
577
|
determine_timezone(h[:tz])
|
|
582
578
|
|
|
583
579
|
return nil unless valid
|
|
@@ -586,15 +582,22 @@ module Fugit
|
|
|
586
582
|
self
|
|
587
583
|
end
|
|
588
584
|
|
|
589
|
-
def expand(min, max, r)
|
|
585
|
+
def expand(min, max, opts, r)
|
|
590
586
|
|
|
591
|
-
sta, edn, sla = r
|
|
587
|
+
sta, edn, sep, sla = r
|
|
592
588
|
|
|
593
589
|
#return false if sla && sla > max
|
|
594
590
|
#
|
|
595
591
|
# let it go, "* */24 * * *" and "* */27 * * *" are okay
|
|
596
592
|
# gh-86 and gh-103
|
|
597
593
|
|
|
594
|
+
return false if sla && sla < 1
|
|
595
|
+
|
|
596
|
+
if sep == :tilde
|
|
597
|
+
sta = random(min, max, sta, edn, sla, opts)
|
|
598
|
+
edn = nil unless sla
|
|
599
|
+
end
|
|
600
|
+
|
|
598
601
|
edn = max if sla && edn.nil?
|
|
599
602
|
|
|
600
603
|
return nil if sta.nil? && edn.nil? && sla.nil?
|
|
@@ -607,6 +610,24 @@ module Fugit
|
|
|
607
610
|
range(min, max, sta, edn, sla)
|
|
608
611
|
end
|
|
609
612
|
|
|
613
|
+
def random(min, max, sta, edn, sla, opts)
|
|
614
|
+
|
|
615
|
+
sta = min if sta.nil?
|
|
616
|
+
edn = max if edn.nil?
|
|
617
|
+
|
|
618
|
+
random = opts.fetch(:random, true)
|
|
619
|
+
random = Random if random == true
|
|
620
|
+
return sta unless random
|
|
621
|
+
|
|
622
|
+
n = edn - sta + 1
|
|
623
|
+
n = max - min + 1 + n if n <= 0
|
|
624
|
+
n = sla if sla && sla < n
|
|
625
|
+
|
|
626
|
+
r = sta + random.rand(n)
|
|
627
|
+
r = r + min - max - 1 if r > max
|
|
628
|
+
r
|
|
629
|
+
end
|
|
630
|
+
|
|
610
631
|
def range(min, max, sta, edn, sla)
|
|
611
632
|
|
|
612
633
|
return [ nil ] if sta == min && edn == max && sla == 1
|
|
@@ -649,13 +670,13 @@ module Fugit
|
|
|
649
670
|
.uniq
|
|
650
671
|
end
|
|
651
672
|
|
|
652
|
-
def do_determine(key, arr, min, max)
|
|
673
|
+
def do_determine(key, arr, min, max, opts)
|
|
653
674
|
|
|
654
675
|
null = false
|
|
655
676
|
|
|
656
677
|
r = arr
|
|
657
678
|
.collect { |v|
|
|
658
|
-
expand(min, max, v) }
|
|
679
|
+
expand(min, max, opts, v) }
|
|
659
680
|
.flatten(1)
|
|
660
681
|
.collect { |e|
|
|
661
682
|
return false if e == false
|
|
@@ -666,36 +687,44 @@ module Fugit
|
|
|
666
687
|
r.uniq.sort
|
|
667
688
|
end
|
|
668
689
|
|
|
669
|
-
def determine_seconds(arr)
|
|
670
|
-
(@seconds = do_determine(:seconds, arr || [ 0 ], 0, 59)) != false
|
|
690
|
+
def determine_seconds(arr, opts)
|
|
691
|
+
(@seconds = do_determine(:seconds, arr || [ 0 ], 0, 59, opts)) != false
|
|
671
692
|
end
|
|
672
693
|
|
|
673
|
-
def determine_minutes(arr)
|
|
674
|
-
(@minutes = do_determine(:minutes, arr, 0, 59)) != false
|
|
694
|
+
def determine_minutes(arr, opts)
|
|
695
|
+
(@minutes = do_determine(:minutes, arr, 0, 59, opts)) != false
|
|
675
696
|
end
|
|
676
697
|
|
|
677
|
-
def determine_hours(arr)
|
|
678
|
-
(@hours = do_determine(:hours, arr, 0, 23)) != false
|
|
698
|
+
def determine_hours(arr, opts)
|
|
699
|
+
(@hours = do_determine(:hours, arr, 0, 23, opts)) != false
|
|
679
700
|
end
|
|
680
701
|
|
|
681
|
-
def determine_monthdays(arr)
|
|
682
|
-
(@monthdays = do_determine(:monthdays, arr, 1, 31)) != false
|
|
702
|
+
def determine_monthdays(arr, opts)
|
|
703
|
+
(@monthdays = do_determine(:monthdays, arr, 1, 31, opts)) != false
|
|
683
704
|
end
|
|
684
705
|
|
|
685
|
-
def determine_months(arr)
|
|
686
|
-
(@months = do_determine(:months, arr, 1, 12)) != false
|
|
706
|
+
def determine_months(arr, opts)
|
|
707
|
+
(@months = do_determine(:months, arr, 1, 12, opts)) != false
|
|
687
708
|
end
|
|
688
709
|
|
|
689
|
-
def determine_weekdays(arr)
|
|
710
|
+
def determine_weekdays(arr, opts)
|
|
690
711
|
|
|
691
712
|
@weekdays = []
|
|
692
713
|
|
|
693
|
-
arr.each do |a, z, sl, ha, mo| # a to z, slash, hash, and mod
|
|
714
|
+
arr.each do |a, z, sp, sl, ha, mo| # a to z, seperator, slash, hash, and mod
|
|
715
|
+
if sp == :tilde
|
|
716
|
+
a = random(0, 6, a, z, sl, opts)
|
|
717
|
+
z = nil unless sl
|
|
718
|
+
end
|
|
719
|
+
|
|
694
720
|
if ha || mo
|
|
695
721
|
@weekdays << [ a, ha || mo ]
|
|
696
722
|
elsif sl
|
|
697
|
-
|
|
698
|
-
|
|
723
|
+
z ||= (a ? a : 6)
|
|
724
|
+
a ||= 0
|
|
725
|
+
z = z + 7 if a > z
|
|
726
|
+
(a..z).step(sl < 1 ? 1 : sl)
|
|
727
|
+
.each { |i| @weekdays << [ (i > 6) ? i - 7 : i ] }
|
|
699
728
|
elsif z
|
|
700
729
|
z = z + 7 if a > z
|
|
701
730
|
(a..z).each { |i| @weekdays << [ (i > 6) ? i - 7 : i ] }
|
|
@@ -735,7 +764,8 @@ module Fugit
|
|
|
735
764
|
else
|
|
736
765
|
a.collect(&:to_s).join('#')
|
|
737
766
|
end }
|
|
738
|
-
.join(',')
|
|
767
|
+
.join(',') +
|
|
768
|
+
(@day_and ? '&' : '')
|
|
739
769
|
end
|
|
740
770
|
|
|
741
771
|
module Parser include Raabro
|
|
@@ -757,7 +787,8 @@ module Fugit
|
|
|
757
787
|
|
|
758
788
|
def s(i); rex(nil, i, /[ \t]+/); end
|
|
759
789
|
def star(i); str(nil, i, '*'); end
|
|
760
|
-
def hyphen(i); str(
|
|
790
|
+
def hyphen(i); str(:hyphen, i, '-'); end
|
|
791
|
+
def tilde(i); str(:tilde, i, '~'); end
|
|
761
792
|
def comma(i); rex(nil, i, /,([ \t]*,)*/); end
|
|
762
793
|
def comma?(i); rex(nil, i, /([ \t]*,)*/); end
|
|
763
794
|
def and?(i); rex(nil, i, /&?/); end
|
|
@@ -785,12 +816,19 @@ module Fugit
|
|
|
785
816
|
def r_mon(i); seq(nil, i, :mon, :_mon, '?'); end
|
|
786
817
|
def r_dow(i); seq(nil, i, :dow, :_dow, '?'); end
|
|
787
818
|
|
|
819
|
+
# t: tilde range
|
|
820
|
+
def t_mos(i); seq(nil, i, :mos, '?', :tilde, :mos, '?'); end
|
|
821
|
+
def t_hou(i); seq(nil, i, :hou, '?', :tilde, :hou, '?'); end
|
|
822
|
+
def t_dom(i); seq(nil, i, :dom, '?', :tilde, :dom, '?'); end
|
|
823
|
+
def t_mon(i); seq(nil, i, :mon, '?', :tilde, :mon, '?'); end
|
|
824
|
+
def t_dow(i); seq(nil, i, :dow, '?', :tilde, :dow, '?'); end
|
|
825
|
+
|
|
788
826
|
# sor: star or range
|
|
789
|
-
def sor_mos(i); alt(nil, i, :star, :r_mos); end
|
|
790
|
-
def sor_hou(i); alt(nil, i, :star, :r_hou); end
|
|
791
|
-
def sor_dom(i); alt(nil, i, :star, :r_dom); end
|
|
792
|
-
def sor_mon(i); alt(nil, i, :star, :r_mon); end
|
|
793
|
-
def sor_dow(i); alt(nil, i, :star, :r_dow); end
|
|
827
|
+
def sor_mos(i); alt(nil, i, :star, :t_mos, :r_mos); end
|
|
828
|
+
def sor_hou(i); alt(nil, i, :star, :t_hou, :r_hou); end
|
|
829
|
+
def sor_dom(i); alt(nil, i, :star, :t_dom, :r_dom); end
|
|
830
|
+
def sor_mon(i); alt(nil, i, :star, :t_mon, :r_mon); end
|
|
831
|
+
def sor_dow(i); alt(nil, i, :star, :t_dow, :r_dow); end
|
|
794
832
|
|
|
795
833
|
# sorws: star or range with[out] slash
|
|
796
834
|
def sorws_mos(i); seq(nil, i, :sor_mos, :slash, '?'); end
|
|
@@ -870,12 +908,16 @@ module Fugit
|
|
|
870
908
|
|
|
871
909
|
def rewrite_elt(k, t)
|
|
872
910
|
|
|
873
|
-
at, zt, slt, hat, mot = nil
|
|
911
|
+
at, zt, spt, slt, hat, mot = nil
|
|
912
|
+
|
|
913
|
+
t.subgather(nil).each do |tt|
|
|
874
914
|
case tt.name
|
|
875
915
|
when :slash then slt = tt
|
|
876
916
|
when :hash then hat = tt
|
|
877
917
|
when :mod then mot = tt
|
|
878
|
-
|
|
918
|
+
when :hyphen then spt = tt
|
|
919
|
+
when :tilde then spt = tt
|
|
920
|
+
else if spt; zt ||= tt; else; at = tt; end
|
|
879
921
|
end
|
|
880
922
|
end
|
|
881
923
|
|
|
@@ -889,11 +931,12 @@ module Fugit
|
|
|
889
931
|
|
|
890
932
|
a = at ? rewrite_bound(k, at) : nil
|
|
891
933
|
z = zt ? rewrite_bound(k, zt) : nil
|
|
934
|
+
sp = spt ? spt.name : nil
|
|
892
935
|
|
|
893
936
|
#a, z = z, a if a && z && a > z
|
|
894
937
|
# handled downstream since gh-27
|
|
895
938
|
|
|
896
|
-
[ a, z, sl, ha, mo ]
|
|
939
|
+
[ a, z, sp, sl, ha, mo ]
|
|
897
940
|
end
|
|
898
941
|
|
|
899
942
|
def rewrite_entry(t)
|
data/lib/fugit/nat.rb
CHANGED
|
@@ -710,7 +710,7 @@ module Fugit
|
|
|
710
710
|
e.uniq.sort_by(&letters_last).collect(&:to_s).join(',') }
|
|
711
711
|
.join(' ')
|
|
712
712
|
|
|
713
|
-
c = Fugit::Cron.parse(s)
|
|
713
|
+
c = Fugit::Cron.parse(s, opts)
|
|
714
714
|
|
|
715
715
|
if opts[:strict]
|
|
716
716
|
restrict(a, c)
|
|
@@ -719,6 +719,8 @@ module Fugit
|
|
|
719
719
|
end
|
|
720
720
|
end
|
|
721
721
|
|
|
722
|
+
SLASH_REGEX = /^(\d+|\*)\/(\d+)$/.freeze
|
|
723
|
+
|
|
722
724
|
# Return nil if the cron is "not strict"
|
|
723
725
|
#
|
|
724
726
|
# For example, "0 0/17 * * *" (gh-86) is a perfectly valid
|
|
@@ -729,16 +731,18 @@ module Fugit
|
|
|
729
731
|
#
|
|
730
732
|
def restrict(a, cron)
|
|
731
733
|
|
|
732
|
-
if m = ((a[
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
734
|
+
if m = ((a[0] && a[0] != [ 0 ] && a[0][0]) || '').match(SLASH_REGEX)
|
|
735
|
+
return nil unless (1..60).include?(m[1].to_i)
|
|
736
|
+
end
|
|
737
|
+
if m = ((a[1] && a[1][0]) || '').match(SLASH_REGEX)
|
|
738
|
+
return nil unless [ 1, 2, 3, 4, 5, 6, 8, 12 ].include?(m[1].to_i)
|
|
736
739
|
end
|
|
737
740
|
|
|
738
741
|
cron
|
|
739
742
|
end
|
|
740
743
|
|
|
741
744
|
def slot(key, default)
|
|
745
|
+
|
|
742
746
|
s = @slots[key]
|
|
743
747
|
s ? s.data0 : [ default ]
|
|
744
748
|
end
|
data/lib/fugit/parse.rb
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Fugit
|
|
4
4
|
|
|
5
|
+
DO_PARSE_ORDER = %i[ cron duration nat at ].freeze
|
|
6
|
+
|
|
5
7
|
class << self
|
|
6
8
|
|
|
7
|
-
def parse_cron(s); ::Fugit::Cron.parse(s); end
|
|
9
|
+
def parse_cron(s, opts={}); ::Fugit::Cron.parse(s, opts || {}); end
|
|
8
10
|
def parse_duration(s); ::Fugit::Duration.parse(s); end
|
|
9
|
-
def parse_nat(s, opts={}); ::Fugit::Nat.parse(s, opts); end
|
|
10
|
-
def parse_at(s); ::Fugit::At.parse(s); end
|
|
11
|
+
def parse_nat(s, opts={}); ::Fugit::Nat.parse(s, opts || {}); end
|
|
12
|
+
def parse_at(s, opts={}); ::Fugit::At.parse(s, opts || {}); end
|
|
11
13
|
def parse_in(s); parse_duration(s); end
|
|
12
14
|
|
|
13
|
-
def do_parse_cron(s); ::Fugit::Cron.do_parse(s); end
|
|
15
|
+
def do_parse_cron(s, opts={}); ::Fugit::Cron.do_parse(s, opts || {}); end
|
|
14
16
|
def do_parse_duration(s); ::Fugit::Duration.do_parse(s); end
|
|
15
|
-
def do_parse_nat(s, opts={}); ::Fugit::Nat.do_parse(s, opts); end
|
|
17
|
+
def do_parse_nat(s, opts={}); ::Fugit::Nat.do_parse(s, opts || {}); end
|
|
16
18
|
def do_parse_at(s); ::Fugit::At.do_parse(s); end
|
|
17
19
|
def do_parse_in(s); do_parse_duration(s); end
|
|
18
20
|
|
|
@@ -20,10 +22,10 @@ module Fugit
|
|
|
20
22
|
|
|
21
23
|
opts[:at] = opts[:in] if opts.has_key?(:in)
|
|
22
24
|
|
|
23
|
-
(opts[:cron] != false && parse_cron(s)) || # 542ms 616ms
|
|
25
|
+
(opts[:cron] != false && parse_cron(s, opts || {})) || # 542ms 616ms
|
|
24
26
|
(opts[:duration] != false && parse_duration(s)) || # 645ms # 534ms
|
|
25
|
-
(opts[:nat] != false && parse_nat(s, opts)) || # 2s # 35s
|
|
26
|
-
(opts[:at] != false && parse_at(s)) || # 568ms 622ms
|
|
27
|
+
(opts[:nat] != false && parse_nat(s, opts || {})) || # 2s # 35s
|
|
28
|
+
(opts[:at] != false && parse_at(s, opts || {})) || # 568ms 622ms
|
|
27
29
|
nil
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -34,7 +36,7 @@ module Fugit
|
|
|
34
36
|
result = nil
|
|
35
37
|
errors = []
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
DO_PARSE_ORDER
|
|
38
40
|
.each { |k|
|
|
39
41
|
begin
|
|
40
42
|
result ||= (opts[k] != false && self.send("do_parse_#{k}", s))
|
|
@@ -52,14 +54,14 @@ module Fugit
|
|
|
52
54
|
|
|
53
55
|
def parse_cronish(s, opts={})
|
|
54
56
|
|
|
55
|
-
r = parse_cron(s) || parse_nat(s, opts)
|
|
57
|
+
r = parse_cron(s, opts) || parse_nat(s, opts)
|
|
56
58
|
|
|
57
59
|
r.is_a?(::Fugit::Cron) ? r : nil
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
def do_parse_cronish(s, opts={})
|
|
61
63
|
|
|
62
|
-
parse_cronish(s) ||
|
|
64
|
+
parse_cronish(s, opts) ||
|
|
63
65
|
fail(ArgumentError.new("not cron or 'natural' cron string: #{s.inspect}"))
|
|
64
66
|
end
|
|
65
67
|
|
data/lib/fugit.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fugit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Mettraux
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-07-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: raabro
|
|
@@ -30,20 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '1'
|
|
34
|
-
- - ">="
|
|
35
|
-
- !ruby/object:Gem::Version
|
|
36
|
-
version: 1.2.11
|
|
33
|
+
version: '1.4'
|
|
37
34
|
type: :runtime
|
|
38
35
|
prerelease: false
|
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
40
37
|
requirements:
|
|
41
38
|
- - "~>"
|
|
42
39
|
- !ruby/object:Gem::Version
|
|
43
|
-
version: '1'
|
|
44
|
-
- - ">="
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: 1.2.11
|
|
40
|
+
version: '1.4'
|
|
47
41
|
- !ruby/object:Gem::Dependency
|
|
48
42
|
name: rspec
|
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -72,6 +66,20 @@ dependencies:
|
|
|
72
66
|
- - "~>"
|
|
73
67
|
- !ruby/object:Gem::Version
|
|
74
68
|
version: '0.10'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: ostruct
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
75
83
|
description: Time tools for flor and the floraison project. Cron parsing and occurrence
|
|
76
84
|
computing. Timestamps and more.
|
|
77
85
|
email:
|