crony 0.0.1

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.
@@ -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: