andrewtimberlake-whenever 0.1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +26 -0
- data/README.rdoc +125 -0
- data/Rakefile +13 -0
- data/bin/whenever +85 -0
- data/bin/wheneverize +69 -0
- data/lib/base.rb +15 -0
- data/lib/job_list.rb +90 -0
- data/lib/job_types/default.rb +27 -0
- data/lib/job_types/rake_task.rb +12 -0
- data/lib/job_types/runner.rb +12 -0
- data/lib/outputs/cron.rb +104 -0
- data/lib/version.rb +9 -0
- data/lib/whenever.rb +19 -0
- data/test/cron_test.rb +187 -0
- data/test/output_command_test.rb +70 -0
- data/test/output_env_test.rb +23 -0
- data/test/output_rake_test.rb +74 -0
- data/test/output_runner_test.rb +125 -0
- data/test/test_helper.rb +33 -0
- data/whenever.gemspec +39 -0
- metadata +114 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
== 0.1.5 / February 19th, 2009
|
2
|
+
|
3
|
+
* Fixed load path so Whenever's files don't conflict with anything in Rails. Thanks Ryan Koopmans. [Javan Makhmali]
|
4
|
+
|
5
|
+
|
6
|
+
== 0.1.4 / February 17th, 2009
|
7
|
+
|
8
|
+
* Added --load-file and --user opts to whenever binary. [Javan Makhmali]
|
9
|
+
|
10
|
+
|
11
|
+
== 0.1.3 / February 16th, 2009
|
12
|
+
|
13
|
+
* Added 'rake' helper for defining scheduled rake tasks. [Javan Makhmali]
|
14
|
+
|
15
|
+
* Renamed :cron_environment and :cron_path to :enviroment and :path for better (word) compatibility with rake tasks. [Javan Makhmali]
|
16
|
+
|
17
|
+
* Improved test load paths so tests can be run individually. [Javan Makhmali]
|
18
|
+
|
19
|
+
* Got rid of already initialized constant warning. [Javan Makhmali]
|
20
|
+
|
21
|
+
* Requiring specific gem versions: Chronic >=0.2.3 and activesupport >= 1.3.0 [Javan Makhmali]
|
22
|
+
|
23
|
+
|
24
|
+
== 0.1.0 / February 15th, 2009
|
25
|
+
|
26
|
+
* Initial release [Javan Makhmali]
|
data/README.rdoc
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
== Introduction
|
2
|
+
|
3
|
+
Whenever is a Ruby gem that provides a clear syntax for defining cron jobs. It outputs valid cron syntax and can even write your crontab file for you. It is designed to work well with Rails applications and can be deployed with Capistrano. Whenever works fine independently as well.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
Regular (non-Rails) install:
|
8
|
+
|
9
|
+
$ gem sources -a http://gems.github.com #you only need to run this once
|
10
|
+
$ sudo gem install javan-whenever
|
11
|
+
|
12
|
+
In a Rails (2.1 or greater) application:
|
13
|
+
|
14
|
+
in your "config/environment.rb" file:
|
15
|
+
|
16
|
+
Rails::Initializer.run do |config|
|
17
|
+
config.gem 'javan-whenever', :lib => false, :version => '>= 0.1.5' :source => 'http://gems.github.com'
|
18
|
+
end
|
19
|
+
|
20
|
+
To install this gem (and all other missing gem dependencies), run rake gems:install (use sudo if necessary).
|
21
|
+
|
22
|
+
In older versions of Rails:
|
23
|
+
|
24
|
+
$ gem sources -a http://gems.github.com #you only need to run this once
|
25
|
+
$ gem install javan-whenever
|
26
|
+
|
27
|
+
in your "config/environment.rb" file:
|
28
|
+
|
29
|
+
Rails::Initializer.run do |config|
|
30
|
+
...
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'whenever'
|
34
|
+
|
35
|
+
NOTE: Requiring the whenever gem inside your Rails application is technically optional. However, if you plan to use something like Capistrano to automatically deploy and write your crontab file, you'll need to have the gem installed on your servers, and requiring it in your app is one way to ensure this.
|
36
|
+
|
37
|
+
== Getting started
|
38
|
+
|
39
|
+
$ cd /my/rails/app
|
40
|
+
$ wheneverize .
|
41
|
+
|
42
|
+
This will create an initial "config/schedule.rb" file you.
|
43
|
+
|
44
|
+
== Example schedule.rb file
|
45
|
+
|
46
|
+
every 3.hours do
|
47
|
+
runner "MyModel.some_process"
|
48
|
+
rake "my:rake:task"
|
49
|
+
command "/usr/bin/my_great_command"
|
50
|
+
end
|
51
|
+
|
52
|
+
every 1.day, :at => '4:30 am' do
|
53
|
+
runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
|
54
|
+
end
|
55
|
+
|
56
|
+
every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
|
57
|
+
runner "SomeModel.ladeeda"
|
58
|
+
end
|
59
|
+
|
60
|
+
every :sunday do # Use any day of the week or :weekend, :weekday
|
61
|
+
runner "Task.do_something_great"
|
62
|
+
end
|
63
|
+
|
64
|
+
More examples on the wiki: http://wiki.github.com/javan/whenever/instructions-and-examples
|
65
|
+
|
66
|
+
== Cron output
|
67
|
+
|
68
|
+
$ cd /my/rails/app
|
69
|
+
$ whenever
|
70
|
+
|
71
|
+
And you'll see your schedule.rb converted to cron sytax
|
72
|
+
|
73
|
+
== Capistrano integration
|
74
|
+
|
75
|
+
in your "config/deploy.rb" file do something like:
|
76
|
+
|
77
|
+
after "deploy:symlink", "deploy:write_crontab"
|
78
|
+
|
79
|
+
namespace :deploy do
|
80
|
+
desc "write the crontab file"
|
81
|
+
task :write_crontab, :roles => :app do
|
82
|
+
run "cd #{release_path} && whenever --write-crontab"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
By mixing and matching the "--load-file" and "--user" options with your various :roles in Capistrano it is entirely possible to deploy different crontab schedules under different users to all your various servers. Get creative!
|
87
|
+
|
88
|
+
USING THE "--write-crontab" OPTION WILL COMPLETELY OVERWRITE ANY EXISTING CRONTAB ENTRIES!
|
89
|
+
|
90
|
+
== Credit
|
91
|
+
|
92
|
+
Whenever was created for use at Inkling (http://inklingmarkets.com) where I work. Their take on it: http://blog.inklingmarkets.com/2009/02/whenever-easy-way-to-do-cron-jobs-from.html
|
93
|
+
|
94
|
+
While building Whenever, I learned a lot by digging through the source code of Capistrano - http://github.com/jamis/capistrano
|
95
|
+
|
96
|
+
== Feedback
|
97
|
+
|
98
|
+
Lighthouse: http://javan.lighthouseapp.com/projects/25781-whenever/overview
|
99
|
+
|
100
|
+
Email me: javan [at] javan (dot) us
|
101
|
+
|
102
|
+
== License
|
103
|
+
|
104
|
+
Copyright (c) 2009 Javan Makhmali
|
105
|
+
|
106
|
+
Permission is hereby granted, free of charge, to any person
|
107
|
+
obtaining a copy of this software and associated documentation
|
108
|
+
files (the "Software"), to deal in the Software without
|
109
|
+
restriction, including without limitation the rights to use,
|
110
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
111
|
+
copies of the Software, and to permit persons to whom the
|
112
|
+
Software is furnished to do so, subject to the following
|
113
|
+
conditions:
|
114
|
+
|
115
|
+
The above copyright notice and this permission notice shall be
|
116
|
+
included in all copies or substantial portions of the Software.
|
117
|
+
|
118
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
119
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
120
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
121
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
122
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
123
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
124
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
125
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
require File.expand_path(File.dirname(__FILE__) + "/lib/version")
|
6
|
+
|
7
|
+
Echoe.new('whenever', Whenever::VERSION::STRING) do |p|
|
8
|
+
p.description = "Provides clean ruby syntax for defining messy cron jobs and running them Whenever."
|
9
|
+
p.url = "http://github.com/javan/whenever"
|
10
|
+
p.author = "Javan Makhmali"
|
11
|
+
p.email = "javan@javan.us"
|
12
|
+
p.dependencies = ["chronic >=0.2.3", "activesupport >=1.3.0"]
|
13
|
+
end
|
data/bin/whenever
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << "/home/andrew/dev/whenever/lib"
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'optparse'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'tempfile'
|
9
|
+
require 'whenever'
|
10
|
+
|
11
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/version")
|
12
|
+
|
13
|
+
options = Hash.new
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: whenever [options]"
|
17
|
+
opts.on('-v', '--version') { puts "Whenever v#{Whenever::VERSION::STRING}"; exit }
|
18
|
+
opts.on('-w', '--write-crontab') { options[:write] = true }
|
19
|
+
opts.on('-f', '--load-file [schedule file]', 'default: config/schedule.rb') do |file|
|
20
|
+
options[:file] = file if file
|
21
|
+
end
|
22
|
+
opts.on('-u', '--user [user]', 'default: current user') do |user|
|
23
|
+
options[:user] = user if user
|
24
|
+
end
|
25
|
+
end.parse!
|
26
|
+
|
27
|
+
options[:file] ||= 'config/schedule.rb'
|
28
|
+
|
29
|
+
unless File.exists?(options[:file])
|
30
|
+
warn("[fail] can't find file: #{options[:file]}")
|
31
|
+
exit(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
if options[:write]
|
35
|
+
cron_output = Whenever.cron(:file => options[:file])
|
36
|
+
file_path = File.expand_path(options[:file])
|
37
|
+
|
38
|
+
#Get existing crontab
|
39
|
+
command = ['crontab']
|
40
|
+
command << "-u #{options[:user]}" if options[:user]
|
41
|
+
command << "-l"
|
42
|
+
|
43
|
+
existing_crontab = `#{command.join(' ')}`
|
44
|
+
if $?.exitstatus == 0
|
45
|
+
lines = existing_crontab.split(/\n/)
|
46
|
+
kept_lines = []
|
47
|
+
keep = true
|
48
|
+
lines.each do |line|
|
49
|
+
keep = false if line =~ /#BEGIN.*#{Regexp.escape(file_path)}/
|
50
|
+
|
51
|
+
kept_lines << line if keep
|
52
|
+
|
53
|
+
keep = true if line =~ /#END.*#{Regexp.escape(file_path)}/
|
54
|
+
end
|
55
|
+
else
|
56
|
+
warn "[fail] couldn't read existing crontab"
|
57
|
+
exit(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
tmp_cron_file = Tempfile.new('whenever_tmp_cron').path
|
61
|
+
File.open(tmp_cron_file, File::WRONLY | File::APPEND) do |file|
|
62
|
+
kept_lines.each do |line|
|
63
|
+
file.puts line
|
64
|
+
end
|
65
|
+
file << "#BEGIN: Crontab entry generated from: #{file_path}\n"
|
66
|
+
file << cron_output
|
67
|
+
file << "#END: Crontab entry generated from: #{file_path}\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
command = ['crontab']
|
71
|
+
command << "-u #{options[:user]}" if options[:user]
|
72
|
+
command << tmp_cron_file
|
73
|
+
|
74
|
+
if system(command.join(' '))
|
75
|
+
puts "[write] crontab file updated"
|
76
|
+
exit
|
77
|
+
else
|
78
|
+
warn "[fail] couldn't write crontab"
|
79
|
+
exit(1)
|
80
|
+
end
|
81
|
+
|
82
|
+
else
|
83
|
+
puts Whenever.cron(:file => options[:file])
|
84
|
+
exit
|
85
|
+
end
|
data/bin/wheneverize
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This file is based heavily on Capistrano's `capify` command
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: #{File.basename($0)} [path]"
|
10
|
+
|
11
|
+
begin
|
12
|
+
opts.parse!(ARGV)
|
13
|
+
rescue OptionParser::ParseError => e
|
14
|
+
warn e.message
|
15
|
+
puts opts
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if ARGV.empty?
|
21
|
+
abort "Please specify the directory to wheneverize, e.g. `#{File.basename($0)} .'"
|
22
|
+
elsif !File.exists?(ARGV.first)
|
23
|
+
abort "`#{ARGV.first}' does not exist."
|
24
|
+
elsif !File.directory?(ARGV.first)
|
25
|
+
abort "`#{ARGV.first}' is not a directory."
|
26
|
+
elsif ARGV.length > 1
|
27
|
+
abort "Too many arguments; please specify only the directory to wheneverize."
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
content = <<-FILE
|
32
|
+
# Use this file to easily define all of your cron jobs.
|
33
|
+
#
|
34
|
+
# It's helpful, but not entirely necessary to understand cron before proceeding.
|
35
|
+
# http://en.wikipedia.org/wiki/Cron
|
36
|
+
|
37
|
+
# Example:
|
38
|
+
#
|
39
|
+
# set :cron_log, "/path/to/my/cron_log.log"
|
40
|
+
#
|
41
|
+
# every 2.hours do
|
42
|
+
# command "/usr/bin/some_great_command"
|
43
|
+
# runner "MyModel.some_method"
|
44
|
+
# rake "some:great:rake:task"
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# every 4.days do
|
48
|
+
# runner "AnotherModel.prune_old_records"
|
49
|
+
# end
|
50
|
+
|
51
|
+
# Learn more: http://github.com/javan/whenever
|
52
|
+
FILE
|
53
|
+
|
54
|
+
file = 'config/schedule.rb'
|
55
|
+
base = ARGV.shift
|
56
|
+
|
57
|
+
file = File.join(base, file)
|
58
|
+
if File.exists?(file)
|
59
|
+
warn "[skip] `#{file}' already exists"
|
60
|
+
elsif File.exists?(file.downcase)
|
61
|
+
warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
|
62
|
+
elsif !File.exists?(File.dirname(file))
|
63
|
+
warn "[skip] directory `#{File.dirname(file)}' does not exist"
|
64
|
+
else
|
65
|
+
puts "[add] writing `#{file}'"
|
66
|
+
File.open(file, "w") { |f| f.write(content) }
|
67
|
+
end
|
68
|
+
|
69
|
+
puts "[done] wheneverized!"
|
data/lib/base.rb
ADDED
data/lib/job_list.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Whenever
|
2
|
+
class JobList
|
3
|
+
|
4
|
+
def initialize(options)
|
5
|
+
@jobs = Hash.new
|
6
|
+
@env = Hash.new
|
7
|
+
|
8
|
+
config = case options
|
9
|
+
when String then options
|
10
|
+
when Hash
|
11
|
+
if options[:string]
|
12
|
+
options[:string]
|
13
|
+
elsif options[:file]
|
14
|
+
File.read(options[:file])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
eval(config)
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(variable, value)
|
22
|
+
instance_variable_set("@#{variable}".to_sym, value)
|
23
|
+
self.class.send(:attr_reader, variable.to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
def env(variable, value)
|
27
|
+
@env[variable.to_s] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def every(frequency, options = {})
|
31
|
+
@current_time_scope = frequency
|
32
|
+
@options = options
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
|
36
|
+
def command(task, options = {})
|
37
|
+
options[:cron_log] ||= @cron_log unless options[:cron_log] === false
|
38
|
+
options[:class] ||= Whenever::Job::Default
|
39
|
+
@jobs[@current_time_scope] ||= []
|
40
|
+
@jobs[@current_time_scope] << options[:class].new(@options.merge(:task => task).merge(options))
|
41
|
+
end
|
42
|
+
|
43
|
+
def runner(task, options = {})
|
44
|
+
options.reverse_merge!(:environment => @environment, :path => @path)
|
45
|
+
options[:class] = Whenever::Job::Runner
|
46
|
+
command(task, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
def rake(task, options = {})
|
50
|
+
options.reverse_merge!(:environment => @environment, :path => @path)
|
51
|
+
options[:class] = Whenever::Job::RakeTask
|
52
|
+
command(task, options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_cron_output
|
56
|
+
[environment_variables, cron_jobs].compact.join
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def environment_variables
|
62
|
+
return if @env.empty?
|
63
|
+
|
64
|
+
output = []
|
65
|
+
@env.each do |key, val|
|
66
|
+
output << "#{key}=#{val}\n"
|
67
|
+
end
|
68
|
+
output << "\n"
|
69
|
+
|
70
|
+
output.join
|
71
|
+
end
|
72
|
+
|
73
|
+
def cron_jobs
|
74
|
+
return if @jobs.empty?
|
75
|
+
|
76
|
+
output = []
|
77
|
+
@jobs.each do |time, jobs|
|
78
|
+
jobs.each do |job|
|
79
|
+
cron = Whenever::Output::Cron.output(time, job)
|
80
|
+
cron << " >> #{job.cron_log} 2>&1" if job.cron_log
|
81
|
+
cron << "\n\n"
|
82
|
+
output << cron
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
output.join
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Whenever
|
2
|
+
module Job
|
3
|
+
class Default
|
4
|
+
|
5
|
+
attr_accessor :task, :at, :cron_log
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@task = options[:task]
|
9
|
+
@at = options[:at]
|
10
|
+
@cron_log = options[:cron_log]
|
11
|
+
@environment = options[:environment] || :production
|
12
|
+
@path = options[:path] || Whenever.path
|
13
|
+
end
|
14
|
+
|
15
|
+
def output
|
16
|
+
task
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def path_required
|
22
|
+
raise ArgumentError, "No path available; set :path, '/your/path' in your schedule file" if @path.blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/outputs/cron.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Whenever
|
2
|
+
module Output
|
3
|
+
|
4
|
+
class Cron
|
5
|
+
|
6
|
+
attr_accessor :time, :task
|
7
|
+
|
8
|
+
def initialize(time = nil, task = nil, at = nil)
|
9
|
+
@time = time
|
10
|
+
@task = task
|
11
|
+
@at = at.is_a?(String) ? (Chronic.parse(at) || 0) : (at || 0)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.output(time, job)
|
15
|
+
out = new(time, job.output, job.at)
|
16
|
+
"#{out.time_in_cron_syntax} #{out.task}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def time_in_cron_syntax
|
20
|
+
case @time
|
21
|
+
when Symbol then parse_symbol
|
22
|
+
when String then parse_as_string
|
23
|
+
else parse_time
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def parse_symbol
|
30
|
+
case @time
|
31
|
+
when :reboot then '@reboot'
|
32
|
+
when :year, :yearly then '@annually'
|
33
|
+
when :day, :daily then '@daily'
|
34
|
+
when :midnight then '@midnight'
|
35
|
+
when :month, :monthly then '@monthly'
|
36
|
+
when :week, :weekly then '@weekly'
|
37
|
+
when :hour, :hourly then '@hourly'
|
38
|
+
else parse_as_string
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_time
|
43
|
+
timing = Array.new(5, '*')
|
44
|
+
case @time
|
45
|
+
when 0.seconds...1.minute
|
46
|
+
raise ArgumentError, "Time must be in minutes or higher"
|
47
|
+
when 1.minute...1.hour
|
48
|
+
minute_frequency = @time / 60
|
49
|
+
timing[0] = comma_separated_timing(minute_frequency, 59)
|
50
|
+
when 1.hour...1.day
|
51
|
+
hour_frequency = (@time / 60 / 60).round
|
52
|
+
timing[0] = @at.is_a?(Time) ? @at.min : @at
|
53
|
+
timing[1] = comma_separated_timing(hour_frequency, 23)
|
54
|
+
when 1.day...1.month
|
55
|
+
day_frequency = (@time / 24 / 60 / 60).round
|
56
|
+
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
57
|
+
timing[1] = @at.is_a?(Time) ? @at.hour : @at
|
58
|
+
timing[2] = comma_separated_timing(day_frequency, 31, 1)
|
59
|
+
when 1.month..12.months
|
60
|
+
month_frequency = (@time / 30 / 24 / 60 / 60).round
|
61
|
+
timing[0] = @at.is_a?(Time) ? @at.min : 0
|
62
|
+
timing[1] = @at.is_a?(Time) ? @at.hour : 0
|
63
|
+
timing[2] = @at.is_a?(Time) ? @at.day : (@at.zero? ? 1 : @at)
|
64
|
+
timing[3] = comma_separated_timing(month_frequency, 12, 1)
|
65
|
+
else
|
66
|
+
return parse_as_string
|
67
|
+
end
|
68
|
+
timing.join(' ')
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_as_string
|
72
|
+
return unless @time
|
73
|
+
string = @time.to_s
|
74
|
+
|
75
|
+
return "0 0 * * mon-fri" if string.downcase.index('weekday')
|
76
|
+
return "0 0 * * sat,sun" if string.downcase.index('weekend')
|
77
|
+
|
78
|
+
%w(sun mon tue wed thu fri sat).each do |day|
|
79
|
+
return "0 0 * * #{day}" if string.downcase.index(day)
|
80
|
+
end
|
81
|
+
|
82
|
+
raise ArgumentError, "Couldn't parse: #{@time}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def comma_separated_timing(frequency, max, start = 0)
|
86
|
+
return start if frequency.blank? || frequency.zero?
|
87
|
+
return '*' if frequency == 1
|
88
|
+
return frequency if frequency > (max * 0.5).ceil
|
89
|
+
|
90
|
+
original_start = start
|
91
|
+
|
92
|
+
start += frequency unless (max + 1).modulo(frequency).zero? || start > 0
|
93
|
+
output = (start..max).step(frequency).to_a
|
94
|
+
|
95
|
+
max_occurances = (max.to_f / (frequency.to_f)).round
|
96
|
+
max_occurances += 1 if original_start.zero?
|
97
|
+
|
98
|
+
output[0, max_occurances].join(',')
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
data/lib/version.rb
ADDED
data/lib/whenever.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
unless defined?(Whenever)
|
2
|
+
$:.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
# Hoping to load Rails' Rakefile
|
5
|
+
begin
|
6
|
+
load 'Rakefile'
|
7
|
+
rescue LoadError => e
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Dependencies
|
13
|
+
require 'activesupport'
|
14
|
+
require 'chronic'
|
15
|
+
|
16
|
+
# Whenever files
|
17
|
+
%w{ base version job_list job_types/default job_types/rake_task job_types/runner outputs/cron }.each do |file|
|
18
|
+
require File.expand_path(File.dirname(__FILE__) + "/#{file}")
|
19
|
+
end
|
data/test/cron_test.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class CronTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "When parsing time in minutes" do
|
6
|
+
should "raise if less than 1 minute" do
|
7
|
+
assert_raises ArgumentError do
|
8
|
+
parse_time(59.seconds)
|
9
|
+
end
|
10
|
+
|
11
|
+
assert_raises ArgumentError do
|
12
|
+
parse_time(0.minutes)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# For santity, do some tests on straight String
|
17
|
+
should "parse correctly" do
|
18
|
+
assert_equal '* * * * *', parse_time(1.minute)
|
19
|
+
assert_equal '0,5,10,15,20,25,30,35,40,45,50,55 * * * *', parse_time(5.minutes)
|
20
|
+
assert_equal '7,14,21,28,35,42,49,56 * * * *', parse_time(7.minutes)
|
21
|
+
assert_equal '0,30 * * * *', parse_time(30.minutes)
|
22
|
+
assert_equal '32 * * * *', parse_time(32.minutes)
|
23
|
+
assert_not_equal '60 * * * *', parse_time(60.minutes) # 60 minutes bumps up into the hour range
|
24
|
+
end
|
25
|
+
|
26
|
+
# Test all minutes
|
27
|
+
(2..59).each do |num|
|
28
|
+
should "parse correctly for #{num} minutes" do
|
29
|
+
start = 0
|
30
|
+
start += num unless 60.modulo(num).zero?
|
31
|
+
minutes = (start..59).step(num).to_a
|
32
|
+
|
33
|
+
assert_equal "#{minutes.join(',')} * * * *", parse_time(num.minutes)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "When parsing time in hours" do
|
39
|
+
should "parse correctly" do
|
40
|
+
assert_equal '0 * * * *', parse_time(1.hour)
|
41
|
+
assert_equal '0 0,2,4,6,8,10,12,14,16,18,20,22 * * *', parse_time(2.hours)
|
42
|
+
assert_equal '0 0,3,6,9,12,15,18,21 * * *', parse_time(3.hours)
|
43
|
+
assert_equal '0 5,10,15,20 * * *', parse_time(5.hours)
|
44
|
+
assert_equal '0 17 * * *', parse_time(17.hours)
|
45
|
+
assert_not_equal '0 24 * * *', parse_time(24.hours) # 24 hours bumps up into the day range
|
46
|
+
end
|
47
|
+
|
48
|
+
(2..23).each do |num|
|
49
|
+
should "parse correctly for #{num} hours" do
|
50
|
+
start = 0
|
51
|
+
start += num unless 24.modulo(num).zero?
|
52
|
+
hours = (start..23).step(num).to_a
|
53
|
+
|
54
|
+
assert_equal "0 #{hours.join(',')} * * *", parse_time(num.hours)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
should "parse correctly when given an 'at' with minutes as an Integer" do
|
59
|
+
assert_minutes_equals "1", 1
|
60
|
+
assert_minutes_equals "14", 14
|
61
|
+
assert_minutes_equals "27", 27
|
62
|
+
assert_minutes_equals "55", 55
|
63
|
+
end
|
64
|
+
|
65
|
+
should "parse correctly when given an 'at' with minutes as a Time" do
|
66
|
+
# Basically just testing that Chronic parses some times and we get the minutes out of it
|
67
|
+
assert_minutes_equals "1", '3:01am'
|
68
|
+
assert_minutes_equals "1", 'January 21 2:01 PM'
|
69
|
+
assert_minutes_equals "0", 'midnight'
|
70
|
+
assert_minutes_equals "59", '13:59'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "When parsing time in days (of month)" do
|
75
|
+
should "parse correctly" do
|
76
|
+
assert_equal '0 0 * * *', parse_time(1.days)
|
77
|
+
assert_equal '0 0 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31 * *', parse_time(2.days)
|
78
|
+
assert_equal '0 0 1,5,9,13,17,21,25,29 * *', parse_time(4.days)
|
79
|
+
assert_equal '0 0 1,8,15,22 * *', parse_time(7.days)
|
80
|
+
assert_equal '0 0 1,17 * *', parse_time(16.days)
|
81
|
+
assert_equal '0 0 17 * *', parse_time(17.days)
|
82
|
+
assert_equal '0 0 29 * *', parse_time(29.days)
|
83
|
+
assert_not_equal '0 0 30 * *', parse_time(30.days) # 30 days bumps into the month range
|
84
|
+
end
|
85
|
+
|
86
|
+
should "parse correctly when given an 'at' with hours, minutes as a Time" do
|
87
|
+
# first param is an array with [hours, minutes]
|
88
|
+
assert_hours_and_minutes_equals %w(3 45), '3:45am'
|
89
|
+
assert_hours_and_minutes_equals %w(20 1), '8:01pm'
|
90
|
+
assert_hours_and_minutes_equals %w(0 0), 'midnight'
|
91
|
+
assert_hours_and_minutes_equals %w(1 23), '1:23 AM'
|
92
|
+
assert_hours_and_minutes_equals %w(23 59), 'March 21 11:59 pM'
|
93
|
+
end
|
94
|
+
|
95
|
+
should "parse correctly when given an 'at' with hours as an Integer" do
|
96
|
+
# first param is an array with [hours, minutes]
|
97
|
+
assert_hours_and_minutes_equals %w(1 0), 1
|
98
|
+
assert_hours_and_minutes_equals %w(3 0), 3
|
99
|
+
assert_hours_and_minutes_equals %w(15 0), 15
|
100
|
+
assert_hours_and_minutes_equals %w(19 0), 19
|
101
|
+
assert_hours_and_minutes_equals %w(23 0), 23
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "When parsing time in months" do
|
106
|
+
should "parse correctly" do
|
107
|
+
assert_equal '0 0 1 * *', parse_time(1.month)
|
108
|
+
assert_equal '0 0 1 1,3,5,7,9,11 *', parse_time(2.months)
|
109
|
+
assert_equal '0 0 1 1,4,7,10 *', parse_time(3.months)
|
110
|
+
assert_equal '0 0 1 1,5,9 *', parse_time(4.months)
|
111
|
+
assert_equal '0 0 1 1,6 *', parse_time(5.months)
|
112
|
+
assert_equal '0 0 1 7 *', parse_time(7.months)
|
113
|
+
assert_equal '0 0 1 8 *', parse_time(8.months)
|
114
|
+
assert_equal '0 0 1 9 *', parse_time(9.months)
|
115
|
+
assert_equal '0 0 1 10 *', parse_time(10.months)
|
116
|
+
assert_equal '0 0 1 11 *', parse_time(11.months)
|
117
|
+
assert_equal '0 0 1 12 *', parse_time(12.months)
|
118
|
+
end
|
119
|
+
|
120
|
+
should "parse correctly when given an 'at' with days, hours, minutes as a Time" do
|
121
|
+
# first param is an array with [days, hours, minutes]
|
122
|
+
assert_days_and_hours_and_minutes_equals %w(1 3 45), 'January 1st 3:45am'
|
123
|
+
assert_days_and_hours_and_minutes_equals %w(11 23 0), 'Feb 11 11PM'
|
124
|
+
assert_days_and_hours_and_minutes_equals %w(22 1 1), 'march 22nd at 1:01 am'
|
125
|
+
assert_days_and_hours_and_minutes_equals %w(23 0 0), 'march 22nd at midnight' # looks like midnight means the next day
|
126
|
+
end
|
127
|
+
|
128
|
+
should "parse correctly when given an 'at' with days as an Integer" do
|
129
|
+
# first param is an array with [days, hours, minutes]
|
130
|
+
assert_days_and_hours_and_minutes_equals %w(1 0 0), 1
|
131
|
+
assert_days_and_hours_and_minutes_equals %w(15 0 0), 15
|
132
|
+
assert_days_and_hours_and_minutes_equals %w(29 0 0), 29
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "When parsing time in days (of week)" do
|
137
|
+
should "parse days of the week correctly" do
|
138
|
+
{
|
139
|
+
'sun' => %w(sun Sunday SUNDAY SUN),
|
140
|
+
'mon' => %w(mon Monday MONDAY MON),
|
141
|
+
'tue' => %w(tue tues Tuesday TUESDAY TUE),
|
142
|
+
'wed' => %w(wed Wednesday WEDNESDAY WED),
|
143
|
+
'thu' => %w(thu thurs thur Thursday THURSDAY THU),
|
144
|
+
'fri' => %w(fri Friday FRIDAY FRI),
|
145
|
+
'sat' => %w(sat Saturday SATURDAY SAT)
|
146
|
+
}.each do |day, day_tests|
|
147
|
+
day_tests.each do |day_test|
|
148
|
+
assert_equal "0 0 * * #{day}", parse_time(day_test)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
should "parse weekday correctly" do
|
154
|
+
assert_equal '0 0 * * mon-fri', parse_time('weekday')
|
155
|
+
assert_equal '0 0 * * mon-fri', parse_time('Weekdays')
|
156
|
+
end
|
157
|
+
|
158
|
+
should "parse weekend correctly" do
|
159
|
+
assert_equal '0 0 * * sat,sun', parse_time('weekend')
|
160
|
+
assert_equal '0 0 * * sat,sun', parse_time('Weekends')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def assert_days_and_hours_and_minutes_equals(expected, time)
|
167
|
+
cron = parse_time(2.months, 'some task', time)
|
168
|
+
minutes, hours, days, *garbage = cron.split(' ')
|
169
|
+
assert_equal expected, [days, hours, minutes]
|
170
|
+
end
|
171
|
+
|
172
|
+
def assert_hours_and_minutes_equals(expected, time)
|
173
|
+
cron = parse_time(2.days, 'some task', time)
|
174
|
+
minutes, hours, *garbage = cron.split(' ')
|
175
|
+
assert_equal expected, [hours, minutes]
|
176
|
+
end
|
177
|
+
|
178
|
+
def assert_minutes_equals(expected, time)
|
179
|
+
cron = parse_time(2.hours, 'some task', time)
|
180
|
+
assert_equal expected, cron.split(' ')[0]
|
181
|
+
end
|
182
|
+
|
183
|
+
def parse_time(time = nil, task = nil, at = nil)
|
184
|
+
Whenever::Output::Cron.new(time, task, at).time_in_cron_syntax
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class OutputCommandTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "A plain command" do
|
6
|
+
setup do
|
7
|
+
@output = Whenever.cron \
|
8
|
+
<<-file
|
9
|
+
every 2.hours do
|
10
|
+
command "blahblah"
|
11
|
+
end
|
12
|
+
file
|
13
|
+
end
|
14
|
+
|
15
|
+
should "output the command" do
|
16
|
+
assert_match /^.+ .+ .+ .+ blahblah$/, @output
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "A command when the cron_log is set" do
|
21
|
+
setup do
|
22
|
+
@output = Whenever.cron \
|
23
|
+
<<-file
|
24
|
+
set :cron_log, 'logfile.log'
|
25
|
+
every 2.hours do
|
26
|
+
command "blahblah"
|
27
|
+
end
|
28
|
+
file
|
29
|
+
end
|
30
|
+
|
31
|
+
should "output the command with the log syntax appended" do
|
32
|
+
assert_match /^.+ .+ .+ .+ blahblah >> logfile.log 2>&1$/, @output
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "A command when the cron_log is set and the comand overrides it" do
|
37
|
+
setup do
|
38
|
+
@output = Whenever.cron \
|
39
|
+
<<-file
|
40
|
+
set :cron_log, 'logfile.log'
|
41
|
+
every 2.hours do
|
42
|
+
command "blahblah", :cron_log => 'otherlog.log'
|
43
|
+
end
|
44
|
+
file
|
45
|
+
end
|
46
|
+
|
47
|
+
should "output the command with the log syntax appended" do
|
48
|
+
assert_no_match /.+ .+ .+ .+ blahblah >> logfile.log 2>&1/, @output
|
49
|
+
assert_match /^.+ .+ .+ .+ blahblah >> otherlog.log 2>&1$/, @output
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "A command when the cron_log is set and the comand rejects it" do
|
54
|
+
setup do
|
55
|
+
@output = Whenever.cron \
|
56
|
+
<<-file
|
57
|
+
set :cron_log, 'logfile.log'
|
58
|
+
every 2.hours do
|
59
|
+
command "blahblah", :cron_log => false
|
60
|
+
end
|
61
|
+
file
|
62
|
+
end
|
63
|
+
|
64
|
+
should "output the command without the log syntax appended" do
|
65
|
+
assert_no_match /.+ .+ .+ .+ blahblah >> logfile.log 2>&1/, @output
|
66
|
+
assert_match /^.+ .+ .+ .+ blahblah$/, @output
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class OutputEnvTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "The output from Whenever with environment variables set" do
|
6
|
+
setup do
|
7
|
+
@output = Whenever.cron \
|
8
|
+
<<-file
|
9
|
+
env :MYVAR, 'blah'
|
10
|
+
env 'MAILTO', "someone@example.com"
|
11
|
+
file
|
12
|
+
end
|
13
|
+
|
14
|
+
should "output MYVAR environment variable" do
|
15
|
+
assert_match "MYVAR=blah", @output
|
16
|
+
end
|
17
|
+
|
18
|
+
should "output MAILTO environment variable" do
|
19
|
+
assert_match "MAILTO=someone@example.com", @output
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class OutputRakeTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
# Rake are generated in an almost identical way to runners so we
|
6
|
+
# only need some basic tests to ensure they are output correctly
|
7
|
+
|
8
|
+
context "A rake command with path set" do
|
9
|
+
setup do
|
10
|
+
@output = Whenever.cron \
|
11
|
+
<<-file
|
12
|
+
set :path, '/my/path'
|
13
|
+
every 2.hours do
|
14
|
+
rake "blahblah"
|
15
|
+
end
|
16
|
+
file
|
17
|
+
end
|
18
|
+
|
19
|
+
should "output the rake command using that path" do
|
20
|
+
assert_match two_hours + ' cd /my/path && RAILS_ENV=production /usr/bin/env rake blahblah', @output
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "A rake command that overrides the path set" do
|
25
|
+
setup do
|
26
|
+
@output = Whenever.cron \
|
27
|
+
<<-file
|
28
|
+
set :path, '/my/path'
|
29
|
+
every 2.hours do
|
30
|
+
rake "blahblah", :path => '/some/other/path'
|
31
|
+
end
|
32
|
+
file
|
33
|
+
end
|
34
|
+
|
35
|
+
should "output the rake command using that path" do
|
36
|
+
assert_match two_hours + ' cd /some/other/path && RAILS_ENV=production /usr/bin/env rake blahblah', @output
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "A rake command with environment set" do
|
41
|
+
setup do
|
42
|
+
@output = Whenever.cron \
|
43
|
+
<<-file
|
44
|
+
set :environment, :silly
|
45
|
+
set :path, '/my/path'
|
46
|
+
every 2.hours do
|
47
|
+
rake "blahblah"
|
48
|
+
end
|
49
|
+
file
|
50
|
+
end
|
51
|
+
|
52
|
+
should "output the rake command using that environment" do
|
53
|
+
assert_match two_hours + ' cd /my/path && RAILS_ENV=silly /usr/bin/env rake blahblah', @output
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "A rake command that overrides the environment set" do
|
58
|
+
setup do
|
59
|
+
@output = Whenever.cron \
|
60
|
+
<<-file
|
61
|
+
set :environment, :silly
|
62
|
+
set :path, '/my/path'
|
63
|
+
every 2.hours do
|
64
|
+
rake "blahblah", :environment => :serious
|
65
|
+
end
|
66
|
+
file
|
67
|
+
end
|
68
|
+
|
69
|
+
should "output the rake command using that environment" do
|
70
|
+
assert_match two_hours + ' cd /my/path && RAILS_ENV=serious /usr/bin/env rake blahblah', @output
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class OutputRunnerTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "A runner with path set" do
|
6
|
+
setup do
|
7
|
+
@output = Whenever.cron \
|
8
|
+
<<-file
|
9
|
+
set :path, '/my/path'
|
10
|
+
every 2.hours do
|
11
|
+
runner "blahblah"
|
12
|
+
end
|
13
|
+
file
|
14
|
+
end
|
15
|
+
|
16
|
+
should "output the runner using that path" do
|
17
|
+
assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "A runner that overrides the path set" do
|
22
|
+
setup do
|
23
|
+
@output = Whenever.cron \
|
24
|
+
<<-file
|
25
|
+
set :path, '/my/path'
|
26
|
+
every 2.hours do
|
27
|
+
runner "blahblah", :path => '/some/other/path'
|
28
|
+
end
|
29
|
+
file
|
30
|
+
end
|
31
|
+
|
32
|
+
should "output the runner using that path" do
|
33
|
+
assert_match two_hours + ' /some/other/path/script/runner -e production "blahblah"', @output
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "A runner with no path set and RAILS_ROOT defined" do
|
38
|
+
setup do
|
39
|
+
Whenever.stubs(:path).returns('/my/path')
|
40
|
+
|
41
|
+
@output = Whenever.cron \
|
42
|
+
<<-file
|
43
|
+
every 2.hours do
|
44
|
+
runner "blahblah"
|
45
|
+
end
|
46
|
+
file
|
47
|
+
end
|
48
|
+
|
49
|
+
should "output the runner using that path" do
|
50
|
+
assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "A runner with path set AND RAILS_ROOT defined" do
|
55
|
+
setup do
|
56
|
+
Whenever.stubs(:path).returns('/my/rails/path')
|
57
|
+
|
58
|
+
@output = Whenever.cron \
|
59
|
+
<<-file
|
60
|
+
set :path, '/my/path'
|
61
|
+
every 2.hours do
|
62
|
+
runner "blahblah"
|
63
|
+
end
|
64
|
+
file
|
65
|
+
end
|
66
|
+
|
67
|
+
should "use the path" do
|
68
|
+
assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
|
69
|
+
assert_no_match /\/rails\/path/, @output
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "A runner with no path set and no RAILS_ROOT defined" do
|
74
|
+
setup do
|
75
|
+
Whenever.stubs(:path).returns(nil)
|
76
|
+
|
77
|
+
@input = <<-file
|
78
|
+
every 2.hours do
|
79
|
+
runner "blahblah"
|
80
|
+
end
|
81
|
+
file
|
82
|
+
end
|
83
|
+
|
84
|
+
should "raise an exception" do
|
85
|
+
assert_raises ArgumentError do
|
86
|
+
Whenever.cron(@input)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "A runner with an environment set" do
|
92
|
+
setup do
|
93
|
+
@output = Whenever.cron \
|
94
|
+
<<-file
|
95
|
+
set :environment, :silly
|
96
|
+
set :path, '/my/path'
|
97
|
+
every 2.hours do
|
98
|
+
runner "blahblah"
|
99
|
+
end
|
100
|
+
file
|
101
|
+
end
|
102
|
+
|
103
|
+
should "output the runner using that environment" do
|
104
|
+
assert_match two_hours + ' /my/path/script/runner -e silly "blahblah"', @output
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "A runner that overrides the environment set" do
|
109
|
+
setup do
|
110
|
+
@output = Whenever.cron \
|
111
|
+
<<-file
|
112
|
+
set :environment, :silly
|
113
|
+
set :path, '/my/path'
|
114
|
+
every 2.hours do
|
115
|
+
runner "blahblah", :environment => :serious
|
116
|
+
end
|
117
|
+
file
|
118
|
+
end
|
119
|
+
|
120
|
+
should "output the runner using that environment" do
|
121
|
+
assert_match two_hours + ' /my/path/script/runner -e serious "blahblah"', @output
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/whenever")
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'shoulda'
|
8
|
+
rescue LoadError
|
9
|
+
warn 'To test Whenever you need the shoulda gem:'
|
10
|
+
warn '$ sudo gem install thoughtbot-shoulda'
|
11
|
+
exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
require 'mocha'
|
16
|
+
rescue LoadError
|
17
|
+
warn 'To test Whenever you need the mocha gem:'
|
18
|
+
warn '$ sudo gem install mocha'
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
module TestExtensions
|
24
|
+
|
25
|
+
def two_hours
|
26
|
+
"0 0,2,4,6,8,10,12,14,16,18,20,22 * * *"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class Test::Unit::TestCase
|
32
|
+
include TestExtensions
|
33
|
+
end
|
data/whenever.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{whenever}
|
5
|
+
s.version = "0.1.5.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Javan Makhmali"]
|
9
|
+
s.date = %q{2009-02-19}
|
10
|
+
s.description = %q{Provides clean ruby syntax for defining messy cron jobs and running them Whenever.}
|
11
|
+
s.email = %q{javan@javan.us}
|
12
|
+
s.executables = ["whenever", "wheneverize"]
|
13
|
+
s.extra_rdoc_files = ["bin/whenever", "bin/wheneverize", "CHANGELOG.rdoc", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/rake_task.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/version.rb", "lib/whenever.rb", "README.rdoc"]
|
14
|
+
s.files = ["bin/whenever", "bin/wheneverize", "CHANGELOG.rdoc", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/rake_task.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/version.rb", "lib/whenever.rb", "Manifest", "Rakefile", "README.rdoc", "test/cron_test.rb", "test/output_command_test.rb", "test/output_env_test.rb", "test/output_rake_test.rb", "test/output_runner_test.rb", "test/test_helper.rb", "whenever.gemspec"]
|
15
|
+
s.has_rdoc = true
|
16
|
+
s.homepage = %q{http://github.com/javan/whenever}
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Whenever", "--main", "README.rdoc"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{whenever}
|
20
|
+
s.rubygems_version = %q{1.3.1}
|
21
|
+
s.summary = %q{Provides clean ruby syntax for defining messy cron jobs and running them Whenever.}
|
22
|
+
s.test_files = ["test/cron_test.rb", "test/output_command_test.rb", "test/output_env_test.rb", "test/output_rake_test.rb", "test/output_runner_test.rb", "test/test_helper.rb"]
|
23
|
+
|
24
|
+
if s.respond_to? :specification_version then
|
25
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
26
|
+
s.specification_version = 2
|
27
|
+
|
28
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
29
|
+
s.add_runtime_dependency(%q<chronic>, [">= 0.2.3"])
|
30
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 1.3.0"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<chronic>, [">= 0.2.3"])
|
33
|
+
s.add_dependency(%q<activesupport>, [">= 1.3.0"])
|
34
|
+
end
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<chronic>, [">= 0.2.3"])
|
37
|
+
s.add_dependency(%q<activesupport>, [">= 1.3.0"])
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: andrewtimberlake-whenever
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Javan Makhmali
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-19 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: chronic
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.2.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.3.0
|
34
|
+
version:
|
35
|
+
description: Provides clean ruby syntax for defining messy cron jobs and running them Whenever.
|
36
|
+
email: javan@javan.us
|
37
|
+
executables:
|
38
|
+
- whenever
|
39
|
+
- wheneverize
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- bin/whenever
|
44
|
+
- bin/wheneverize
|
45
|
+
- CHANGELOG.rdoc
|
46
|
+
- lib/base.rb
|
47
|
+
- lib/job_list.rb
|
48
|
+
- lib/job_types/default.rb
|
49
|
+
- lib/job_types/rake_task.rb
|
50
|
+
- lib/job_types/runner.rb
|
51
|
+
- lib/outputs/cron.rb
|
52
|
+
- lib/version.rb
|
53
|
+
- lib/whenever.rb
|
54
|
+
- README.rdoc
|
55
|
+
files:
|
56
|
+
- bin/whenever
|
57
|
+
- bin/wheneverize
|
58
|
+
- CHANGELOG.rdoc
|
59
|
+
- lib/base.rb
|
60
|
+
- lib/job_list.rb
|
61
|
+
- lib/job_types/default.rb
|
62
|
+
- lib/job_types/rake_task.rb
|
63
|
+
- lib/job_types/runner.rb
|
64
|
+
- lib/outputs/cron.rb
|
65
|
+
- lib/version.rb
|
66
|
+
- lib/whenever.rb
|
67
|
+
- Manifest
|
68
|
+
- Rakefile
|
69
|
+
- README.rdoc
|
70
|
+
- test/cron_test.rb
|
71
|
+
- test/output_command_test.rb
|
72
|
+
- test/output_env_test.rb
|
73
|
+
- test/output_rake_test.rb
|
74
|
+
- test/output_runner_test.rb
|
75
|
+
- test/test_helper.rb
|
76
|
+
- whenever.gemspec
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://github.com/javan/whenever
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options:
|
81
|
+
- --line-numbers
|
82
|
+
- --inline-source
|
83
|
+
- --title
|
84
|
+
- Whenever
|
85
|
+
- --main
|
86
|
+
- README.rdoc
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: "1.2"
|
100
|
+
version:
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project: whenever
|
104
|
+
rubygems_version: 1.2.0
|
105
|
+
signing_key:
|
106
|
+
specification_version: 2
|
107
|
+
summary: Provides clean ruby syntax for defining messy cron jobs and running them Whenever.
|
108
|
+
test_files:
|
109
|
+
- test/cron_test.rb
|
110
|
+
- test/output_command_test.rb
|
111
|
+
- test/output_env_test.rb
|
112
|
+
- test/output_rake_test.rb
|
113
|
+
- test/output_runner_test.rb
|
114
|
+
- test/test_helper.rb
|