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