whenever 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|