javan-whenever 0.1.0

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/README.rdoc ADDED
@@ -0,0 +1,79 @@
1
+ = Whenever
2
+
3
+ Whenever is a ruby gem that provides a ruby syntax for defining cron jobs. It was designed to work well with Rails applications, but can be used independently as well.
4
+
5
+ == Installation
6
+
7
+ To install Whenever in a Rails (2.1 or greater) application:
8
+
9
+ in your "config/environment.rb" file:
10
+
11
+ Rails::Initializer.run do |config|
12
+ config.gem 'javan-whenever', :lib => 'whenever', :source => 'http://gems.github.com'
13
+ end
14
+
15
+ To install this gem (and all other missing gem dependencies), run rake gems:install (use sudo if necessary).
16
+
17
+ In older versions of Rails:
18
+
19
+ $ gem sources -a http://gems.github.com
20
+ $ gem install whenever
21
+
22
+ in your "config/environment.rb" file:
23
+
24
+ Rails::Initializer.run do |config|
25
+ ...
26
+ end
27
+
28
+ require "whenever"
29
+
30
+ == Getting started
31
+
32
+ $ cd /my/rails/app
33
+ $ wheneverize .
34
+
35
+ This will create an initial "config/schedule.rb" file you.
36
+
37
+ == Example schedule.rb file
38
+
39
+ set :runner_path, '/var/www/apps/my_app' # Whenever will try to use your RAILS_ROOT if this isn't set
40
+ set :runner_environment, :production # Whenever defaults to production so only set this if you want to use a different environment.
41
+ set :cron_log, '/path/to/my/cronlog.log' # Where to log (this should NOT be your Rails log)
42
+
43
+
44
+ every 2.hours do
45
+ runner "MyModel.some_process" # runners are the script/runners you know and love
46
+ command "/usr/local/bin/my_great_command" # commands are any unix command
47
+ end
48
+
49
+ every 1.day, :at => '4:30 am' do # If not :at option is set these jobs will run at midnight
50
+ runner "DB.Backup", :cron_log => false # You can specify false for no logging or a string a different log file to override logging.
51
+ end
52
+
53
+ every :hour do # Many shortcuts available: :hour, :day, :month, :year, :reboot
54
+ runner "SomeModel.ladeda"
55
+ end
56
+
57
+ every :sunday do # Use any day of the week or :weekend, :weekday
58
+ runner "Task.do_something_great"
59
+ end
60
+
61
+ == Cron output
62
+
63
+ $ cd /my/rails/app
64
+ $ whenever
65
+
66
+ And you'll see your schedule.rb converted to cron sytax
67
+
68
+ == Capistrano integration
69
+
70
+ Use the "whenever:write_cron" task to automatically write your crontab file with each deploy.
71
+
72
+ in your "config/deploy.rb" file do something like:
73
+
74
+ after "deploy:symlink", "whenever:write_cron"
75
+
76
+ THIS WILL COMPLETELY OVERWRITE ANY EXISTING CRONTAB ENTRIES!
77
+ ------------------------------------------------------------
78
+
79
+ Better documentation on the way!
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+ require 'lib/base'
5
+
6
+ Echoe.new('whenever', Whenever::VERSION) do |p|
7
+ p.description = "Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever."
8
+ p.url = "http://github.com/javan/whenever"
9
+ p.author = "Javan Makhmali"
10
+ p.email = "javan@javan.us"
11
+ p.dependencies = ["chronic", "activesupport"]
12
+ p.ignore_pattern = ["tmp/*", "script/*"]
13
+ p.development_dependencies = []
14
+ end
data/bin/whenever ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rake'
4
+ require 'optparse'
5
+ require 'whenever'
6
+
7
+ task = "whenever:output_cron"
8
+
9
+ OptionParser.new do |opts|
10
+ opts.banner = "Usage: whenever [options]"
11
+ opts.on('-c', '--write-crontab') { task = "whenever:write_cron" }
12
+ opts.on('-v', '--version') { puts "Whenever v#{Whenever::VERSION}"; exit }
13
+ end.parse!
14
+
15
+ Rake::Task[task].invoke
data/bin/wheneverize ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is based heavily on Capistrano's `capify` command
4
+
5
+ require 'optparse'
6
+
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: #{File.basename($0)} [path]"
9
+
10
+ begin
11
+ opts.parse!(ARGV)
12
+ rescue OptionParser::ParseError => e
13
+ warn e.message
14
+ puts opts
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ if ARGV.empty?
20
+ abort "Please specify the directory to wheneverize, e.g. `#{File.basename($0)} .'"
21
+ elsif !File.exists?(ARGV.first)
22
+ abort "`#{ARGV.first}' does not exist."
23
+ elsif !File.directory?(ARGV.first)
24
+ abort "`#{ARGV.first}' is not a directory."
25
+ elsif ARGV.length > 1
26
+ abort "Too many arguments; please specify only the directory to wheneverize."
27
+ end
28
+
29
+
30
+ content = <<-FILE
31
+ # Use this file to easily define all of your cron jobs.
32
+ #
33
+ # It's helpful, but not entirely necessary to understand cron before proceeding.
34
+ # http://en.wikipedia.org/wiki/Cron
35
+
36
+ # Example:
37
+ #
38
+ # set :cron_log, "/path/to/my/cron_log.log"
39
+ #
40
+ # every 2.hours do
41
+ # command "/usr/bin/some_great_command"
42
+ # runner "MyModel.some_method"
43
+ # end
44
+ #
45
+ # every 4.days do
46
+ # runner "AnotherModel.prune_old_records"
47
+ # end
48
+
49
+ # Learn more: http://github.com/javan/whenever
50
+ FILE
51
+
52
+ file = 'config/schedule.rb'
53
+ base = ARGV.shift
54
+
55
+ file = File.join(base, file)
56
+ if File.exists?(file)
57
+ warn "[skip] `#{file}' already exists"
58
+ elsif File.exists?(file.downcase)
59
+ warn "[skip] `#{file.downcase}' exists, which could conflict with `#{file}'"
60
+ elsif !File.exists?(File.dirname(file))
61
+ warn "[skip] directory `#{File.dirname(file)}' does not exist"
62
+ else
63
+ puts "[add] writing `#{file}'"
64
+ File.open(file, "w") { |f| f.write(content) }
65
+ end
66
+
67
+ puts "[done] wheneverized!"
data/lib/base.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'job_list'
2
+ require 'job_types/default'
3
+ require 'job_types/runner'
4
+ require 'outputs/cron'
5
+
6
+ module Whenever
7
+ VERSION = '0.1.0'
8
+
9
+ def self.cron(options)
10
+ Whenever::JobList.new(options).generate_cron_output
11
+ end
12
+ end
data/lib/job_list.rb ADDED
@@ -0,0 +1,84 @@
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 => @runner_environment, :path => @runner_path)
45
+ options[:class] = Whenever::Job::Runner
46
+ command(task, options)
47
+ end
48
+
49
+ def generate_cron_output
50
+ [environment_variables, cron_jobs].compact.join
51
+ end
52
+
53
+ private
54
+
55
+ def environment_variables
56
+ return if @env.empty?
57
+
58
+ output = []
59
+ @env.each do |key, val|
60
+ output << "#{key}=#{val}\n"
61
+ end
62
+ output << "\n"
63
+
64
+ output.join
65
+ end
66
+
67
+ def cron_jobs
68
+ return if @jobs.empty?
69
+
70
+ output = []
71
+ @jobs.each do |time, jobs|
72
+ jobs.each do |job|
73
+ cron = Whenever::Output::Cron.output(time, job)
74
+ cron << " >> #{job.cron_log} 2>&1" if job.cron_log
75
+ cron << "\n\n"
76
+ output << cron
77
+ end
78
+ end
79
+
80
+ output.join
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,17 @@
1
+ module Whenever
2
+ module Job
3
+ class Default
4
+ attr_accessor :task, :at, :cron_log
5
+
6
+ def initialize(options = {})
7
+ @task = options[:task]
8
+ @at = options[:at]
9
+ @cron_log = options[:cron_log]
10
+ end
11
+
12
+ def output
13
+ task
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module Whenever
2
+ module Job
3
+ class Runner < Whenever::Job::Default
4
+
5
+ def initialize(options = {})
6
+ super(options)
7
+
8
+ @environment = options[:environment] || :production
9
+
10
+ if [Whenever::Job::Runner.rails_root, options[:path]].all?(&:blank?)
11
+ raise ArgumentError, "no cron_path available for runner to use"
12
+ else
13
+ @path = options[:path] || Whenever::Job::Runner.rails_root
14
+ end
15
+ end
16
+
17
+ def self.rails_root
18
+ if defined?(RAILS_ROOT)
19
+ RAILS_ROOT
20
+ elsif defined?(::RAILS_ROOT)
21
+ ::RAILS_ROOT
22
+ end
23
+ end
24
+
25
+ def output
26
+ %Q(#{File.join(@path, 'script', 'runner')} -e #{@environment} "#{task}")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -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
@@ -0,0 +1,21 @@
1
+ namespace :whenever do
2
+
3
+ desc "outputs cron"
4
+ task :output_cron do
5
+ puts Whenever.cron(:file => "config/schedule.rb")
6
+ end
7
+
8
+ desc "writes cron"
9
+ task :write_cron do
10
+ require 'tempfile'
11
+ cron_output = Whenever.cron(:file => "config/schedule.rb")
12
+
13
+ tmp_cron_file = Tempfile.new('whenever_tmp_cron').path
14
+ File.open(tmp_cron_file, File::WRONLY | File::APPEND) do |file|
15
+ file << cron_output
16
+ end
17
+ sh "crontab #{tmp_cron_file}"
18
+ puts "[write] crontab file updated"
19
+ end
20
+
21
+ end
data/lib/whenever.rb ADDED
@@ -0,0 +1,24 @@
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
+
11
+ # Load Whenever's rake tasks
12
+ begin
13
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '*.rake')].each { |rake| load rake }
14
+ rescue LoadError => e
15
+ nil
16
+ end
17
+
18
+ end
19
+
20
+
21
+ require 'activesupport'
22
+ require 'chronic'
23
+
24
+ require 'base'
data/test/cron_test.rb ADDED
@@ -0,0 +1,187 @@
1
+ require '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 'test_helper'
2
+
3
+ class OutputCommandTest < Test::Unit::TestCase
4
+
5
+ context "A plain command" do
6
+ setup do
7
+ @output = load_whenever_output \
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 = load_whenever_output \
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 = load_whenever_output \
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 = load_whenever_output \
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 '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 = load_whenever_output \
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,75 @@
1
+ require 'test_helper'
2
+
3
+ class OutputRunnerTest < Test::Unit::TestCase
4
+
5
+ context "A runner with runner_path set" do
6
+ setup do
7
+ @output = load_whenever_output \
8
+ <<-file
9
+ set :runner_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 with no runner_path set and RAILS_ROOT defined" do
22
+ setup do
23
+ Whenever::Job::Runner.stubs(:rails_root).returns('/my/path')
24
+
25
+ @output = load_whenever_output \
26
+ <<-file
27
+ every 2.hours do
28
+ runner "blahblah"
29
+ end
30
+ file
31
+ end
32
+
33
+ should "output the runner using that path" do
34
+ assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
35
+ end
36
+ end
37
+
38
+ context "A runner with runner_path set AND RAILS_ROOT defined" do
39
+ setup do
40
+ Whenever::Job::Runner.stubs(:rails_root).returns('/my/path')
41
+
42
+ @output = load_whenever_output \
43
+ <<-file
44
+ set :runner_path, '/my/path'
45
+ every 2.hours do
46
+ runner "blahblah"
47
+ end
48
+ file
49
+ end
50
+
51
+ should "use the runner_path" do
52
+ assert_match two_hours + ' /my/path/script/runner -e production "blahblah"', @output
53
+ assert_no_match /\/rails\/path/, @output
54
+ end
55
+ end
56
+
57
+ context "A runner with no runner_path set and no RAILS_ROOT defined" do
58
+ setup do
59
+ Whenever::Job::Runner.stubs(:rails_root).returns(nil)
60
+
61
+ @input = <<-file
62
+ every 2.hours do
63
+ runner "blahblah"
64
+ end
65
+ file
66
+ end
67
+
68
+ should "raise an exception" do
69
+ assert_raises ArgumentError do
70
+ load_whenever_output(@input)
71
+ end
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ begin
5
+ require 'shoulda'
6
+ rescue LoadError
7
+ warn 'To test Whenever you need the shoulda gem:'
8
+ warn '$ sudo gem install thoughtbot-shoulda'
9
+ exit(1)
10
+ end
11
+
12
+ begin
13
+ require 'mocha'
14
+ rescue LoadError
15
+ warn 'To test Whenever you need the mocha gem:'
16
+ warn '$ sudo gem install mocha'
17
+ exit(1)
18
+ end
19
+
20
+ require 'whenever'
21
+
22
+ module TestExtensions
23
+
24
+ def load_whenever_output(input)
25
+ Whenever.cron(input)
26
+ end
27
+
28
+ def two_hours
29
+ "0 0,2,4,6,8,10,12,14,16,18,20,22 * * *"
30
+ end
31
+ end
32
+
33
+ class Test::Unit::TestCase
34
+ include TestExtensions
35
+ 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.0"
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-15}
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", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/tasks/whenever.rake", "lib/whenever.rb", "README.rdoc"]
14
+ s.files = ["bin/whenever", "bin/wheneverize", "lib/base.rb", "lib/job_list.rb", "lib/job_types/default.rb", "lib/job_types/runner.rb", "lib/outputs/cron.rb", "lib/tasks/whenever.rake", "lib/whenever.rb", "Manifest", "Rakefile", "README.rdoc", "test/cron_test.rb", "test/output_command_test.rb", "test/output_env_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_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"])
30
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
31
+ else
32
+ s.add_dependency(%q<chronic>, [">= 0"])
33
+ s.add_dependency(%q<activesupport>, [">= 0"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<chronic>, [">= 0"])
37
+ s.add_dependency(%q<activesupport>, [">= 0"])
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: javan-whenever
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Javan Makhmali
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-15 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"
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: "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
+ - lib/base.rb
46
+ - lib/job_list.rb
47
+ - lib/job_types/default.rb
48
+ - lib/job_types/runner.rb
49
+ - lib/outputs/cron.rb
50
+ - lib/tasks/whenever.rake
51
+ - lib/whenever.rb
52
+ - README.rdoc
53
+ files:
54
+ - bin/whenever
55
+ - bin/wheneverize
56
+ - lib/base.rb
57
+ - lib/job_list.rb
58
+ - lib/job_types/default.rb
59
+ - lib/job_types/runner.rb
60
+ - lib/outputs/cron.rb
61
+ - lib/tasks/whenever.rake
62
+ - lib/whenever.rb
63
+ - Manifest
64
+ - Rakefile
65
+ - README.rdoc
66
+ - test/cron_test.rb
67
+ - test/output_command_test.rb
68
+ - test/output_env_test.rb
69
+ - test/output_runner_test.rb
70
+ - test/test_helper.rb
71
+ - whenever.gemspec
72
+ has_rdoc: true
73
+ homepage: http://github.com/javan/whenever
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --line-numbers
77
+ - --inline-source
78
+ - --title
79
+ - Whenever
80
+ - --main
81
+ - README.rdoc
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ version:
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: "1.2"
95
+ version:
96
+ requirements: []
97
+
98
+ rubyforge_project: whenever
99
+ rubygems_version: 1.2.0
100
+ signing_key:
101
+ specification_version: 2
102
+ summary: Provides (clean) ruby syntax for defining (messy) cron jobs and running them Whenever.
103
+ test_files:
104
+ - test/cron_test.rb
105
+ - test/output_command_test.rb
106
+ - test/output_env_test.rb
107
+ - test/output_runner_test.rb
108
+ - test/test_helper.rb