whenever 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -5
- data/CHANGELOG.md +14 -0
- data/LICENSE +1 -1
- data/README.md +75 -10
- data/bin/whenever +3 -0
- data/bin/wheneverize +5 -2
- data/lib/whenever/capistrano/v2/recipes.rb +3 -2
- data/lib/whenever/capistrano/v3/tasks/whenever.rake +17 -4
- data/lib/whenever/command_line.rb +38 -18
- data/lib/whenever/cron.rb +32 -11
- data/lib/whenever/job.rb +2 -1
- data/lib/whenever/job_list.rb +50 -20
- data/lib/whenever/setup.rb +4 -0
- data/lib/whenever/version.rb +1 -1
- data/test/functional/command_line_test.rb +159 -32
- data/test/functional/output_at_test.rb +39 -0
- data/test/functional/output_jobs_with_mailto_test.rb +168 -0
- data/test/test_helper.rb +18 -8
- data/test/unit/cron_test.rb +130 -4
- data/test/unit/executable_test.rb +142 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c8fe56b21ffa57026a2b944ae208bea62a9d7e9
|
4
|
+
data.tar.gz: 543d704698ab03d944679daa1e85bee1b3936286
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d0bd440a4beab5ecde93ccb91fad7cf5b2bf2ffe14a376ad24392fe5b4a3d53ba1d7ce15a53d2debc3b639a35f609436d21ab78de16b99a857fe1e16ed8bed1
|
7
|
+
data.tar.gz: 4bb8692d2f7130e45d80fc05a6a54609fd160155014eae330da8f0fc9ed97e9338d523282cd4f8ec197531231493bd04d2409a3d7a6bbc57761ff6ad0ff7f422
|
data/.travis.yml
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
language: ruby
|
2
|
-
script: bundle exec rake
|
3
2
|
|
4
3
|
before_install:
|
5
4
|
- gem install bundler
|
@@ -7,12 +6,16 @@ before_install:
|
|
7
6
|
rvm:
|
8
7
|
- 1.9.3
|
9
8
|
- 2.0.0
|
10
|
-
- 2.1.
|
11
|
-
- 2.2.
|
12
|
-
- 2.3.
|
13
|
-
-
|
9
|
+
- 2.1.10
|
10
|
+
- 2.2.6
|
11
|
+
- 2.3.3
|
12
|
+
- 2.4.0
|
13
|
+
- jruby-9.1.9.0
|
14
14
|
|
15
15
|
gemfile:
|
16
16
|
- Gemfile
|
17
17
|
- gemfiles/activesupport4.1.gemfile
|
18
18
|
- gemfiles/activesupport4.2.gemfile
|
19
|
+
env:
|
20
|
+
global:
|
21
|
+
- JRUBY_OPTS=--debug
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
### develop
|
2
2
|
|
3
|
+
### 0.10.0 / November 19, 2017
|
4
|
+
|
5
|
+
* Modify wheneverize to allow for the creating of 'config' directory when not present
|
6
|
+
|
7
|
+
* Add --crontab-command to whenever binary for overriding the crontab command. [Martin Grandrath]
|
8
|
+
|
9
|
+
* Allow setting the path within which Capistrano will execute whenever. [Samuel Johnson](https://github.com/javan/whenever/pull/619)
|
10
|
+
|
11
|
+
* Allow the use of string literals for month and day-of-week in raw cron syntax.. [Potamianos Gregory](https://github.com/javan/whenever/pull/711)
|
12
|
+
|
13
|
+
* Include Capistrano default environment variables when executing Whenever. [Karl Li](https://github.com/javan/whenever/pull/719)
|
14
|
+
|
15
|
+
* Allow configuring an alternative schedule file in Capistrano. [Shinichi Okamoto](https://github.com/javan/whenever/pull/666)
|
16
|
+
|
3
17
|
### 0.9.7 / June 14, 2016
|
4
18
|
|
5
19
|
* Restore compatibility with Capistrano v3; it has a bug which we have to work around [Ben Langfeld, Chris Gunther, Shohei Yamasaki]
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -19,7 +19,7 @@ $ cd /apps/my-great-project
|
|
19
19
|
$ wheneverize .
|
20
20
|
```
|
21
21
|
|
22
|
-
This will create an initial `config/schedule.rb` file for you.
|
22
|
+
This will create an initial `config/schedule.rb` file for you (as long as the config folder is already present in your project).
|
23
23
|
|
24
24
|
### The `whenever` command
|
25
25
|
|
@@ -34,14 +34,21 @@ This will simply show you your `schedule.rb` file converted to cron syntax. It d
|
|
34
34
|
$ whenever --update-crontab
|
35
35
|
```
|
36
36
|
|
37
|
+
Other commonly used options include:
|
38
|
+
```sh
|
39
|
+
$ whenever --user app # set a user as which to install the crontab
|
40
|
+
$ whenever --load-file config/my_schedule.rb # set the schedule file
|
41
|
+
$ whenever --crontab-command 'sudo crontab` # override the crontab command
|
42
|
+
```
|
43
|
+
|
37
44
|
You can list installed cron jobs using `crontab -l`.
|
38
45
|
|
39
|
-
Run `whenever --help` for a complete list of options for selecting the schedule to use, setting variables in the schedule,
|
46
|
+
Run `whenever --help` for a complete list of options for selecting the schedule to use, setting variables in the schedule, etc.
|
40
47
|
|
41
48
|
### Example schedule.rb file
|
42
49
|
|
43
50
|
```ruby
|
44
|
-
every 3.hours do
|
51
|
+
every 3.hours do # 1.minute 1.day 1.week 1.month 1.year is also supported
|
45
52
|
runner "MyModel.some_process"
|
46
53
|
rake "my:rake:task"
|
47
54
|
command "/usr/bin/my_great_command"
|
@@ -113,6 +120,57 @@ Or set the job_template to nil to have your jobs execute normally.
|
|
113
120
|
set :job_template, nil
|
114
121
|
```
|
115
122
|
|
123
|
+
### Parsing dates and times
|
124
|
+
|
125
|
+
Whenever uses the [Chronic](https://github.com/mojombo/chronic) gem to parse the specified dates and times.
|
126
|
+
|
127
|
+
You can set your custom Chronic configuration if the defaults don't fit you.
|
128
|
+
|
129
|
+
For example, to assume a 24 hour clock instead of the default 12 hour clock:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
set :chronic_options, :hours24 => true
|
133
|
+
|
134
|
+
# By default this would run the job every day at 3am
|
135
|
+
every 1.day, :at => '3:00' do
|
136
|
+
runner "MyModel.nightly_archive_job"
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
You can see a list of all available options here: <https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb>
|
141
|
+
|
142
|
+
### Customize email recipient with the `MAILTO` environment variable
|
143
|
+
|
144
|
+
Output from the jobs is sent to the email address configured in the `MAILTO` environment variable.
|
145
|
+
|
146
|
+
There are many ways to further configure the recipient.
|
147
|
+
|
148
|
+
Example: A global configuration, overriding the environment's value:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
env 'MAILTO', 'output_of_cron@example.com'
|
152
|
+
|
153
|
+
every 3.hours do
|
154
|
+
command "/usr/bin/my_great_command"
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
Example: A `MAILTO` configured for all the jobs in an interval block:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
every 3.hours, mailto: 'my_super_command@example.com' do
|
162
|
+
command "/usr/bin/my_super_command"
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
Example: A `MAILTO` configured for a single job:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
every 3.hours do
|
170
|
+
command "/usr/bin/my_super_command", mailto: 'my_super_command_output@example.com'
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
116
174
|
### Capistrano integration
|
117
175
|
|
118
176
|
Use the built-in Capistrano recipe for easy crontab updates with deploys. For Capistrano V3, see the next section.
|
@@ -140,7 +198,7 @@ require "whenever/capistrano"
|
|
140
198
|
|
141
199
|
The capistrano variable `:stage` should be the one holding your environment name. This will make the correct `:environment` available in your `schedule.rb`.
|
142
200
|
|
143
|
-
If both your environments are on the same server you'll want to namespace them or they'll overwrite each other when you deploy:
|
201
|
+
If both your environments are on the same server you'll want to namespace them, or they'll overwrite each other when you deploy:
|
144
202
|
|
145
203
|
```ruby
|
146
204
|
set :whenever_environment, defer { stage }
|
@@ -148,6 +206,13 @@ set :whenever_identifier, defer { "#{application}_#{stage}" }
|
|
148
206
|
require "whenever/capistrano"
|
149
207
|
```
|
150
208
|
|
209
|
+
If you use a schedule at an alternative path, you may configure it like so:
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
set :whenever_load_file, defer { "#{release_path}/somewhere/else/schedule.rb" }
|
213
|
+
require "whenever/capistrano"
|
214
|
+
```
|
215
|
+
|
151
216
|
### Capistrano V3 Integration
|
152
217
|
|
153
218
|
In your "Capfile" file:
|
@@ -172,10 +237,10 @@ different servers in your capistrano deployment, then you can safely stop readin
|
|
172
237
|
now and everything should just work the same way it always has.
|
173
238
|
|
174
239
|
When you define a job in your schedule.rb file, by default it will be deployed to
|
175
|
-
all servers in the whenever_roles list (which defaults to [:db]).
|
240
|
+
all servers in the whenever_roles list (which defaults to `[:db]`).
|
176
241
|
|
177
242
|
However, if you want to restrict certain jobs to only run on subset of servers,
|
178
|
-
you can add a
|
243
|
+
you can add a `:roles => [...]` argument to their definitions. **Make sure to add
|
179
244
|
that role to the whenever_roles list in your deploy.rb.**
|
180
245
|
|
181
246
|
When you run `cap deploy`, jobs with a :roles list specified will only be added to
|
@@ -184,8 +249,8 @@ the crontabs on servers with one or more of the roles in that list.
|
|
184
249
|
Jobs with no :roles argument will be deployed to all servers in the whenever_roles
|
185
250
|
list. This is to maintain backward compatibility with previous releases of whenever.
|
186
251
|
|
187
|
-
So, for example, with the default whenever_roles of [:db]
|
188
|
-
deployed to all servers with the
|
252
|
+
So, for example, with the default whenever_roles of `[:db]`, a job like this would be
|
253
|
+
deployed to all servers with the `:db` role:
|
189
254
|
|
190
255
|
```ruby
|
191
256
|
every :day, :at => '12:20am' do
|
@@ -193,7 +258,7 @@ every :day, :at => '12:20am' do
|
|
193
258
|
end
|
194
259
|
```
|
195
260
|
|
196
|
-
If we set whenever_roles to [:db, :app] in deploy.rb, and have the following
|
261
|
+
If we set whenever_roles to `[:db, :app]` in deploy.rb, and have the following
|
197
262
|
jobs in schedule.rb:
|
198
263
|
|
199
264
|
```ruby
|
@@ -257,4 +322,4 @@ It's a little bit dated now, but remains a good introduction.
|
|
257
322
|
|
258
323
|
----
|
259
324
|
|
260
|
-
Copyright ©
|
325
|
+
Copyright © 2017 Javan Makhmali
|
data/bin/whenever
CHANGED
@@ -35,6 +35,9 @@ OptionParser.new do |opts|
|
|
35
35
|
opts.on('-r', '--roles [role1,role2]', 'Comma-separated list of server roles to generate cron jobs for') do |roles|
|
36
36
|
options[:roles] = roles.split(',').map(&:to_sym) if roles
|
37
37
|
end
|
38
|
+
opts.on('-x', '--crontab-command [command]', 'Default: crontab') do |crontab_command|
|
39
|
+
options[:crontab_command] = crontab_command if crontab_command
|
40
|
+
end
|
38
41
|
opts.on('-v', '--version') { puts "Whenever v#{Whenever::VERSION}"; exit(0) }
|
39
42
|
end.parse!
|
40
43
|
|
data/bin/wheneverize
CHANGED
@@ -58,9 +58,12 @@ if File.exist?(file)
|
|
58
58
|
warn "[skip] `#{file}' already exists"
|
59
59
|
elsif File.exist?(file.downcase)
|
60
60
|
warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
|
61
|
-
elsif !File.exist?(File.dirname(file))
|
62
|
-
warn "[skip] directory `#{File.dirname(file)}' does not exist"
|
63
61
|
else
|
62
|
+
dir = File.dirname(file)
|
63
|
+
if !File.exist?(dir)
|
64
|
+
warn "[add] creating `#{dir}'"
|
65
|
+
FileUtils.mkdir_p(dir)
|
66
|
+
end
|
64
67
|
puts "[add] writing `#{file}'"
|
65
68
|
File.open(file, "w") { |f| f.write(content) }
|
66
69
|
end
|
@@ -11,6 +11,7 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
11
11
|
_cset(:whenever_variables) { "environment=#{fetch :whenever_environment}" }
|
12
12
|
_cset(:whenever_update_flags) { "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
|
13
13
|
_cset(:whenever_clear_flags) { "--clear-crontab #{fetch :whenever_identifier}" }
|
14
|
+
_cset(:whenever_path) { fetch :latest_release }
|
14
15
|
|
15
16
|
namespace :whenever do
|
16
17
|
desc "Update application's crontab entries using Whenever"
|
@@ -18,7 +19,7 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
18
19
|
args = {
|
19
20
|
:command => fetch(:whenever_command),
|
20
21
|
:flags => fetch(:whenever_update_flags),
|
21
|
-
:path => fetch(:
|
22
|
+
:path => fetch(:whenever_path)
|
22
23
|
}
|
23
24
|
|
24
25
|
if whenever_servers.any?
|
@@ -38,7 +39,7 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
38
39
|
args = {
|
39
40
|
:command => fetch(:whenever_command),
|
40
41
|
:flags => fetch(:whenever_clear_flags),
|
41
|
-
:path => fetch(:
|
42
|
+
:path => fetch(:whenever_path)
|
42
43
|
}
|
43
44
|
|
44
45
|
whenever_run_commands(args)
|
@@ -4,7 +4,7 @@ namespace :whenever do
|
|
4
4
|
|
5
5
|
on roles fetch(:whenever_roles) do |host|
|
6
6
|
args_for_host = block_given? ? args + Array(yield(host)) : args
|
7
|
-
within
|
7
|
+
within fetch(:whenever_path) do
|
8
8
|
with fetch(:whenever_command_environment_variables) do
|
9
9
|
execute(*args_for_host)
|
10
10
|
end
|
@@ -12,17 +12,28 @@ namespace :whenever do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def load_file
|
16
|
+
file = fetch(:whenever_load_file)
|
17
|
+
if file
|
18
|
+
"-f #{file}"
|
19
|
+
else
|
20
|
+
''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
desc "Update application's crontab entries using Whenever"
|
16
25
|
task :update_crontab do
|
17
26
|
setup_whenever_task do |host|
|
18
27
|
roles = host.roles_array.join(",")
|
19
|
-
[fetch(:whenever_update_flags),
|
28
|
+
[fetch(:whenever_update_flags), "--roles=#{roles}", load_file]
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
23
32
|
desc "Clear application's crontab entries using Whenever"
|
24
33
|
task :clear_crontab do
|
25
|
-
setup_whenever_task
|
34
|
+
setup_whenever_task do |host|
|
35
|
+
[fetch(:whenever_clear_flags), load_file]
|
36
|
+
end
|
26
37
|
end
|
27
38
|
|
28
39
|
after "deploy:updated", "whenever:update_crontab"
|
@@ -33,11 +44,13 @@ namespace :load do
|
|
33
44
|
task :defaults do
|
34
45
|
set :whenever_roles, ->{ :db }
|
35
46
|
set :whenever_command, ->{ [:bundle, :exec, :whenever] }
|
36
|
-
set :whenever_command_environment_variables, ->{
|
47
|
+
set :whenever_command_environment_variables, ->{ fetch(:default_env).merge!(rails_env: fetch(:whenever_environment)) }
|
37
48
|
set :whenever_identifier, ->{ fetch :application }
|
38
49
|
set :whenever_environment, ->{ fetch :rails_env, fetch(:stage, "production") }
|
39
50
|
set :whenever_variables, ->{ "environment=#{fetch :whenever_environment}" }
|
51
|
+
set :whenever_load_file, ->{ nil }
|
40
52
|
set :whenever_update_flags, ->{ "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
|
41
53
|
set :whenever_clear_flags, ->{ "--clear-crontab #{fetch :whenever_identifier}" }
|
54
|
+
set :whenever_path, ->{ fetch :release_path }
|
42
55
|
end
|
43
56
|
end
|
@@ -9,9 +9,10 @@ module Whenever
|
|
9
9
|
def initialize(options={})
|
10
10
|
@options = options
|
11
11
|
|
12
|
-
@options[:
|
13
|
-
@options[:
|
14
|
-
@options[:
|
12
|
+
@options[:crontab_command] ||= 'crontab'
|
13
|
+
@options[:file] ||= 'config/schedule.rb'
|
14
|
+
@options[:cut] ||= 0
|
15
|
+
@options[:identifier] ||= default_identifier
|
15
16
|
|
16
17
|
if !File.exist?(@options[:file]) && @options[:clear].nil?
|
17
18
|
warn("[fail] Can't find file: #{@options[:file]}")
|
@@ -28,6 +29,8 @@ module Whenever
|
|
28
29
|
exit(1)
|
29
30
|
end
|
30
31
|
@options[:cut] = @options[:cut].to_i
|
32
|
+
|
33
|
+
@timestamp = Time.now.to_s
|
31
34
|
end
|
32
35
|
|
33
36
|
def run
|
@@ -55,9 +58,10 @@ module Whenever
|
|
55
58
|
end
|
56
59
|
|
57
60
|
def read_crontab
|
58
|
-
return @current_crontab if
|
61
|
+
return @current_crontab if instance_variable_defined?(:@current_crontab)
|
59
62
|
|
60
|
-
command = [
|
63
|
+
command = [@options[:crontab_command]]
|
64
|
+
command << '-l'
|
61
65
|
command << "-u #{@options[:user]}" if @options[:user]
|
62
66
|
|
63
67
|
command_results = %x[#{command.join(' ')} 2> /dev/null]
|
@@ -65,7 +69,7 @@ module Whenever
|
|
65
69
|
end
|
66
70
|
|
67
71
|
def write_crontab(contents)
|
68
|
-
command = [
|
72
|
+
command = [@options[:crontab_command]]
|
69
73
|
command << "-u #{@options[:user]}" if @options[:user]
|
70
74
|
# Solaris/SmartOS cron does not support the - option to read from stdin.
|
71
75
|
command << "-" unless OS.solaris?
|
@@ -90,19 +94,19 @@ module Whenever
|
|
90
94
|
|
91
95
|
def updated_crontab
|
92
96
|
# Check for unopened or unclosed identifier blocks
|
93
|
-
if read_crontab =~ Regexp.new("^#{
|
94
|
-
warn "[fail] Unclosed indentifier; Your crontab file contains '#{comment_open}', but no '#{comment_close}'"
|
97
|
+
if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")).nil?
|
98
|
+
warn "[fail] Unclosed indentifier; Your crontab file contains '#{comment_open(false)}', but no '#{comment_close(false)}'"
|
95
99
|
exit(1)
|
96
|
-
elsif (read_crontab =~ Regexp.new("^#{
|
97
|
-
warn "[fail] Unopened indentifier; Your crontab file contains '#{comment_close}', but no '#{comment_open}'"
|
100
|
+
elsif (read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
|
101
|
+
warn "[fail] Unopened indentifier; Your crontab file contains '#{comment_close(false)}', but no '#{comment_open(false)}'"
|
98
102
|
exit(1)
|
99
103
|
end
|
100
104
|
|
101
105
|
# If an existing identier block is found, replace it with the new cron entries
|
102
|
-
if read_crontab =~ Regexp.new("^#{
|
106
|
+
if read_crontab =~ Regexp.new("^#{comment_open_regex}\s*$") && read_crontab =~ Regexp.new("^#{comment_close_regex}\s*$")
|
103
107
|
# If the existing crontab file contains backslashes they get lost going through gsub.
|
104
108
|
# .gsub('\\', '\\\\\\') preserves them. Go figure.
|
105
|
-
read_crontab.gsub(Regexp.new("^#{
|
109
|
+
read_crontab.gsub(Regexp.new("^#{comment_open_regex}\s*$.+^#{comment_close_regex}\s*$", Regexp::MULTILINE), whenever_cron.chomp.gsub('\\', '\\\\\\'))
|
106
110
|
else # Otherwise, append the new cron entries after any existing ones
|
107
111
|
[read_crontab, whenever_cron].join("\n\n")
|
108
112
|
end.gsub(/\n{3,}/, "\n\n") # More than two newlines becomes just two.
|
@@ -120,16 +124,32 @@ module Whenever
|
|
120
124
|
stripped_contents.gsub!(/\s+$/, $/)
|
121
125
|
end
|
122
126
|
|
123
|
-
def comment_base
|
124
|
-
|
127
|
+
def comment_base(include_timestamp = true)
|
128
|
+
if include_timestamp
|
129
|
+
"Whenever generated tasks for: #{@options[:identifier]} at: #{@timestamp}"
|
130
|
+
else
|
131
|
+
"Whenever generated tasks for: #{@options[:identifier]}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def comment_open(include_timestamp = true)
|
136
|
+
"# Begin #{comment_base(include_timestamp)}"
|
137
|
+
end
|
138
|
+
|
139
|
+
def comment_close(include_timestamp = true)
|
140
|
+
"# End #{comment_base(include_timestamp)}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def comment_open_regex
|
144
|
+
"#{comment_open(false)}(#{timestamp_regex}|)"
|
125
145
|
end
|
126
146
|
|
127
|
-
def
|
128
|
-
"#
|
147
|
+
def comment_close_regex
|
148
|
+
"#{comment_close(false)}(#{timestamp_regex}|)"
|
129
149
|
end
|
130
150
|
|
131
|
-
def
|
132
|
-
"
|
151
|
+
def timestamp_regex
|
152
|
+
" at: \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} ([+-]\\d{4}|UTC)"
|
133
153
|
end
|
134
154
|
end
|
135
155
|
end
|
data/lib/whenever/cron.rb
CHANGED
@@ -3,16 +3,20 @@ require 'chronic'
|
|
3
3
|
module Whenever
|
4
4
|
module Output
|
5
5
|
class Cron
|
6
|
+
DAYS = %w(sun mon tue wed thu fri sat)
|
7
|
+
MONTHS = %w(jan feb mar apr may jun jul aug sep oct nov dec)
|
6
8
|
KEYWORDS = [:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly]
|
7
|
-
REGEX = /^(@(#{KEYWORDS.join '|'})|((\*?[\d\/,\-]*)\s
|
9
|
+
REGEX = /^(@(#{KEYWORDS.join '|'})|((\*?[\d\/,\-]*)\s){3}(\*?([\d\/,\-]|(#{MONTHS.join '|'}))*\s)(\*?([\d\/,\-]|(#{DAYS.join '|'}))*))$/i
|
8
10
|
|
9
11
|
attr_accessor :time, :task
|
10
12
|
|
11
|
-
def initialize(time = nil, task = nil, at = nil)
|
13
|
+
def initialize(time = nil, task = nil, at = nil, options = {})
|
14
|
+
chronic_options = options[:chronic_options] || {}
|
15
|
+
|
12
16
|
@at_given = at
|
13
17
|
@time = time
|
14
18
|
@task = task
|
15
|
-
@at = at.is_a?(String) ? (Chronic.parse(at) || 0) : (at || 0)
|
19
|
+
@at = at.is_a?(String) ? (Chronic.parse(at, chronic_options) || 0) : (at || 0)
|
16
20
|
end
|
17
21
|
|
18
22
|
def self.enumerate(item, detect_cron = true)
|
@@ -30,10 +34,10 @@ module Whenever
|
|
30
34
|
items
|
31
35
|
end
|
32
36
|
|
33
|
-
def self.output(times, job)
|
37
|
+
def self.output(times, job, options = {})
|
34
38
|
enumerate(times).each do |time|
|
35
39
|
enumerate(job.at, false).each do |at|
|
36
|
-
yield new(time, job.output, at).output
|
40
|
+
yield new(time, job.output, at, options).output
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
@@ -54,18 +58,18 @@ module Whenever
|
|
54
58
|
|
55
59
|
protected
|
56
60
|
def day_given?
|
57
|
-
|
58
|
-
@at_given.is_a?(String) && months.any? { |m| @at_given.downcase.index(m) }
|
61
|
+
@at_given.is_a?(String) && (MONTHS.any? { |m| @at_given.downcase.index(m) } || @at_given[/\d\/\d/])
|
59
62
|
end
|
60
63
|
|
61
64
|
def parse_symbol
|
62
65
|
shortcut = case @time
|
63
66
|
when *KEYWORDS then "@#{@time}" # :reboot => '@reboot'
|
64
|
-
when :year then Whenever.seconds(
|
67
|
+
when :year then Whenever.seconds(1, :year)
|
65
68
|
when :day then Whenever.seconds(1, :day)
|
66
69
|
when :month then Whenever.seconds(1, :month)
|
67
70
|
when :week then Whenever.seconds(1, :week)
|
68
71
|
when :hour then Whenever.seconds(1, :hour)
|
72
|
+
when :minute then Whenever.seconds(1, :minute)
|
69
73
|
end
|
70
74
|
|
71
75
|
if shortcut.is_a?(Numeric)
|
@@ -94,13 +98,15 @@ module Whenever
|
|
94
98
|
hour_frequency = (@time / 60 / 60).round
|
95
99
|
timing[0] = @at.is_a?(Time) ? @at.min : @at
|
96
100
|
timing[1] = comma_separated_timing(hour_frequency, 23)
|
101
|
+
raise ArgumentError, "Minute must be between 0-59, #{timing[0]} given" unless (0..59).include?(timing[0])
|
97
102
|
when Whenever.seconds(1, :day)...Whenever.seconds(1, :month)
|
98
103
|
day_frequency = (@time / 24 / 60 / 60).round
|
99
104
|
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
100
105
|
timing[1] = @at.is_a?(Time) ? @at.hour : @at
|
101
106
|
timing[2] = comma_separated_timing(day_frequency, 31, 1)
|
102
|
-
|
103
|
-
|
107
|
+
raise ArgumentError, "Hour must be between 0-23, #{timing[1]} given" unless (0..23).include?(timing[1])
|
108
|
+
when Whenever.seconds(1, :month)...Whenever.seconds(1, :year)
|
109
|
+
month_frequency = (@time / 30 / 24 / 60 / 60).round
|
104
110
|
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
105
111
|
timing[1] = @at.is_a?(Time) ? @at.hour : 0
|
106
112
|
timing[2] = if @at.is_a?(Time)
|
@@ -109,6 +115,21 @@ module Whenever
|
|
109
115
|
@at.zero? ? 1 : @at
|
110
116
|
end
|
111
117
|
timing[3] = comma_separated_timing(month_frequency, 12, 1)
|
118
|
+
raise ArgumentError, "Day must be between 1-31, #{timing[2]} given" unless (1..31).include?(timing[2])
|
119
|
+
when Whenever.seconds(1, :year)
|
120
|
+
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
121
|
+
timing[1] = @at.is_a?(Time) ? @at.hour : 0
|
122
|
+
timing[2] = if @at.is_a?(Time)
|
123
|
+
day_given? ? @at.day : 1
|
124
|
+
else
|
125
|
+
1
|
126
|
+
end
|
127
|
+
timing[3] = if @at.is_a?(Time)
|
128
|
+
day_given? ? @at.month : 1
|
129
|
+
else
|
130
|
+
@at.zero? ? 1 : @at
|
131
|
+
end
|
132
|
+
raise ArgumentError, "Month must be between 1-12, #{timing[3]} given" unless (1..12).include?(timing[3])
|
112
133
|
else
|
113
134
|
return parse_as_string
|
114
135
|
end
|
@@ -126,7 +147,7 @@ module Whenever
|
|
126
147
|
return (timing << '1-5') * " " if string.downcase.index('weekday')
|
127
148
|
return (timing << '6,0') * " " if string.downcase.index('weekend')
|
128
149
|
|
129
|
-
|
150
|
+
DAYS.each_with_index do |day, i|
|
130
151
|
return (timing << i) * " " if string.downcase.index(day)
|
131
152
|
end
|
132
153
|
|