whenever 0.7.0 → 0.7.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.
- data/CHANGELOG.md +15 -0
- data/lib/whenever/capistrano.rb +6 -4
- data/lib/whenever/cron.rb +16 -5
- data/lib/whenever/job.rb +13 -7
- data/lib/whenever/version.rb +1 -1
- data/lib/whenever.rb +1 -2
- data/test/unit/cron_test.rb +15 -6
- data/test/unit/job_test.rb +42 -12
- metadata +15 -15
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
### 0.7.1 / December 19th, 2011
|
2
|
+
|
3
|
+
* Require thread before active_support for compatibility with Rails < 2.3.11 and RubyGems >= 1.6.0. [Micah Geisel]
|
4
|
+
|
5
|
+
* More advanced role filtering in Cap task. [Brad Gessler]
|
6
|
+
|
7
|
+
* Added whenever_variables as a configuration variable in Cap task. [Steve Agalloco]
|
8
|
+
|
9
|
+
* Escape percent signs and reject newlines in jobs. [Amir Yalon]
|
10
|
+
|
11
|
+
* Escape paths so spaces don't trip up cron. [Javan Makhmali]
|
12
|
+
|
13
|
+
* Fix ambiguous handling of 1.month with :at. #99 [Javan Makhmali]
|
14
|
+
|
15
|
+
|
1
16
|
### 0.7.0 / September 2nd, 2011
|
2
17
|
|
3
18
|
* Use mojombo's chronic, it's active again. [Javan Makhmali]
|
data/lib/whenever/capistrano.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
Capistrano::Configuration.instance(:must_exist).load do
|
2
2
|
_cset(:whenever_roles) { :db }
|
3
|
+
_cset(:whenever_options) { {:roles => fetch(:whenever_roles)} }
|
3
4
|
_cset(:whenever_command) { "whenever" }
|
4
5
|
_cset(:whenever_identifier) { fetch :application }
|
5
6
|
_cset(:whenever_environment) { fetch :rails_env, "production" }
|
6
|
-
_cset(:
|
7
|
+
_cset(:whenever_variables) { "environment=#{fetch :whenever_environment}" }
|
8
|
+
_cset(:whenever_update_flags) { "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
|
7
9
|
_cset(:whenever_clear_flags) { "--clear-crontab #{fetch :whenever_identifier}" }
|
8
10
|
|
9
11
|
# Disable cron jobs at the begining of a deploy.
|
@@ -28,7 +30,7 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
28
30
|
which servers the crontab is updated on by setting the :whenever_roles variable.
|
29
31
|
DESC
|
30
32
|
task :update_crontab do
|
31
|
-
options =
|
33
|
+
options = fetch(:whenever_options)
|
32
34
|
|
33
35
|
if find_servers(options).any?
|
34
36
|
on_rollback do
|
@@ -55,8 +57,8 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
55
57
|
the :whenever_roles variable.
|
56
58
|
DESC
|
57
59
|
task :clear_crontab do
|
58
|
-
options =
|
59
|
-
run "cd #{fetch :
|
60
|
+
options = fetch(:whenever_options)
|
61
|
+
run "cd #{fetch :current_path} && #{fetch :whenever_command} #{fetch :whenever_clear_flags}", options if find_servers(options).any?
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
data/lib/whenever/cron.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'chronic'
|
2
|
+
|
1
3
|
module Whenever
|
2
4
|
module Output
|
3
5
|
class Cron
|
@@ -6,6 +8,7 @@ module Whenever
|
|
6
8
|
attr_accessor :time, :task
|
7
9
|
|
8
10
|
def initialize(time = nil, task = nil, at = nil)
|
11
|
+
@at_given = at
|
9
12
|
@time = time
|
10
13
|
@task = task
|
11
14
|
@at = at.is_a?(String) ? (Chronic.parse(at) || 0) : (at || 0)
|
@@ -13,7 +16,7 @@ module Whenever
|
|
13
16
|
|
14
17
|
def self.enumerate(item, detect_cron = true)
|
15
18
|
if item and item.is_a?(String)
|
16
|
-
items =
|
19
|
+
items =
|
17
20
|
if detect_cron && item =~ REGEX
|
18
21
|
[item]
|
19
22
|
else
|
@@ -33,7 +36,7 @@ module Whenever
|
|
33
36
|
end
|
34
37
|
end
|
35
38
|
end
|
36
|
-
|
39
|
+
|
37
40
|
def output
|
38
41
|
[time_in_cron_syntax, task].compact.join(' ').strip
|
39
42
|
end
|
@@ -48,12 +51,16 @@ module Whenever
|
|
48
51
|
end
|
49
52
|
|
50
53
|
protected
|
54
|
+
def day_given?
|
55
|
+
months = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]
|
56
|
+
@at_given.is_a?(String) && months.any? { |m| @at_given.downcase.index(m) }
|
57
|
+
end
|
51
58
|
|
52
59
|
def parse_symbol
|
53
60
|
shortcut = case @time
|
54
61
|
when :reboot then '@reboot'
|
55
62
|
when :year then 12.months
|
56
|
-
when :yearly,
|
63
|
+
when :yearly,
|
57
64
|
:annually then '@annually'
|
58
65
|
when :day then 1.day
|
59
66
|
when :daily then '@daily'
|
@@ -65,7 +72,7 @@ module Whenever
|
|
65
72
|
when :hour then 1.hour
|
66
73
|
when :hourly then '@hourly'
|
67
74
|
end
|
68
|
-
|
75
|
+
|
69
76
|
if shortcut.is_a?(Numeric)
|
70
77
|
@time = shortcut
|
71
78
|
parse_time
|
@@ -101,7 +108,11 @@ module Whenever
|
|
101
108
|
month_frequency = (@time / 30 / 24 / 60 / 60).round
|
102
109
|
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
103
110
|
timing[1] = @at.is_a?(Time) ? @at.hour : 0
|
104
|
-
timing[2] = @at.is_a?(Time)
|
111
|
+
timing[2] = if @at.is_a?(Time)
|
112
|
+
day_given? ? @at.day : 1
|
113
|
+
else
|
114
|
+
@at.zero? ? 1 : @at
|
115
|
+
end
|
105
116
|
timing[3] = comma_separated_timing(month_frequency, 12, 1)
|
106
117
|
else
|
107
118
|
return parse_as_string
|
data/lib/whenever/job.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
1
3
|
module Whenever
|
2
4
|
class Job
|
3
5
|
attr_reader :at
|
4
|
-
|
6
|
+
|
5
7
|
def initialize(options = {})
|
6
8
|
@options = options
|
7
9
|
@at = options.delete(:at)
|
@@ -9,16 +11,20 @@ module Whenever
|
|
9
11
|
@job_template = options.delete(:job_template) || ":job"
|
10
12
|
@options[:output] = Whenever::Output::Redirection.new(options[:output]).to_s if options.has_key?(:output)
|
11
13
|
@options[:environment] ||= :production
|
12
|
-
@options[:path]
|
14
|
+
@options[:path] = Shellwords.shellescape(@options[:path] || Whenever.path)
|
13
15
|
end
|
14
|
-
|
16
|
+
|
15
17
|
def output
|
16
18
|
job = process_template(@template, @options).strip
|
17
|
-
process_template(@job_template, { :job => job }).strip
|
19
|
+
out = process_template(@job_template, { :job => job }).strip
|
20
|
+
if out =~ /\n/
|
21
|
+
raise ArgumentError, "Task contains newline"
|
22
|
+
end
|
23
|
+
out.gsub(/%/, '\%')
|
18
24
|
end
|
19
|
-
|
25
|
+
|
20
26
|
protected
|
21
|
-
|
27
|
+
|
22
28
|
def process_template(template, options)
|
23
29
|
template.gsub(/:\w+/) do |key|
|
24
30
|
before_and_after = [$`[-1..-1], $'[0..0]]
|
@@ -37,7 +43,7 @@ module Whenever
|
|
37
43
|
def escape_single_quotes(str)
|
38
44
|
str.gsub(/'/) { "'\\''" }
|
39
45
|
end
|
40
|
-
|
46
|
+
|
41
47
|
def escape_double_quotes(str)
|
42
48
|
str.gsub(/"/) { '\"' }
|
43
49
|
end
|
data/lib/whenever/version.rb
CHANGED
data/lib/whenever.rb
CHANGED
data/test/unit/cron_test.rb
CHANGED
@@ -117,6 +117,15 @@ class CronTest < Test::Unit::TestCase
|
|
117
117
|
assert_equal '0 0 1 12 *', parse_time(12.months)
|
118
118
|
end
|
119
119
|
|
120
|
+
should "parse months with a date and/or time" do
|
121
|
+
# should set the day to 1 if no date is given
|
122
|
+
assert_equal '0 17 1 * *', parse_time(1.month, nil, "5pm")
|
123
|
+
# should use the date if one is given
|
124
|
+
assert_equal '0 2 23 * *', parse_time(1.month, nil, "February 23rd at 2am")
|
125
|
+
# should use an iteger as the day
|
126
|
+
assert_equal '0 0 5 * *', parse_time(1.month, nil, 5)
|
127
|
+
end
|
128
|
+
|
120
129
|
should "parse correctly when given an 'at' with days, hours, minutes as a Time" do
|
121
130
|
# first param is an array with [days, hours, minutes]
|
122
131
|
assert_days_and_hours_and_minutes_equals %w(1 3 45), 'January 1st 3:45am'
|
@@ -170,7 +179,7 @@ class CronTest < Test::Unit::TestCase
|
|
170
179
|
assert_equal '2 18 * * 6,0', parse_time('Weekends', nil, "6:02PM")
|
171
180
|
end
|
172
181
|
end
|
173
|
-
|
182
|
+
|
174
183
|
context "When parsing time using the cron shortcuts" do
|
175
184
|
should "parse a :symbol into the correct shortcut" do
|
176
185
|
assert_equal '@reboot', parse_time(:reboot)
|
@@ -182,7 +191,7 @@ class CronTest < Test::Unit::TestCase
|
|
182
191
|
assert_equal '@weekly', parse_time(:weekly)
|
183
192
|
assert_equal '@hourly', parse_time(:hourly)
|
184
193
|
end
|
185
|
-
|
194
|
+
|
186
195
|
should "convert time-based shortcuts to times" do
|
187
196
|
assert_equal '0 0 1 * *', parse_time(:month)
|
188
197
|
assert_equal '0 0 * * *', parse_time(:day)
|
@@ -190,22 +199,22 @@ class CronTest < Test::Unit::TestCase
|
|
190
199
|
assert_equal '0 0 1 12 *', parse_time(:year)
|
191
200
|
assert_equal '0 0 1,8,15,22 * *', parse_time(:week)
|
192
201
|
end
|
193
|
-
|
202
|
+
|
194
203
|
should "raise an exception if a valid shortcut is given but also an :at" do
|
195
204
|
assert_raises ArgumentError do
|
196
205
|
parse_time(:hourly, nil, "1:00 am")
|
197
206
|
end
|
198
|
-
|
207
|
+
|
199
208
|
assert_raises ArgumentError do
|
200
209
|
parse_time(:reboot, nil, 5)
|
201
210
|
end
|
202
|
-
|
211
|
+
|
203
212
|
assert_raises ArgumentError do
|
204
213
|
parse_time(:daily, nil, '4:20pm')
|
205
214
|
end
|
206
215
|
end
|
207
216
|
end
|
208
|
-
|
217
|
+
|
209
218
|
context "When given raw cron sytax" do
|
210
219
|
should "return the same cron sytax" do
|
211
220
|
crons = ['0 0 27-31 * *', '* * * * *', '2/3 1,9,22 11-26 1-6 *']
|
data/test/unit/job_test.rb
CHANGED
@@ -1,39 +1,69 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
2
|
|
3
3
|
class JobTest < Test::Unit::TestCase
|
4
|
-
|
4
|
+
|
5
5
|
context "A Job" do
|
6
6
|
should "return the :at set when #at is called" do
|
7
7
|
assert_equal 'foo', new_job(:at => 'foo').at
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
should "substitute the :task when #output is called" do
|
11
11
|
job = new_job(:template => ":task", :task => 'abc123')
|
12
12
|
assert_equal 'abc123', job.output
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
should "substitute the :path when #output is called" do
|
16
16
|
assert_equal 'foo', new_job(:template => ':path', :path => 'foo').output
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
should "substitute the :path with the default Whenever.path if none is provided when #output is called" do
|
20
20
|
Whenever.expects(:path).returns('/my/path')
|
21
21
|
assert_equal '/my/path', new_job(:template => ':path').output
|
22
22
|
end
|
23
|
+
|
24
|
+
should "escape the :path" do
|
25
|
+
assert_equal '/my/spacey\ path', new_job(:template => ':path', :path => '/my/spacey path').output
|
26
|
+
end
|
27
|
+
|
28
|
+
should "escape percent signs" do
|
29
|
+
job = new_job(
|
30
|
+
:template => "before :foo after",
|
31
|
+
:foo => "percent -> % <- percent"
|
32
|
+
)
|
33
|
+
assert_equal %q(before percent -> \% <- percent after), job.output
|
34
|
+
end
|
35
|
+
|
36
|
+
should "assume percent signs are not already escaped" do
|
37
|
+
job = new_job(
|
38
|
+
:template => "before :foo after",
|
39
|
+
:foo => %q(percent preceded by a backslash -> \% <-)
|
40
|
+
)
|
41
|
+
assert_equal %q(before percent preceded by a backslash -> \\\% <- after), job.output
|
42
|
+
end
|
43
|
+
|
44
|
+
should "reject newlines" do
|
45
|
+
job = new_job(
|
46
|
+
:template => "before :foo after",
|
47
|
+
:foo => "newline -> \n <- newline"
|
48
|
+
)
|
49
|
+
assert_raise ArgumentError do
|
50
|
+
job.output
|
51
|
+
end
|
52
|
+
end
|
23
53
|
end
|
24
54
|
|
25
|
-
|
55
|
+
|
26
56
|
context "A Job with quotes" do
|
27
57
|
should "output the :task if it's in single quotes" do
|
28
58
|
job = new_job(:template => "':task'", :task => 'abc123')
|
29
59
|
assert_equal %q('abc123'), job.output
|
30
60
|
end
|
31
|
-
|
61
|
+
|
32
62
|
should "output the :task if it's in double quotes" do
|
33
63
|
job = new_job(:template => '":task"', :task => 'abc123')
|
34
64
|
assert_equal %q("abc123"), job.output
|
35
65
|
end
|
36
|
-
|
66
|
+
|
37
67
|
should "output escaped single quotes in when it's wrapped in them" do
|
38
68
|
job = new_job(
|
39
69
|
:template => "before ':foo' after",
|
@@ -41,7 +71,7 @@ class JobTest < Test::Unit::TestCase
|
|
41
71
|
)
|
42
72
|
assert_equal %q(before 'quote -> '\'' <- quote' after), job.output
|
43
73
|
end
|
44
|
-
|
74
|
+
|
45
75
|
should "output escaped double quotes when it's wrapped in them" do
|
46
76
|
job = new_job(
|
47
77
|
:template => 'before ":foo" after',
|
@@ -50,18 +80,18 @@ class JobTest < Test::Unit::TestCase
|
|
50
80
|
assert_equal %q(before "quote -> \" <- quote" after), job.output
|
51
81
|
end
|
52
82
|
end
|
53
|
-
|
83
|
+
|
54
84
|
context "A Job with a job_template" do
|
55
85
|
should "use the job template" do
|
56
86
|
job = new_job(:template => ':task', :task => 'abc123', :job_template => 'left :job right')
|
57
87
|
assert_equal 'left abc123 right', job.output
|
58
88
|
end
|
59
|
-
|
89
|
+
|
60
90
|
should "escape single quotes" do
|
61
91
|
job = new_job(:template => "before ':task' after", :task => "quote -> ' <- quote", :job_template => "left ':job' right")
|
62
92
|
assert_equal %q(left 'before '\''quote -> '\\''\\'\\'''\\'' <- quote'\'' after' right), job.output
|
63
93
|
end
|
64
|
-
|
94
|
+
|
65
95
|
should "escape double quotes" do
|
66
96
|
job = new_job(:template => 'before ":task" after', :task => 'quote -> " <- quote', :job_template => 'left ":job" right')
|
67
97
|
assert_equal %q(left "before \"quote -> \\\" <- quote\" after" right), job.output
|
@@ -73,5 +103,5 @@ private
|
|
73
103
|
def new_job(options={})
|
74
104
|
Whenever::Job.new(options)
|
75
105
|
end
|
76
|
-
|
106
|
+
|
77
107
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whenever
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chronic
|
16
|
-
requirement: &
|
16
|
+
requirement: &70096166103200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.6.3
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70096166103200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70096166102060 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 2.3.4
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70096166102060
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: shoulda
|
38
|
-
requirement: &
|
38
|
+
requirement: &70096166100840 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.1.1
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70096166100840
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mocha
|
49
|
-
requirement: &
|
49
|
+
requirement: &70096166076460 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.9.5
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70096166076460
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rake
|
60
|
-
requirement: &
|
60
|
+
requirement: &70096166074280 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70096166074280
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: i18n
|
71
|
-
requirement: &
|
71
|
+
requirement: &70096166071760 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ! '>='
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: '0'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70096166071760
|
80
80
|
description: Clean ruby syntax for writing and deploying cron jobs.
|
81
81
|
email:
|
82
82
|
- javan@javan.us
|
@@ -134,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
134
|
version: '0'
|
135
135
|
requirements: []
|
136
136
|
rubyforge_project:
|
137
|
-
rubygems_version: 1.8.
|
137
|
+
rubygems_version: 1.8.10
|
138
138
|
signing_key:
|
139
139
|
specification_version: 3
|
140
140
|
summary: Cron jobs in ruby.
|