crony 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in crony.gemspec
4
+ gemspec
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'cucumber', :cli => '--profile default' do
5
+ watch(%r{^features/.+\.feature$}) { 'features' }
6
+ watch(%r{^features/support/.+$}) { 'features' }
7
+ watch(%r{^lib/.+$}) { 'features' }
8
+ watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
9
+ end
@@ -0,0 +1,9 @@
1
+ Copyright (c) 2013 Michael Berkowitz
2
+
3
+ MIT License
4
+
5
+ take my code with you
6
+ and do whatever you want
7
+ but please don't blame me
8
+
9
+ --Aaron Swartz
@@ -0,0 +1,55 @@
1
+ ## Does this sound like you?
2
+
3
+ * you _sorta_ know how to write a [cron][cron_wiki] expression
4
+ * you have to write them from time to time, but...
5
+ * you know that getting the answer from a friend (or the internet) isn't going to help you learn how to write them for yourself
6
+ * you'd just like to know if that cron expression you just wrote does what you meant it to
7
+
8
+ Crony's got your back.
9
+
10
+ # Crony
11
+
12
+ Crony is a 'spell-checker' for cron expressions. Ask it to parse that cron
13
+ expression you just wrote, and it'll let you know if it's gonna do anything
14
+ close to what you meant it to.
15
+
16
+ See Usage below for examples.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ gem 'crony'
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install crony
31
+
32
+ ## Usage
33
+
34
+ irb > require 'crony'
35
+ => true
36
+ irb > Crony.parse("3 4 22 5 * 2015")
37
+ => "04:03 on May 22, 2015"
38
+ irb > Crony.parse("30 6 * * 6L *")
39
+ => "06:30 on the last Saturday of every month"
40
+ irb > Crony.parse("10-15 6,12,18 * NOV THU#4 *")
41
+ => "Every minute between 06:10 and 06:15, 12:10 and 12:15 and 18:10 and 18:15 on the 4th Thursday of November"
42
+
43
+ ## Contributing
44
+
45
+ 1. Fork it
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
48
+ 4. Push to the branch (`git push origin my-new-feature`)
49
+ 5. Create new Pull Request
50
+
51
+ ## Copyright
52
+
53
+ Copyright (c) 2013 Michael Berkowitz (@hal678). See LICENSE.txt for further details.
54
+
55
+ [cron_wiki]: http://en.wikipedia.org/wiki/Cron
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'cucumber/rake/task'
4
+
5
+ Cucumber::Rake::Task.new(:features)
6
+
7
+ Cucumber::Rake::Task.new('features:wip', 'Run work-in-progress cukes') do |task|
8
+ task.profile = 'wip'
9
+ end
10
+
11
+ task default: 'features:wip'
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'crony/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "crony"
8
+ gem.version = Crony::VERSION
9
+ gem.authors = ["Michael Berkowitz"]
10
+ gem.email = ["michael.berkowitz@gmail.com"]
11
+ gem.description = %q{Spell-checker for cron expressions}
12
+ gem.summary = %q{Confirm that the cron expression you just wrote does what you meant it to do.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency('cucumber')
21
+ gem.add_development_dependency('guard')
22
+ gem.add_development_dependency('guard-cucumber')
23
+ gem.add_development_dependency('rb-fsevent')
24
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ default: --tags ~@wip --strict --format pretty
3
+ wip: --tags @wip --format pretty
@@ -0,0 +1,251 @@
1
+ Feature: Parsing a cron expression with dates
2
+ In order to give anyone who doesn't have cron format 100% down cold
3
+ a way to check that they've got the correct expression
4
+
5
+ Scenario Outline: Parsing cron expressions
6
+ When I parse the expression "<cron_expression>"
7
+ Then I return "<human_readable>"
8
+
9
+ Examples:
10
+ | cron_expression | human_readable |
11
+ | * * 3-20/3 * * * | Every minute on every 3rd day from the 3rd to the 20th of every month |
12
+ | * * 1-15 * * * | Every minute on the 1st to the 15th of every month |
13
+ | * * */5 * * * | Every minute on every 5th day of every month |
14
+ | * * 2/5 * * * | Every minute on every 5th day starting on the 2nd of every month |
15
+ | * * 22 * * * | Every minute on the 22nd of every month |
16
+ | * * 4,5,7 * * * | Every minute on the 4th, 5th and 7th of every month |
17
+ | * * L * * * | Every minute on the last day of every month |
18
+ | * * 17W * * * | Every minute on the weekday closest to the 17th of every month |
19
+ | * * * 1-11/2 * * | Every minute on every day of every 2nd month from January to November |
20
+ | * * 3-20/3 1-11/2 * * | Every minute on every 3rd day from the 3rd to the 20th of every 2nd month from January to November |
21
+ | * * 1-15 1-11/2 * * | Every minute on the 1st to the 15th of every 2nd month from January to November |
22
+ | * * */5 1-11/2 * * | Every minute on every 5th day of every 2nd month from January to November |
23
+ | * * 2/5 1-11/2 * * | Every minute on every 5th day starting on the 2nd of every 2nd month from January to November |
24
+ | * * 22 1-11/2 * * | Every minute on the 22nd of every 2nd month from January to November |
25
+ | * * 4,5,7 1-11/2 * * | Every minute on the 4th, 5th and 7th of every 2nd month from January to November |
26
+ | * * L 1-11/2 * * | Every minute on the last day of every 2nd month from January to November |
27
+ | * * 17W 1-11/2 * * | Every minute on the weekday closest to the 17th of every 2nd month from January to November |
28
+ | * * * 3-7 * * | Every minute on every day of every month from March to July |
29
+ | * * 3-20/3 3-7 * * | Every minute on every 3rd day from the 3rd to the 20th of every month from March to July |
30
+ | * * 1-15 3-7 * * | Every minute on the 1st to the 15th of every month from March to July |
31
+ | * * */5 3-7 * * | Every minute on every 5th day of every month from March to July |
32
+ | * * 2/5 3-7 * * | Every minute on every 5th day starting on the 2nd of every month from March to July |
33
+ | * * 22 3-7 * * | Every minute on the 22nd of every month from March to July |
34
+ | * * 4,5,7 3-7 * * | Every minute on the 4th, 5th and 7th of every month from March to July |
35
+ | * * L 3-7 * * | Every minute on the last day of every month from March to July |
36
+ | * * 17W 3-7 * * | Every minute on the weekday closest to the 17th of every month from March to July |
37
+ | * * * */4 * * | Every minute on every day of every 4th month |
38
+ | * * 3-20/3 */4 * * | Every minute on every 3rd day from the 3rd to the 20th of every 4th month |
39
+ | * * 1-15 */4 * * | Every minute on the 1st to the 15th of every 4th month |
40
+ | * * */5 */4 * * | Every minute on every 5th day of every 4th month |
41
+ | * * 2/5 */4 * * | Every minute on every 5th day starting on the 2nd of every 4th month |
42
+ | * * 22 */4 * * | Every minute on the 22nd of every 4th month |
43
+ | * * 4,5,7 */4 * * | Every minute on the 4th, 5th and 7th of every 4th month |
44
+ | * * L */4 * * | Every minute on the last day of every 4th month |
45
+ | * * 17W */4 * * | Every minute on the weekday closest to the 17th of every 4th month |
46
+ | * * * 2/5 * * | Every minute on every day of every 5th month from February to December |
47
+ | * * 3-20/3 2/5 * * | Every minute on every 3rd day from the 3rd to the 20th of every 5th month from February to December |
48
+ | * * 1-15 2/5 * * | Every minute on the 1st to the 15th of every 5th month from February to December |
49
+ | * * */5 2/5 * * | Every minute on every 5th day of every 5th month from February to December |
50
+ | * * 2/5 2/5 * * | Every minute on every 5th day starting on the 2nd of every 5th month from February to December |
51
+ | * * 22 2/5 * * | Every minute on the 22nd of every 5th month from February to December |
52
+ | * * 4,5,7 2/5 * * | Every minute on the 4th, 5th and 7th of every 5th month from February to December |
53
+ | * * L 2/5 * * | Every minute on the last day of every 5th month from February to December |
54
+ | * * 17W 2/5 * * | Every minute on the weekday closest to the 17th of every 5th month from February to December |
55
+ | * * * 5 * * | Every minute on every day of May |
56
+ | * * 3-20/3 5 * * | Every minute on every 3rd day from the 3rd to the 20th of May |
57
+ | * * 1-15 5 * * | Every minute on the 1st to the 15th of May |
58
+ | * * */5 5 * * | Every minute on every 5th day of May |
59
+ | * * 2/5 5 * * | Every minute on every 5th day of May starting on the 2nd |
60
+ | * * 22 5 * * | Every minute on May 22nd |
61
+ | * * 4,5,7 5 * * | Every minute on May 4th, 5th and 7th |
62
+ | * * L 5 * * | Every minute on the last day of May |
63
+ | * * 17W 5 * * | Every minute on the weekday closest to May 17th |
64
+ | * * * APR,AUG * * | Every minute on every day of April and August |
65
+ | * * 3-20/3 apr,aug * * | Every minute on every 3rd day from the 3rd to the 20th of April and August |
66
+ | * * 1-15 APR,AUG * * | Every minute on the 1st to the 15th of April and August |
67
+ | * * */5 apr,aug * * | Every minute on every 5th day of April and August |
68
+ | * * 2/5 apr,aug * * | Every minute on every 5th day of April and August starting on the 2nd |
69
+ | * * 22 APR,AUG * * | Every minute on the 22nd of April and August |
70
+ | * * 4,5,7 apr,aug * * | Every minute on the 4th, 5th and 7th of April and August |
71
+ | * * L APR,AUG * * | Every minute on the last day of April and August |
72
+ | * * 17W APR,AUG * * | Every minute on the weekdays closest to April and August 17th |
73
+ | * * * * 1 * | Every minute on every Monday of every month |
74
+ | * * 3-20/3 * 1 * | Every minute on every 3rd day from the 3rd to the 20th and every Monday of every month |
75
+ | * * 1-15 * 1 * | Every minute on the 1st to the 15th and every Monday of every month |
76
+ | * * */5 * 1 * | Every minute on every 5th day and every Monday of every month |
77
+ | * * 2/5 * 1 * | Every minute on every 5th day starting on the 2nd and every Monday of every month |
78
+ | * * 22 * 1 * | Every minute on the 22nd and every Monday of every month |
79
+ | * * 4,5,7 * 1 * | Every minute on the 4th, 5th and 7th and every Monday of every month |
80
+ | * * L * 1 * | Every minute on the last day and every Monday of every month |
81
+ | * * 17W * 1 * | Every minute on the weekday closest to the 17th and every Monday of every month |
82
+ | * * * 1-11/2 1 * | Every minute on every Monday of every 2nd month from January to November |
83
+ | * * 3-20/3 1-11/2 1 * | Every minute on every 3rd day from the 3rd to the 20th and every Monday of every 2nd month from January to November |
84
+ | * * 1-15 1-11/2 1 * | Every minute on the 1st to the 15th and every Monday of every 2nd month from January to November |
85
+ | * * */5 1-11/2 1 * | Every minute on every 5th day and every Monday of every 2nd month from January to November |
86
+ | * * 2/5 1-11/2 1 * | Every minute on every 5th day starting on the 2nd and every Monday of every 2nd month from January to November |
87
+ | * * 22 1-11/2 1 * | Every minute on the 22nd and every Monday of every 2nd month from January to November |
88
+ | * * 4,5,7 1-11/2 1 * | Every minute on the 4th, 5th and 7th and every Monday of every 2nd month from January to November |
89
+ | * * L 1-11/2 1 * | Every minute on the last day and every Monday of every 2nd month from January to November |
90
+ | * * 17W 1-11/2 1 * | Every minute on the weekday closest to the 17th and every Monday of every 2nd month from January to November |
91
+ | * * * 5 1 * | Every minute on every Monday of May |
92
+ | * * 3-20/3 5 1 * | Every minute on every 3rd day from the 3rd to the 20th and every Monday of May |
93
+ | * * 1-15 5 1 * | Every minute on the 1st to the 15th and every Monday of May |
94
+ | * * */5 5 1 * | Every minute on every 5th day and every Monday of May |
95
+ | * * 2/5 5 1 * | Every minute on every 5th day starting on the 2nd and every Monday of May |
96
+ | * * 22 5 1 * | Every minute on May 22nd and every Monday in May |
97
+ | * * 4,5,7 5 1 * | Every minute on May 4th, 5th and 7th and every Monday in May |
98
+ | * * L 5 1 * | Every minute on the last day and every Monday of May |
99
+ | * * 17W 5 1 * | Every minute on the weekday closest to May 17th and every Monday in May |
100
+ | * * * APR,AUG 1 * | Every minute on every Monday of April and August |
101
+ | * * 3-20/3 apr,aug 1 * | Every minute on every 3rd day from the 3rd to the 20th and every Monday of April and August |
102
+ | * * 1-15 APR,AUG 1 * | Every minute on the 1st to the 15th and every Monday of April and August |
103
+ | * * */5 apr,aug 1 * | Every minute on every 5th day and every Monday of April and August |
104
+ | * * 2/5 apr,aug 1 * | Every minute on every 5th day starting on the 2nd and every Monday of April and August |
105
+ | * * 22 APR,AUG 1 * | Every minute on the 22nd and every Monday of April and August |
106
+ | * * 4,5,7 apr,aug 1 * | Every minute on the 4th, 5th and 7th and every Monday of April and August |
107
+ | * * L APR,AUG 1 * | Every minute on the last day and every Monday of April and August |
108
+ | * * 17W APR,AUG 1 * | Every minute on the weekdays closest to April and August 17th and every Monday in April and August |
109
+ | * * * * 2-4 * | Every minute on every Tuesday, Wednesday and Thursday of every month |
110
+ | * * 3-20/3 * 2-4 * | Every minute on every 3rd day from the 3rd to the 20th and every Tuesday, Wednesday and Thursday of every month |
111
+ | * * 1-15 * 2-4 * | Every minute on the 1st to the 15th and every Tuesday, Wednesday and Thursday of every month |
112
+ | * * */5 * 2-4 * | Every minute on every 5th day and every Tuesday, Wednesday and Thursday of every month |
113
+ | * * 2/5 * 2-4 * | Every minute on every 5th day starting on the 2nd and every Tuesday, Wednesday and Thursday of every month |
114
+ | * * 22 * 2-4 * | Every minute on the 22nd and every Tuesday, Wednesday and Thursday of every month |
115
+ | * * 4,5,7 * 2-4 * | Every minute on the 4th, 5th and 7th and every Tuesday, Wednesday and Thursday of every month |
116
+ | * * L * 2-4 * | Every minute on the last day and every Tuesday, Wednesday and Thursday of every month |
117
+ | * * 17W * 2-4 * | Every minute on the weekday closest to the 17th and every Tuesday, Wednesday and Thursday of every month |
118
+ | * * * 1-11/2 2-4 * | Every minute on every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
119
+ | * * 3-20/3 1-11/2 2-4 * | Every minute on every 3rd day from the 3rd to the 20th and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
120
+ | * * 1-15 1-11/2 2-4 * | Every minute on the 1st to the 15th and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
121
+ | * * */5 1-11/2 2-4 * | Every minute on every 5th day and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
122
+ | * * 2/5 1-11/2 2-4 * | Every minute on every 5th day starting on the 2nd and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
123
+ | * * 22 1-11/2 2-4 * | Every minute on the 22nd and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
124
+ | * * 4,5,7 1-11/2 2-4 * | Every minute on the 4th, 5th and 7th and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
125
+ | * * L 1-11/2 2-4 * | Every minute on the last day and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
126
+ | * * 17W 1-11/2 2-4 * | Every minute on the weekday closest to the 17th and every Tuesday, Wednesday and Thursday of every 2nd month from January to November |
127
+ | * * * 5 2-4 * | Every minute on every Tuesday, Wednesday and Thursday of May |
128
+ | * * 3-20/3 5 2-4 * | Every minute on every 3rd day from the 3rd to the 20th and every Tuesday, Wednesday and Thursday of May |
129
+ | * * 1-15 5 2-4 * | Every minute on the 1st to the 15th and every Tuesday, Wednesday and Thursday of May |
130
+ | * * */5 5 2-4 * | Every minute on every 5th day and every Tuesday, Wednesday and Thursday of May |
131
+ | * * 2/5 5 2-4 * | Every minute on every 5th day starting on the 2nd and every Tuesday, Wednesday and Thursday of May |
132
+ | * * 22 5 2-4 * | Every minute on May 22nd and every Tuesday, Wednesday and Thursday in May |
133
+ | * * 4,5,7 5 2-4 * | Every minute on May 4th, 5th and 7th and every Tuesday, Wednesday and Thursday in May |
134
+ | * * L 5 2-4 * | Every minute on the last day and every Tuesday, Wednesday and Thursday of May |
135
+ | * * 17W 5 2-4 * | Every minute on the weekday closest to May 17th and every Tuesday, Wednesday and Thursday in May |
136
+ | * * * APR,AUG 2-4 * | Every minute on every Tuesday, Wednesday and Thursday of April and August |
137
+ | * * 3-20/3 apr,aug 2-4 * | Every minute on every 3rd day from the 3rd to the 20th and every Tuesday, Wednesday and Thursday of April and August |
138
+ | * * 1-15 APR,AUG 2-4 * | Every minute on the 1st to the 15th and every Tuesday, Wednesday and Thursday of April and August |
139
+ | * * */5 apr,aug 2-4 * | Every minute on every 5th day and every Tuesday, Wednesday and Thursday of April and August |
140
+ | * * 2/5 apr,aug 2-4 * | Every minute on every 5th day starting on the 2nd and every Tuesday, Wednesday and Thursday of April and August |
141
+ | * * 22 APR,AUG 2-4 * | Every minute on the 22nd and every Tuesday, Wednesday and Thursday of April and August |
142
+ | * * 4,5,7 apr,aug 2-4 * | Every minute on the 4th, 5th and 7th and every Tuesday, Wednesday and Thursday of April and August |
143
+ | * * L APR,AUG 2-4 * | Every minute on the last day and every Tuesday, Wednesday and Thursday of April and August |
144
+ | * * 17W APR,AUG 2-4 * | Every minute on the weekdays closest to April and August 17th and every Tuesday, Wednesday and Thursday in April and August |
145
+ | * * * * mon,wed,fri * | Every minute on every Monday, Wednesday and Friday of every month |
146
+ | * * 3-20/3 * MON,WED,FRI * | Every minute on every 3rd day from the 3rd to the 20th and every Monday, Wednesday and Friday of every month |
147
+ | * * 1-15 * MON,WED,FRI * | Every minute on the 1st to the 15th and every Monday, Wednesday and Friday of every month |
148
+ | * * */5 * MON,WED,FRI * | Every minute on every 5th day and every Monday, Wednesday and Friday of every month |
149
+ | * * 2/5 * MON,WED,FRI * | Every minute on every 5th day starting on the 2nd and every Monday, Wednesday and Friday of every month |
150
+ | * * 22 * MON,WED,FRI * | Every minute on the 22nd and every Monday, Wednesday and Friday of every month |
151
+ | * * 4,5,7 * MON,WED,FRI * | Every minute on the 4th, 5th and 7th and every Monday, Wednesday and Friday of every month |
152
+ | * * L * MON,WED,FRI * | Every minute on the last day and every Monday, Wednesday and Friday of every month |
153
+ | * * 17W * MON,WED,FRI * | Every minute on the weekday closest to the 17th and every Monday, Wednesday and Friday of every month |
154
+ | * * * 1-11/2 MON,WED,FRI * | Every minute on every Monday, Wednesday and Friday of every 2nd month from January to November |
155
+ | * * 3-20/3 1-11/2 MON,WED,FRI * | Every minute on every 3rd day from the 3rd to the 20th and every Monday, Wednesday and Friday of every 2nd month from January to November |
156
+ | * * 1-15 1-11/2 MON,WED,FRI * | Every minute on the 1st to the 15th and every Monday, Wednesday and Friday of every 2nd month from January to November |
157
+ | * * */5 1-11/2 MON,WED,FRI * | Every minute on every 5th day and every Monday, Wednesday and Friday of every 2nd month from January to November |
158
+ | * * 2/5 1-11/2 MON,WED,FRI * | Every minute on every 5th day starting on the 2nd and every Monday, Wednesday and Friday of every 2nd month from January to November |
159
+ | * * 22 1-11/2 MON,WED,FRI * | Every minute on the 22nd and every Monday, Wednesday and Friday of every 2nd month from January to November |
160
+ | * * 4,5,7 1-11/2 MON,WED,FRI * | Every minute on the 4th, 5th and 7th and every Monday, Wednesday and Friday of every 2nd month from January to November |
161
+ | * * L 1-11/2 MON,WED,FRI * | Every minute on the last day and every Monday, Wednesday and Friday of every 2nd month from January to November |
162
+ | * * 17W 1-11/2 MON,WED,FRI * | Every minute on the weekday closest to the 17th and every Monday, Wednesday and Friday of every 2nd month from January to November |
163
+ | * * * 5 MON,WED,FRI * | Every minute on every Monday, Wednesday and Friday of May |
164
+ | * * 3-20/3 5 MON,WED,FRI * | Every minute on every 3rd day from the 3rd to the 20th and every Monday, Wednesday and Friday of May |
165
+ | * * 1-15 5 MON,WED,FRI * | Every minute on the 1st to the 15th and every Monday, Wednesday and Friday of May |
166
+ | * * 2/5 5 MON,WED,FRI * | Every minute on every 5th day starting on the 2nd and every Monday, Wednesday and Friday of May |
167
+ | * * 22 5 MON,WED,FRI * | Every minute on May 22nd and every Monday, Wednesday and Friday in May |
168
+ | * * 4,5,7 5 MON,WED,FRI * | Every minute on May 4th, 5th and 7th and every Monday, Wednesday and Friday in May |
169
+ | * * L 5 MON,WED,FRI * | Every minute on the last day and every Monday, Wednesday and Friday of May |
170
+ | * * 17W 5 MON,WED,FRI * | Every minute on the weekday closest to May 17th and every Monday, Wednesday and Friday in May |
171
+ | * * * APR,AUG MON,WED,FRI * | Every minute on every Monday, Wednesday and Friday of April and August |
172
+ | * * 3-20/3 apr,aug MON,WED,FRI * | Every minute on every 3rd day from the 3rd to the 20th and every Monday, Wednesday and Friday of April and August |
173
+ | * * 1-15 APR,AUG MON,WED,FRI * | Every minute on the 1st to the 15th and every Monday, Wednesday and Friday of April and August |
174
+ | * * */5 apr,aug MON,WED,FRI * | Every minute on every 5th day and every Monday, Wednesday and Friday of April and August |
175
+ | * * 2/5 apr,aug MON,WED,FRI * | Every minute on every 5th day starting on the 2nd and every Monday, Wednesday and Friday of April and August |
176
+ | * * 22 APR,AUG MON,WED,FRI * | Every minute on the 22nd and every Monday, Wednesday and Friday of April and August |
177
+ | * * 4,5,7 apr,aug MON,WED,FRI * | Every minute on the 4th, 5th and 7th and every Monday, Wednesday and Friday of April and August |
178
+ | * * L APR,AUG MON,WED,FRI * | Every minute on the last day and every Monday, Wednesday and Friday of April and August |
179
+ | * * 17W APR,AUG MON,WED,FRI * | Every minute on the weekdays closest to April and August 17th and every Monday, Wednesday and Friday in April and August |
180
+ | * * * * 6l * | Every minute on the last Saturday of every month |
181
+ | * * 3-20/3 * 6L * | Every minute on every 3rd day from the 3rd to the 20th and the last Saturday of every month |
182
+ | * * 1-15 * 6L * | Every minute on the 1st to the 15th and the last Saturday of every month |
183
+ | * * */5 * 6L * | Every minute on every 5th day and the last Saturday of every month |
184
+ | * * 2/5 * 6L * | Every minute on every 5th day starting on the 2nd and the last Saturday of every month |
185
+ | * * 22 * 6L * | Every minute on the 22nd and the last Saturday of every month |
186
+ | * * 4,5,7 * 6L * | Every minute on the 4th, 5th and 7th and the last Saturday of every month |
187
+ | * * L * 6L * | Every minute on the last day and the last Saturday of every month |
188
+ | * * 17W * 6L * | Every minute on the weekday closest to the 17th and the last Saturday of every month |
189
+ | * * * 1-11/2 6L * | Every minute on the last Saturday of every 2nd month from January to November |
190
+ | * * 3-20/3 1-11/2 6L * | Every minute on every 3rd day from the 3rd to the 20th and the last Saturday of every 2nd month from January to November |
191
+ | * * 1-15 1-11/2 6L * | Every minute on the 1st to the 15th and the last Saturday of every 2nd month from January to November |
192
+ | * * */5 1-11/2 6L * | Every minute on every 5th day and the last Saturday of every 2nd month from January to November |
193
+ | * * 2/5 1-11/2 6L * | Every minute on every 5th day starting on the 2nd and the last Saturday of every 2nd month from January to November |
194
+ | * * 22 1-11/2 6L * | Every minute on the 22nd and the last Saturday of every 2nd month from January to November |
195
+ | * * 4,5,7 1-11/2 6L * | Every minute on the 4th, 5th and 7th and the last Saturday of every 2nd month from January to November |
196
+ | * * L 1-11/2 6L * | Every minute on the last day and the last Saturday of every 2nd month from January to November |
197
+ | * * 17W 1-11/2 6L * | Every minute on the weekday closest to the 17th and the last Saturday of every 2nd month from January to November |
198
+ | * * * 5 6L * | Every minute on the last Saturday of May |
199
+ | * * 3-20/3 5 6L * | Every minute on every 3rd day from the 3rd to the 20th and the last Saturday of May |
200
+ | * * 1-15 5 6L * | Every minute on the 1st to the 15th and the last Saturday of May |
201
+ | * * */5 5 6L * | Every minute on every 5th day and the last Saturday of May |
202
+ | * * 2/5 5 6L * | Every minute on every 5th day starting on the 2nd and the last Saturday of May |
203
+ | * * 22 5 6L * | Every minute on May 22nd and the last Saturday in May |
204
+ | * * 4,5,7 5 6L * | Every minute on May 4th, 5th and 7th and the last Saturday in May |
205
+ | * * L 5 6L * | Every minute on the last day and the last Saturday of May |
206
+ | * * 17W 5 6L * | Every minute on the weekday closest to May 17th and the last Saturday in May |
207
+ | * * * APR,AUG 6L * | Every minute on the last Saturday of April and August |
208
+ | * * 3-20/3 apr,aug 6L * | Every minute on every 3rd day from the 3rd to the 20th and the last Saturday of April and August |
209
+ | * * 1-15 APR,AUG 6L * | Every minute on the 1st to the 15th and the last Saturday of April and August |
210
+ | * * */5 apr,aug 6L * | Every minute on every 5th day and the last Saturday of April and August |
211
+ | * * 2/5 apr,aug 6L * | Every minute on every 5th day starting on the 2nd and the last Saturday of April and August |
212
+ | * * 22 APR,AUG 6L * | Every minute on the 22nd and the last Saturday of April and August |
213
+ | * * 4,5,7 apr,aug 6L * | Every minute on the 4th, 5th and 7th and the last Saturday of April and August |
214
+ | * * L APR,AUG satL * | Every minute on the last day and the last Saturday of April and August |
215
+ | * * 17W APR,AUG SATL * | Every minute on the weekdays closest to April and August 17th and the last Saturday in April and August |
216
+ | * * * * THU#4 * | Every minute on the 4th Thursday of every month |
217
+ | * * 3-20/3 * THU#4 * | Every minute on every 3rd day from the 3rd to the 20th and the 4th Thursday of every month |
218
+ | * * 1-15 * THU#4 * | Every minute on the 1st to the 15th and the 4th Thursday of every month |
219
+ | * * */5 * THU#4 * | Every minute on every 5th day and the 4th Thursday of every month |
220
+ | * * 2/5 * THU#4 * | Every minute on every 5th day starting on the 2nd and the 4th Thursday of every month |
221
+ | * * 22 * THU#4 * | Every minute on the 22nd and the 4th Thursday of every month |
222
+ | * * 4,5,7 * THU#4 * | Every minute on the 4th, 5th and 7th and the 4th Thursday of every month |
223
+ | * * L * THU#4 * | Every minute on the last day and the 4th Thursday of every month |
224
+ | * * 17W * THU#4 * | Every minute on the weekday closest to the 17th and the 4th Thursday of every month |
225
+ | * * * 1-11/2 THU#4 * | Every minute on the 4th Thursday of every 2nd month from January to November |
226
+ | * * 3-20/3 1-11/2 THU#4 * | Every minute on every 3rd day from the 3rd to the 20th and the 4th Thursday of every 2nd month from January to November |
227
+ | * * 1-15 1-11/2 THU#4 * | Every minute on the 1st to the 15th and the 4th Thursday of every 2nd month from January to November |
228
+ | * * */5 1-11/2 THU#4 * | Every minute on every 5th day and the 4th Thursday of every 2nd month from January to November |
229
+ | * * 2/5 1-11/2 THU#4 * | Every minute on every 5th day starting on the 2nd and the 4th Thursday of every 2nd month from January to November |
230
+ | * * 22 1-11/2 THU#4 * | Every minute on the 22nd and the 4th Thursday of every 2nd month from January to November |
231
+ | * * 4,5,7 1-11/2 THU#4 * | Every minute on the 4th, 5th and 7th and the 4th Thursday of every 2nd month from January to November |
232
+ | * * L 1-11/2 THU#4 * | Every minute on the last day and the 4th Thursday of every 2nd month from January to November |
233
+ | * * 17W 1-11/2 THU#4 * | Every minute on the weekday closest to the 17th and the 4th Thursday of every 2nd month from January to November |
234
+ | * * * 5 THU#4 * | Every minute on the 4th Thursday of May |
235
+ | * * 3-20/3 5 THU#4 * | Every minute on every 3rd day from the 3rd to the 20th and the 4th Thursday of May |
236
+ | * * 1-15 5 THU#4 * | Every minute on the 1st to the 15th and the 4th Thursday of May |
237
+ | * * */5 5 THU#4 * | Every minute on every 5th day and the 4th Thursday of May |
238
+ | * * 2/5 5 THU#4 * | Every minute on every 5th day starting on the 2nd and the 4th Thursday of May |
239
+ | * * 22 5 THU#4 * | Every minute on May 22nd and the 4th Thursday in May |
240
+ | * * 4,5,7 5 THU#4 * | Every minute on May 4th, 5th and 7th and the 4th Thursday in May |
241
+ | * * L 5 THU#4 * | Every minute on the last day and the 4th Thursday of May |
242
+ | * * 17W 5 THU#4 * | Every minute on the weekday closest to May 17th and the 4th Thursday in May |
243
+ | * * * APR,AUG THU#4 * | Every minute on the 4th Thursday of April and August |
244
+ | * * 3-20/3 apr,aug THU#4 * | Every minute on every 3rd day from the 3rd to the 20th and the 4th Thursday of April and August |
245
+ | * * 1-15 APR,AUG THU#4 * | Every minute on the 1st to the 15th and the 4th Thursday of April and August |
246
+ | * * */5 apr,aug THU#4 * | Every minute on every 5th day and the 4th Thursday of April and August |
247
+ | * * 2/5 apr,aug THU#4 * | Every minute on every 5th day starting on the 2nd and the 4th Thursday of April and August |
248
+ | * * 22 APR,AUG THU#4 * | Every minute on the 22nd and the 4th Thursday of April and August |
249
+ | * * 4,5,7 apr,aug THU#4 * | Every minute on the 4th, 5th and 7th and the 4th Thursday of April and August |
250
+ | * * L APR,AUG thu#4 * | Every minute on the last day and the 4th Thursday of April and August |
251
+ | * * 17W APR,AUG thu#4 * | Every minute on the weekdays closest to April and August 17th and the 4th Thursday in April and August |
@@ -0,0 +1,22 @@
1
+ Feature: Parsing a cron expression
2
+ In order to give anyone who doesn't have cron format 100% down cold
3
+ a way to check that they've got the correct expression
4
+
5
+ Scenario Outline: Parsing cron expressions
6
+ When I parse the expression "<cron_expression>"
7
+ Then I return "<human_readable>"
8
+
9
+ Examples:
10
+ | cron_expression | human_readable |
11
+ | sudo rm -r -f * | Invalid Format |
12
+ | * | Invalid Format |
13
+ | * * | Invalid Format |
14
+ | * * * | Invalid Format |
15
+ | * * * * | Invalid Format |
16
+ | 3 4 22 5 * * | 04:03 on May 22nd |
17
+ | 3 4 22 5 * 1985 | 04:03 on May 22nd, 1985 |
18
+ | 3 4 22 5 * 1985,1989 | 04:03 on May 22nd in 1985 and 1989 |
19
+ | 3 4 22 5 * 1990-2000/2 | 04:03 on May 22nd every 2 years between 1990 and 2000 |
20
+ | 3 4 22 5 * 2013/3 | 04:03 on May 22nd every 3 years starting in 2013 |
21
+ | 3 4 22 5 * */4 | 04:03 on May 22nd every 4 years |
22
+ | 3 4 22 5 * 1985-2000 | 04:03 on May 22nd every year between 1985 and 2000 |
@@ -0,0 +1,9 @@
1
+ require 'crony'
2
+
3
+ When /^I parse the expression "(.*?)"$/ do |expression|
4
+ @parsed = Crony.parse(expression)
5
+ end
6
+
7
+ Then /^I return "(.*?)"$/ do |expected|
8
+ assert_equal expected, @parsed
9
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'crony'
3
+
4
+ require 'test/unit/assertions'
5
+ World(Test::Unit::Assertions)
@@ -0,0 +1,61 @@
1
+ Feature: Parsing a cron expression
2
+ In order to give anyone who doesn't have cron format 100% down cold
3
+ a way to check that they've got the correct expression
4
+
5
+ Scenario Outline: Parsing cron expressions
6
+ When I parse the expression "<cron_expression>"
7
+ Then I return "<human_readable>"
8
+
9
+ Examples:
10
+ | cron_expression | human_readable |
11
+ | * * * * * | Every minute every day |
12
+ | * * * * * * | Every minute every day |
13
+ | * 8-20/3 * * * * | Every minute of every 3rd hour between 08:00 and 20:59 every day |
14
+ | * 9-19 * * * * | Every minute between 09:00 and 19:59 every day |
15
+ | * */4 * * * * | Every minute of every 4th hour every day |
16
+ | * 1/6 * * * * | Every minute of every 6th hour starting at 01:00 every day |
17
+ | * 5 * * * * | Every minute between 05:00 and 05:59 every day |
18
+ | * 5,10,15 * * * * | Every minute between 05:00 and 05:59, 10:00 and 10:59 and 15:00 and 15:59 every day |
19
+ | 0 * * * * | The beginning of every hour every day |
20
+ | 8 * * * * * | The 8th minute of every hour every day |
21
+ | 8 8-20/3 * * * * | The 8th minute of every 3rd hour between 08:00 and 20:59 every day |
22
+ | 8 9-19 * * * * | The 8th minute of every hour between 09:00 and 19:59 every day |
23
+ | 8 */4 * * * * | The 8th minute of every 4th hour every day |
24
+ | 8 1/6 * * * * | The 8th minute of every 6th hour starting at 01:00 every day |
25
+ | 8 5 * * * * | 05:08 every day |
26
+ | 8 5,10,15 * * * * | 05:08, 10:08 and 15:08 every day |
27
+ | 2,21,33,44 * * * * * | The 2nd, 21st, 33rd and 44th minutes of every hour every day |
28
+ | 2,21,33,44 8-20/3 * * * * | The 2nd, 21st, 33rd and 44th minutes of every 3rd hour between 08:00 and 20:59 every day |
29
+ | 2,21,33,44 9-19 * * * * | The 2nd, 21st, 33rd and 44th minutes of every hour between 09:00 and 19:59 every day |
30
+ | 2,21,33,44 */4 * * * * | The 2nd, 21st, 33rd and 44th minutes of every 4th hour every day |
31
+ | 2,21,33,44 1/6 * * * * | The 2nd, 21st, 33rd and 44th minutes of every 6th hour starting at 01:00 every day |
32
+ | 2,21,33,44 5 * * * * | 05:02, 05:21, 05:33 and 05:44 every day |
33
+ | 2,21,33,44 5,10,15 * * * * | 05:02, 05:21, 05:33, 05:44, 10:02, 10:21, 10:33, 10:44, 15:02, 15:21, 15:33 and 15:44 every day |
34
+ | */2 * * * * * | Every 2 minutes every day |
35
+ | */2 8-20/3 * * * * | Every 2 minutes of every 3rd hour between 08:00 and 20:59 every day |
36
+ | */2 9-19 * * * * | Every 2 minutes between 09:00 and 19:59 every day |
37
+ | */2 */4 * * * * | Every 2 minutes of every 4th hour every day |
38
+ | */2 1/6 * * * * | Every 2 minutes of every 6th hour starting at 01:00 every day |
39
+ | */2 5 * * * * | Every 2 minutes between 05:00 and 05:59 every day |
40
+ | */2 5,10,15 * * * * | Every 2 minutes between 05:00 and 05:59, 10:00 and 10:59 and 15:00 and 15:59 every day |
41
+ | 3/4 * * * * * | Every 4 minutes starting at xx:03 of every hour every day |
42
+ | 3/4 8-20/3 * * * * | Every 4 minutes starting at xx:03 of every 3rd hour between 08:00 and 20:59 every day |
43
+ | 3/4 9-19 * * * * | Every 4 minutes starting at xx:03 of every hour between 09:00 and 19:59 every day |
44
+ | 3/4 */4 * * * * | Every 4 minutes starting at xx:03 of every 4th hour every day |
45
+ | 3/4 1/6 * * * * | Every 4 minutes starting at xx:03 of every 6th hour starting at 01:00 every day |
46
+ | 3/4 5 * * * * | Every 4 minutes between 05:03 and 05:59 every day |
47
+ | 3/4 5,10,15 * * * * | Every 4 minutes between 05:03 and 05:59, 10:03 and 10:59 and 15:03 and 15:59 every day |
48
+ | 11-13 * * * * * | Every hour from xx:11 to xx:13 every day |
49
+ | 11-13 8-20/3 * * * * | Every minute between xx:11 and xx:13 of every 3rd hour between 08:00 and 20:59 every day |
50
+ | 11-13 9-19 * * * * | Every minute between xx:11 and xx:13 of every hour between 09:00 and 19:59 every day |
51
+ | 11-13 */4 * * * * | Every minute between xx:11 and xx:13 of every 4th hour every day |
52
+ | 11-13 1/6 * * * * | Every minute between xx:11 and xx:13 of every 6th hour starting at 01:00 every day |
53
+ | 11-13 5 * * * * | Every minute between 05:11 and 05:13 every day |
54
+ | 11-13 5,10,15 * * * * | Every minute between 05:11 and 05:13, 10:11 and 10:13 and 15:11 and 15:13 every day |
55
+ | 10-18/2 * * * * * | Every 2 minutes between xx:10 and xx:18 of every hour every day |
56
+ | 10-18/2 8-20/3 * * * * | Every 2 minutes between xx:10 and xx:18 of every 3rd hour between 08:00 and 20:59 every day |
57
+ | 10-18/2 9-19 * * * * | Every 2 minutes between xx:10 and xx:18 of every hour between 09:00 and 19:59 every day |
58
+ | 10-18/2 */4 * * * * | Every 2 minutes between xx:10 and xx:18 of every 4th hour every day |
59
+ | 10-18/2 1/6 * * * * | Every 2 minutes between xx:10 and xx:18 of every 6th hour starting at 01:00 every day |
60
+ | 10-18/2 5 * * * * | Every 2 minutes between 05:10 and 05:18 every day |
61
+ | 10-18/2 5,10,15 * * * * | Every 2 minutes between 05:10 and 05:18, 10:10 and 10:18 and 15:10 and 15:18 every day |
@@ -0,0 +1,27 @@
1
+ require 'utility_functions'
2
+ require 'crony/parser'
3
+ Dir['./lib/crony/**/*.rb'].each {|file| require file}
4
+
5
+ module Crony
6
+ def self.parse(expression)
7
+ return 'Invalid Format' if expression.split(/\s+/).size < 5
8
+
9
+ @minute, @hour, @day, @month, @weekday, @year = expression.split(/\s+/)
10
+ return 'Invalid Format' unless valid?
11
+
12
+ time = TimePresenter.parse(@minute, @hour)
13
+ day = DayPresenter.parse(@day, @month, @weekday)
14
+ year = !!@year ? YearPresenter.parse(@year) : ''
15
+ result = "#{time} #{day} #{year}".strip.squeeze(' ').gsub(/ ,/, ',')
16
+ result.sub(/^(.)/) { $1 == 'x' ? $1 : $1.upcase}
17
+ end
18
+
19
+ def self.valid?
20
+ @minute =~ /^[\*\/,\-\d]+$/ &&
21
+ @hour =~ /^[\*\/,\-\d]+$/ &&
22
+ @day =~ /^[\*\/,\-\?LW\d]+$/ &&
23
+ @month =~ /^[\*\/,\-\djanfebmrpyjulgsoctv]+$/i &&
24
+ @weekday =~ /^[\*\/,\-\?\dsunmotewdhfriaL#]+$/i &&
25
+ (@year.nil? || @year =~ /^[\*\/,\-\d]*$/)
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'ostruct'
2
+
3
+ module Crony
4
+ class CronStruct < OpenStruct
5
+ def every?; !!every; end
6
+ def frequency?; !!frequency; end
7
+ def range?; !!start && !!stop; end
8
+ def unbounded_range?; !!start; end
9
+ def collection?; !!collection; end
10
+ def single_element?; collection? && collection.size == 1; end
11
+
12
+ def single_element; collection[0]; end
13
+
14
+ def sym
15
+ return "e" if every?
16
+ return "f" if frequency? && range?
17
+ return "u" if frequency? && unbounded_range?
18
+ return "v" if frequency?
19
+ return "r" if range?
20
+ return "s" if single_element?
21
+ return "c" if collection?
22
+ end
23
+
24
+ def format(*args)
25
+ send sym, *args
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ module Crony
2
+ module Formatters
3
+ class DayOfMonthFormatter < CronStruct
4
+ def ordinal; self.single_element.ordinal; end
5
+
6
+ def range
7
+ "the #{self.start.ordinal} to the #{self.stop.ordinal}"
8
+ end
9
+
10
+ def sym
11
+ return 'w' if !!nearest
12
+ return 'l' if !!last
13
+ super
14
+ end
15
+
16
+ def c(lead_in=true)
17
+ "#{"on the " if lead_in}#{collection.map(&:ordinal).to_sentence}"
18
+ end
19
+
20
+ def e
21
+ "on every day"
22
+ end
23
+
24
+ def f
25
+ "#{v} from #{range}"
26
+ end
27
+
28
+ def l
29
+ "on the last day"
30
+ end
31
+
32
+ def r
33
+ "on #{range}"
34
+ end
35
+
36
+ def s(lead_in=true)
37
+ "#{"on the " if lead_in}#{ordinal}"
38
+ end
39
+
40
+ def u
41
+ "#{v} starting on the #{start.ordinal}"
42
+ end
43
+
44
+ def v
45
+ "on every #{frequency.ordinal} day"
46
+ end
47
+
48
+ def w
49
+ "on the weekday closest to the #{ordinal}"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ module Crony
2
+ module Formatters
3
+ class DayOfWeekFormatter < CronStruct
4
+ DAYS = %w{ Sunday Monday Tuesday Wednesday Thursday Friday Saturday }
5
+ CRON_DAYS = %w{ sun mon tue wed thu fri sat }
6
+
7
+ def sym
8
+ return 'l' if !!last
9
+ return 'n' if !!nth_week
10
+ super
11
+ end
12
+
13
+ def get_weekday(day)
14
+ if day =~ /[a-z]/i
15
+ DAYS[CRON_DAYS.index(day.downcase)]
16
+ else
17
+ DAYS[day.to_i]
18
+ end
19
+ end
20
+
21
+ def c(weekdays=collection)
22
+ "every #{weekdays.map{|weekday| get_weekday(weekday)}.to_sentence}"
23
+ end
24
+
25
+ def e
26
+ ""
27
+ end
28
+
29
+ def l
30
+ "the last #{get_weekday(single_element)}"
31
+ end
32
+
33
+ def n
34
+ "the #{nth_week.ordinal} #{get_weekday(single_element)}"
35
+ end
36
+
37
+ def r
38
+ c(Array(start.to_i..stop.to_i))
39
+ end
40
+
41
+ def s
42
+ "every #{get_weekday(single_element)}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+ module Crony
2
+ module Formatters
3
+ class HourFormatter < CronStruct
4
+ def c
5
+ collection.map{|hour| "#{hour.two_digits}:00 and #{hour.two_digits}:59" }.to_sentence
6
+ end
7
+
8
+ def e
9
+ 'every hour'
10
+ end
11
+
12
+ def f
13
+ "#{v} between #{r}"
14
+ end
15
+
16
+ def r
17
+ "#{start.two_digits}:00 and #{stop.two_digits}:59"
18
+ end
19
+
20
+ alias :s :c
21
+
22
+ def u
23
+ "#{v} starting at #{start.two_digits}:00"
24
+ end
25
+
26
+ def v
27
+ "every #{frequency.ordinal} hour"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ module Crony
2
+ module Formatters
3
+ class MinuteFormatter < CronStruct
4
+ def c
5
+ "the #{collection.map{|minute| minute.ordinal}.to_sentence} minutes"
6
+ end
7
+
8
+ def e
9
+ 'every minute'
10
+ end
11
+
12
+ def f(hours=nil)
13
+ hours ||= ['xx']
14
+ "#{v} between #{hours.map(&:two_digits).map{|hour| "#{hour}:#{start.two_digits} and #{hour}:#{stop.two_digits}"}.to_sentence}"
15
+ end
16
+
17
+ def r(hours=nil)
18
+ hours ||= ['xx']
19
+ "every minute between #{hours.map(&:two_digits).map{|hour| "#{hour}:#{start.two_digits} and #{hour}:#{stop.two_digits}"}.to_sentence}"
20
+ end
21
+
22
+ def s
23
+ "the #{single_element.to_i == 0 ? "beginning" : "#{single_element.ordinal} minute"}"
24
+ end
25
+
26
+ def u(starting_at=true)
27
+ starting_at ? "#{v} starting at xx:#{start.two_digits}" : v
28
+ end
29
+
30
+ def v
31
+ "every #{frequency} minutes"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ module Crony
2
+ module Formatters
3
+ class MonthFormatter < CronStruct
4
+ MONTHS = %w{ January February March April May June July August September October November December }
5
+ CRON_MONTHS = %w{ jan feb mar apr may jun jul aug sep oct nov dec }
6
+
7
+ def get_month(month)
8
+ if month =~ /[a-z]/i
9
+ MONTHS[CRON_MONTHS.index(month.downcase)]
10
+ else
11
+ MONTHS[month.to_i - 1]
12
+ end
13
+ end
14
+
15
+ def range
16
+ "from #{s(start)} to #{s(stop)}"
17
+ end
18
+
19
+ def c
20
+ collection.map{|month| get_month(month)}.to_sentence
21
+ end
22
+
23
+ def e
24
+ "every month"
25
+ end
26
+
27
+ def f
28
+ "every #{frequency.ordinal} month #{range}"
29
+ end
30
+
31
+ def r
32
+ "every month #{range}"
33
+ end
34
+
35
+ def s(month=single_element)
36
+ get_month(month)
37
+ end
38
+
39
+ alias :u :f
40
+
41
+ def v
42
+ "every #{frequency.ordinal} month"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ module Crony
2
+ module Formatters
3
+ class YearFormatter < CronStruct
4
+ def c
5
+ "in #{collection.to_sentence}"
6
+ end
7
+
8
+ def e
9
+ ''
10
+ end
11
+
12
+ def f
13
+ "#{v} between #{start} and #{stop}"
14
+ end
15
+
16
+ def r
17
+ "every year between #{start} and #{stop}"
18
+ end
19
+
20
+ def s
21
+ ", #{single_element}"
22
+ end
23
+
24
+ def u
25
+ "#{v} starting in #{start}"
26
+ end
27
+
28
+ def v
29
+ "every #{frequency} years"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ module Crony
2
+ class Parser
3
+ def self.parse(element)
4
+ @element = element
5
+ case @element
6
+ when '*' then { every: true }
7
+ when /^l$/i then { last: true }
8
+ when /(\d|\w{3})l$/i
9
+ @element = @element.sub(/l$/i, '')
10
+ { last: true, collection: parse_collection }
11
+ when /.*#\d$/
12
+ @element, nth_week = @element.scan(/(.*)#(\d)$/)[0]
13
+ { nth_week: nth_week, collection: parse_collection }
14
+ when /\d+w$/i
15
+ @element = @element.sub(/w$/i, '')
16
+ { nearest: true, collection: parse_collection }
17
+ when /\//
18
+ start, frequency = @element.scan(/(.*)\/(.*)/)[0]
19
+ case start
20
+ when '*'
21
+ { frequency: frequency }
22
+ when /^\d+$/ #single number
23
+ { frequency: frequency, start: start }
24
+ when /^\d+-\d+$/
25
+ range_start, range_end = *start.scan(/(\d+)-(\d+)/)[0]
26
+ { frequency: frequency, start: range_start, stop: range_end }
27
+ end
28
+ when /^\d+-\d+$/
29
+ range_start, range_end = *@element.scan(/(\d+)-(\d+)/)[0]
30
+ { start: range_start, stop: range_end }
31
+ else { collection: parse_collection }
32
+ end
33
+ end
34
+
35
+ def self.parse_collection
36
+ parts = @element.split(',')
37
+ res = []
38
+ parts.each do |part|
39
+ if part =~ /\-/
40
+ range = part.scan(/(\d+)-(\d+)/)[0]
41
+ res += Array(range[0].to_i..range[1].to_i)
42
+ else
43
+ res << part
44
+ end
45
+ end
46
+ res
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,21 @@
1
+ module Crony
2
+ class DayPresenter
3
+ def self.parse(day, month, weekday)
4
+ day = Formatters::DayOfMonthFormatter.new(Parser.parse(day))
5
+ month = Formatters::MonthFormatter.new(Parser.parse(month))
6
+ weekday = Formatters::DayOfWeekFormatter.new(Parser.parse(weekday))
7
+
8
+ case [day, month, weekday].map(&:sym).join('')
9
+ when 'eee' then 'every day'
10
+ # 'on May 22nd,' not 'on the 22nd of May'
11
+ when /^[sc]s.$/ then "on #{month.s} #{day.format(false)} #{"and #{weekday.format} in #{month.format}" unless weekday.sym == 'e'}"
12
+ # 'on the weekday(s) closest to May 22nd,' not 'on the weekday(s) closest to the 22nd of May'
13
+ when /^w[cs].$/ then "on the weekday#{"s" if month.sym == 'c'} closest to #{month.format} #{day.s(false)} #{"and #{weekday.format} in #{month.format}" unless weekday.sym == 'e'}"
14
+ when /^u[cs]e$/ then "#{day.v} of #{month.format} starting on the #{day.start.ordinal}"
15
+ when /^e.[^e]$/ then ("on %s of %s" % [weekday.format, month.format]).squeeze(' ')
16
+ when /^..e$/ then ("%s of %s" % [day.format, month.format]).squeeze(' ')
17
+ else ("%s and %s of %s" % [day.format, weekday.format, month.format]).squeeze(' ')
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ module Crony
2
+ module TimePresenter
3
+ def self.parse(minute, hour)
4
+ minute = Formatters::MinuteFormatter.new(Parser.parse(minute))
5
+ hour = Formatters::HourFormatter.new(Parser.parse(hour))
6
+
7
+ case [minute, hour].map(&:sym).join('')
8
+ when /[ev]e/ then minute.format
9
+ when 're' then "every hour from xx:#{minute.start.two_digits} to xx:#{minute.stop.two_digits}"
10
+ when 'ss' then "#{time(hour.single_element, minute.single_element)}"
11
+ when 'sc' then "#{hour.collection.map{|hour| time(hour, minute.single_element)}.to_sentence}"
12
+ when 'us' then "#{minute.format(false)} between #{time_range(hour.single_element, minute.start, hour.single_element, 59)}"
13
+ when 'uc' then "#{minute.format(false)} between #{hour.collection.map{|hour| time_range(hour, minute.start, hour, 59)}.to_sentence}"
14
+ when /c[cs]/ then "#{hour.collection.map{|hour| minute.collection.map{|minute| time(hour, minute)}}.flatten.to_sentence}"
15
+ when /[ucsfr]r/ then ("%s of every hour between %s" % [minute.format, hour.format]).squeeze(' ')
16
+ when /^[fr][cs]$/ then "#{minute.format(hour.collection)}"
17
+ when /^.[rsc]$/ then ("%s between %s" % [minute.format, hour.format]).squeeze(' ')
18
+ else ("%s of %s" % [minute.format, hour.format]).squeeze(' ')
19
+ end
20
+ end
21
+
22
+ def self.time(hour, minute)
23
+ [hour, minute].map(&:two_digits).join(':')
24
+ end
25
+
26
+ def self.time_range(s_hour, s_minute, e_hour, e_minute)
27
+ "#{time(s_hour, s_minute)} and #{time(e_hour, e_minute)}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ module Crony
2
+ class YearPresenter
3
+ def self.parse(year)
4
+ year = Formatters::YearFormatter.new(Parser.parse(year))
5
+ year.format
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Crony
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ class Array
2
+ def to_sentence
3
+ case self.size
4
+ when 1 then self.first
5
+ when 2 then self.join(' and ')
6
+ else self[0..-2].join(', ') + " and #{self.last}"
7
+ end
8
+ end
9
+ end
10
+
11
+ class String
12
+ def two_digits
13
+ self.rjust(2, '0')
14
+ end
15
+
16
+ def ordinal; self.to_i.ordinal; end
17
+ end
18
+
19
+ class Numeric
20
+ def two_digits; self.to_s.two_digits; end
21
+
22
+ def ordinal
23
+ return "#{self}th" if self.between?(10, 20)
24
+ case self. % 10
25
+ when 1 then "#{self}st"
26
+ when 2 then "#{self}nd"
27
+ when 3 then "#{self}rd"
28
+ else "#{self}th"
29
+ end
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crony
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Berkowitz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cucumber
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: guard
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: guard-cucumber
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rb-fsevent
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Spell-checker for cron expressions
79
+ email:
80
+ - michael.berkowitz@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - Guardfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - crony.gemspec
92
+ - cucumber.yml
93
+ - features/date.feature
94
+ - features/rubycron.feature
95
+ - features/step_definitions/crony_steps.rb
96
+ - features/support/env.rb
97
+ - features/time.feature
98
+ - lib/crony.rb
99
+ - lib/crony/formatters/cron_struct.rb
100
+ - lib/crony/formatters/day_of_month_formatter.rb
101
+ - lib/crony/formatters/day_of_week_formatter.rb
102
+ - lib/crony/formatters/hour_formatter.rb
103
+ - lib/crony/formatters/minute_formatter.rb
104
+ - lib/crony/formatters/month_formatter.rb
105
+ - lib/crony/formatters/year_formatter.rb
106
+ - lib/crony/parser.rb
107
+ - lib/crony/presenters/day_presenter.rb
108
+ - lib/crony/presenters/time_presenter.rb
109
+ - lib/crony/presenters/year_presenter.rb
110
+ - lib/crony/version.rb
111
+ - lib/utility_functions.rb
112
+ homepage: ''
113
+ licenses: []
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 1.8.24
133
+ signing_key:
134
+ specification_version: 3
135
+ summary: Confirm that the cron expression you just wrote does what you meant it to
136
+ do.
137
+ test_files:
138
+ - features/date.feature
139
+ - features/rubycron.feature
140
+ - features/step_definitions/crony_steps.rb
141
+ - features/support/env.rb
142
+ - features/time.feature
143
+ has_rdoc: