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 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]
@@ -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(:whenever_update_flags) { "--update-crontab #{fetch :whenever_identifier} --set environment=#{fetch :whenever_environment}" }
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 = { :roles => fetch(:whenever_roles) }
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 = { :roles => whenever_roles }
59
- run "cd #{fetch :release_path} && #{fetch :whenever_command} #{fetch :whenever_clear_flags}", options if find_servers(options).any?
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) ? @at.day : (@at.zero? ? 1 : @at)
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] ||= Whenever.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
@@ -1,3 +1,3 @@
1
1
  module Whenever
2
- VERSION = '0.7.0'
2
+ VERSION = '0.7.1'
3
3
  end
data/lib/whenever.rb CHANGED
@@ -1,6 +1,5 @@
1
- require 'chronic'
2
- require 'active_support/all'
3
1
  require 'thread'
2
+ require 'active_support/all'
4
3
 
5
4
  module Whenever
6
5
  autoload :JobList, 'whenever/job_list'
@@ -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 *']
@@ -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.0
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-09-02 00:00:00.000000000Z
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: &70222728174660 !ruby/object:Gem::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: *70222728174660
24
+ version_requirements: *70096166103200
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70222728174020 !ruby/object:Gem::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: *70222728174020
35
+ version_requirements: *70096166102060
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: shoulda
38
- requirement: &70222728173280 !ruby/object:Gem::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: *70222728173280
46
+ version_requirements: *70096166100840
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mocha
49
- requirement: &70222728172720 !ruby/object:Gem::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: *70222728172720
57
+ version_requirements: *70096166076460
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &70222728172280 !ruby/object:Gem::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: *70222728172280
68
+ version_requirements: *70096166074280
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: i18n
71
- requirement: &70222728171580 !ruby/object:Gem::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: *70222728171580
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.6
137
+ rubygems_version: 1.8.10
138
138
  signing_key:
139
139
  specification_version: 3
140
140
  summary: Cron jobs in ruby.