serial-scheduler 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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/lib/serial-scheduler.rb +1 -0
- data/lib/serial_scheduler.rb +30 -0
- data/lib/serial_scheduler/converter.rb +70 -0
- data/lib/serial_scheduler/date_day_names_core_extension.rb +7 -0
- data/lib/serial_scheduler/dsl.rb +109 -0
- data/lib/serial_scheduler/runner.rb +32 -0
- data/lib/serial_scheduler/time_table.rb +74 -0
- data/serial-scheduler.gemspec +66 -0
- data/test/converter_test.rb +50 -0
- data/test/dsl_test.rb +43 -0
- data/test/runner_test.rb +38 -0
- data/test/serial_scheduler_test.rb +25 -0
- data/test/test_helper.rb +12 -0
- metadata +98 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Andi Bade
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= serial-scheduler
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2011 Andi Bade. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "serial-scheduler"
|
8
|
+
gem.summary = %Q{Schedule Task – execute all tasks at the same time one after another}
|
9
|
+
gem.description = %Q{Schedule Task – execute all tasks at the same time one after another}
|
10
|
+
gem.email = "andi@galaxycats.com"
|
11
|
+
gem.homepage = "http://github.com/thyphoon/serial-scheduler"
|
12
|
+
gem.authors = ["Andi Bade"]
|
13
|
+
gem.add_dependency "activesupport", "~> 3.0.0"
|
14
|
+
# gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/*_test.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/*_test.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "serial-scheduler #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1 @@
|
|
1
|
+
require "serial_scheduler"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SerialScheduler
|
2
|
+
|
3
|
+
class <<self
|
4
|
+
|
5
|
+
def define(environment_target, &block)
|
6
|
+
SerialScheduler::Dsl.new.instance_eval(&block) if !defined?(Rails) || environment_target == Rails.env.to_sym
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(time = Time.now)
|
10
|
+
SerialScheduler::Runner.run(time)
|
11
|
+
end
|
12
|
+
|
13
|
+
def dry_run(time = Time.now)
|
14
|
+
SerialScheduler::Runner.dry_run(time)
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined?(NewRelic)
|
18
|
+
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
19
|
+
add_transaction_tracer :run, :category => :task
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
require "serial_scheduler/date_day_names_core_extension"
|
27
|
+
require "serial_scheduler/converter"
|
28
|
+
require "serial_scheduler/dsl"
|
29
|
+
require "serial_scheduler/runner"
|
30
|
+
require "serial_scheduler/time_table"
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class SerialScheduler::Converter
|
2
|
+
|
3
|
+
class <<self
|
4
|
+
def to_datetime(string_or_time_or_datetime)
|
5
|
+
string_or_time_or_datetime.kind_of?(String) ?
|
6
|
+
DateTime.parse(string_or_time_or_datetime) :
|
7
|
+
string_or_time_or_datetime.to_datetime
|
8
|
+
end
|
9
|
+
|
10
|
+
def hour_and_minute(time_or_datetime)
|
11
|
+
datetime = to_datetime(time_or_datetime)
|
12
|
+
formated_hour_and_minute_string(datetime.hour, datetime.min)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hour_and_minute(time_or_datetime)
|
16
|
+
hour_and_minute(time_or_datetime)
|
17
|
+
end
|
18
|
+
|
19
|
+
def hour_and_minute_in_5_minute_steps(time_or_datetime)
|
20
|
+
datetime = to_datetime(time_or_datetime)
|
21
|
+
minutes = floor_to_five(datetime.min)
|
22
|
+
formated_hour_and_minute_string(datetime.hour, minutes)
|
23
|
+
end
|
24
|
+
|
25
|
+
def weekday_hour_and_minute_in_5_minute_steps(time_or_datetime)
|
26
|
+
datetime = to_datetime(time_or_datetime)
|
27
|
+
"#{datetime.wday}:#{hour_and_minute_in_5_minute_steps(datetime)}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def floor_to_five(fixnum)
|
31
|
+
unit_digit = fixnum % 10
|
32
|
+
if unit_digit < 5
|
33
|
+
fixnum - unit_digit
|
34
|
+
else
|
35
|
+
fixnum - (unit_digit - 5)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def formated_hour_and_minute_string(hours, minutes)
|
40
|
+
minutes = minutes < 10 ? "0#{minutes}" : "#{minutes}"
|
41
|
+
hours = hours < 10 ? "0#{hours}" : "#{hours}"
|
42
|
+
"#{hours}:#{minutes}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def hours_between(from, to)
|
46
|
+
hours = []
|
47
|
+
for_each_hour(from,to) do |hour|
|
48
|
+
hours << hour
|
49
|
+
end
|
50
|
+
hours
|
51
|
+
end
|
52
|
+
|
53
|
+
def for_each_hour(from, to)
|
54
|
+
for_each_time_with_step(from, to, 1.hour) do |time|
|
55
|
+
yield(time)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def for_each_time_with_step(from, to, step)
|
60
|
+
current_time = DateTime.parse(from)
|
61
|
+
max_time = DateTime.parse(to)
|
62
|
+
max_time = max_time + 1.day if max_time <= current_time
|
63
|
+
while current_time <= max_time
|
64
|
+
yield(SerialScheduler::Converter.to_hour_and_minute(current_time))
|
65
|
+
current_time += step
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class SerialScheduler::Dsl
|
2
|
+
|
3
|
+
class CallWrapper
|
4
|
+
attr_reader :callee, :meth
|
5
|
+
def initialize(callee, meth)
|
6
|
+
@callee = callee
|
7
|
+
@meth = meth
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
instance_to_call = callee.kind_of?(String) ? callee.constantize : callee
|
12
|
+
instance_to_call.send(meth)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{callee}.#{meth}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :callee
|
21
|
+
|
22
|
+
|
23
|
+
# How to use the scheduler
|
24
|
+
#
|
25
|
+
# With the scheduler DSL you can define jobs in a simple language
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# schedule AnyReceiver do
|
30
|
+
# some_method { every :hour }
|
31
|
+
# some_other_method { every 2.hours, :from => "9:00", :to => "18:00" }
|
32
|
+
# and_another_method { every :friday, :at => "3:00" }
|
33
|
+
# end
|
34
|
+
|
35
|
+
def schedule(callee, &block)
|
36
|
+
self.callee = callee
|
37
|
+
self.instance_eval(&block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def every(*args)
|
41
|
+
case args.first
|
42
|
+
when Symbol
|
43
|
+
time_keys_by_symbol(*args)
|
44
|
+
when ActiveSupport::Duration
|
45
|
+
time_keys_by_duration(*args)
|
46
|
+
else
|
47
|
+
raise "SerialScheduler::Dsl - every - You have to specify either a symbol or a duration (was #{args.first.inspect})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def time_keys_by_symbol(*args)
|
52
|
+
time_keys = []
|
53
|
+
options = args.extract_options!
|
54
|
+
case args.first
|
55
|
+
when :hour
|
56
|
+
from, to = extract_from_to_or_default(options)
|
57
|
+
time_keys = SerialScheduler::Converter.hours_between(from, to)
|
58
|
+
when :day
|
59
|
+
time_keys = extract_at_or_default(options)
|
60
|
+
else
|
61
|
+
at = extract_at_or_default(options)
|
62
|
+
day = args.first
|
63
|
+
if [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday].include?(day)
|
64
|
+
time_keys << "#{Date.const_get(day.to_s.upcase)}:#{at}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
return time_keys
|
68
|
+
end
|
69
|
+
|
70
|
+
def time_keys_by_duration(*args)
|
71
|
+
options = args.extract_options!
|
72
|
+
from, to = extract_from_to_or_default(options)
|
73
|
+
step = args.first
|
74
|
+
time_keys = []
|
75
|
+
SerialScheduler::Converter.for_each_time_with_step(from, to, step) do |time_key|
|
76
|
+
time_keys << time_key
|
77
|
+
end
|
78
|
+
time_keys
|
79
|
+
end
|
80
|
+
|
81
|
+
def extract_from_to_or_default(options)
|
82
|
+
options ||= {}
|
83
|
+
from = options[:from] ? SerialScheduler::Converter.to_hour_and_minute(options[:from]) : "00:00"
|
84
|
+
to = options[:to] ? SerialScheduler::Converter.to_hour_and_minute(options[:to]) : "23:59"
|
85
|
+
return from, to
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_at_or_default(options)
|
89
|
+
options ||= {}
|
90
|
+
options[:at] ? SerialScheduler::Converter.to_hour_and_minute(options[:at]) : "00:00"
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_time_table_with_time_keys(time_keys, callee, meth)
|
94
|
+
time_keys.inject({}) do |time_table, time_key|
|
95
|
+
time_table[time_key] = CallWrapper.new(callee, meth)
|
96
|
+
time_table
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def method_missing(meth, *args, &blk)
|
101
|
+
if block_given?
|
102
|
+
time_keys = instance_eval(&blk)
|
103
|
+
SerialScheduler::TimeTable.add(create_time_table_with_time_keys(time_keys, self.callee, meth))
|
104
|
+
else
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SerialScheduler::Runner
|
2
|
+
|
3
|
+
class <<self
|
4
|
+
|
5
|
+
def run(time, options = {})
|
6
|
+
daily_time_key = SerialScheduler::Converter.hour_and_minute_in_5_minute_steps(time)
|
7
|
+
weekly_time_key = SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps(time)
|
8
|
+
jobs = SerialScheduler::TimeTable.jobs_for([weekly_time_key, daily_time_key].compact)
|
9
|
+
jobs.each do |job|
|
10
|
+
begin
|
11
|
+
if options[:dry_run]
|
12
|
+
puts ">> #{job}"
|
13
|
+
else
|
14
|
+
job.call
|
15
|
+
end
|
16
|
+
rescue => e
|
17
|
+
if defined?(Rails)
|
18
|
+
raise e if Rails.env.development?
|
19
|
+
Notifier.deliver_scheduler_error(e, job, time)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def dry_run(time)
|
26
|
+
run(time, :dry_run => true)
|
27
|
+
puts ">> end"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class SerialScheduler::TimeTable
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
attr_reader :timing
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@timing = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
class <<self
|
13
|
+
def jobs_for(*time_keys)
|
14
|
+
[time_keys].flatten.collect do |time_key|
|
15
|
+
timing[time_key]
|
16
|
+
end.flatten.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(meth, *args, &blk)
|
20
|
+
if self.instance.respond_to?(meth)
|
21
|
+
self.instance.send(meth, *args, &blk)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def add(hash_with_timing_info)
|
29
|
+
hash_with_timing_info.each do |timing_key, calls|
|
30
|
+
self.timing[timing_key] ||= []
|
31
|
+
self.timing[timing_key] += [calls].flatten
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear
|
36
|
+
self.timing = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def weekly_timing
|
40
|
+
timing.reject { |time_key, call| time_key.size != 7 }
|
41
|
+
end
|
42
|
+
|
43
|
+
def daily_timing
|
44
|
+
timing.reject { |time_key, call| time_key.size != 5 }
|
45
|
+
end
|
46
|
+
|
47
|
+
def print
|
48
|
+
print_daily_timing
|
49
|
+
print_weekly_timing
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_daily_timing
|
53
|
+
puts
|
54
|
+
puts "daily timing"
|
55
|
+
puts "-"*15
|
56
|
+
daily_timing.keys.sort.each do |time_key|
|
57
|
+
scheduled_calls = timing[time_key]
|
58
|
+
puts "#{time_key} |\t#{scheduled_calls.join("\n\t")}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def print_weekly_timing
|
63
|
+
puts
|
64
|
+
puts "weekly timing"
|
65
|
+
puts "-"*15
|
66
|
+
weekly_timing.keys.sort.each do |time_key|
|
67
|
+
scheduled_calls = timing[time_key]
|
68
|
+
puts "#{Date.human_wday(time_key.first.to_i)}, #{time_key[2..-1]} |\t#{scheduled_calls.join("\n\t")}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
attr_writer :timing
|
74
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{serial-scheduler}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Andi Bade"]
|
12
|
+
s.date = %q{2011-02-28}
|
13
|
+
s.description = %q{Schedule Task – execute all tasks at the same time one after another}
|
14
|
+
s.email = %q{andi@galaxycats.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/serial-scheduler.rb",
|
27
|
+
"lib/serial_scheduler.rb",
|
28
|
+
"lib/serial_scheduler/converter.rb",
|
29
|
+
"lib/serial_scheduler/date_day_names_core_extension.rb",
|
30
|
+
"lib/serial_scheduler/dsl.rb",
|
31
|
+
"lib/serial_scheduler/runner.rb",
|
32
|
+
"lib/serial_scheduler/time_table.rb",
|
33
|
+
"serial-scheduler.gemspec",
|
34
|
+
"test/converter_test.rb",
|
35
|
+
"test/dsl_test.rb",
|
36
|
+
"test/runner_test.rb",
|
37
|
+
"test/serial_scheduler_test.rb",
|
38
|
+
"test/test_helper.rb"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/thyphoon/serial-scheduler}
|
41
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
42
|
+
s.require_paths = ["lib"]
|
43
|
+
s.rubygems_version = %q{1.3.6}
|
44
|
+
s.summary = %q{Schedule Task – execute all tasks at the same time one after another}
|
45
|
+
s.test_files = [
|
46
|
+
"test/converter_test.rb",
|
47
|
+
"test/dsl_test.rb",
|
48
|
+
"test/runner_test.rb",
|
49
|
+
"test/serial_scheduler_test.rb",
|
50
|
+
"test/test_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.0"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.0"])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ConverterTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "should calculate hour and minute string floored in 5 minute steps" do
|
6
|
+
assert_equal "11:55", SerialScheduler::Converter.hour_and_minute_in_5_minute_steps("17.12.2010 11:59".to_datetime)
|
7
|
+
assert_equal "12:00", SerialScheduler::Converter.hour_and_minute_in_5_minute_steps("17.12.2010 12:00".to_datetime)
|
8
|
+
assert_equal "12:00", SerialScheduler::Converter.hour_and_minute_in_5_minute_steps("17.12.2010 12:04".to_datetime)
|
9
|
+
assert_equal "12:05", SerialScheduler::Converter.hour_and_minute_in_5_minute_steps("17.12.2010 12:05".to_datetime)
|
10
|
+
assert_equal "12:05", SerialScheduler::Converter.hour_and_minute_in_5_minute_steps("17.12.2010 12:06".to_datetime)
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should calculate weekday, hour and minute string floored in 5 minute steps" do
|
14
|
+
assert_equal "#{Date::FRIDAY}:11:55", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("17.12.2010 11:59".to_datetime)
|
15
|
+
assert_equal "#{Date::SATURDAY}:12:00", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("18.12.2010 12:00".to_datetime)
|
16
|
+
assert_equal "#{Date::SUNDAY}:12:00", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("19.12.2010 12:04".to_datetime)
|
17
|
+
assert_equal "#{Date::MONDAY}:12:05", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("20.12.2010 12:05".to_datetime)
|
18
|
+
assert_equal "#{Date::TUESDAY}:12:05", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("21.12.2010 12:06".to_datetime)
|
19
|
+
assert_equal "#{Date::WEDNESDAY}:14:05", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("22.12.2010 14:06".to_datetime)
|
20
|
+
assert_equal "#{Date::THURSDAY}:18:05", SerialScheduler::Converter.weekday_hour_and_minute_in_5_minute_steps("23.12.2010 18:06".to_datetime)
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should round (floor) numbers in steps of 5" do
|
24
|
+
assert_equal 0, SerialScheduler::Converter.floor_to_five(1)
|
25
|
+
assert_equal 0, SerialScheduler::Converter.floor_to_five(4)
|
26
|
+
assert_equal 5, SerialScheduler::Converter.floor_to_five(5)
|
27
|
+
assert_equal 5, SerialScheduler::Converter.floor_to_five(6)
|
28
|
+
assert_equal 5, SerialScheduler::Converter.floor_to_five(9)
|
29
|
+
assert_equal 10, SerialScheduler::Converter.floor_to_five(10)
|
30
|
+
assert_equal 10, SerialScheduler::Converter.floor_to_five(11)
|
31
|
+
end
|
32
|
+
|
33
|
+
test "should format hour and minute as a string with two and two digits" do
|
34
|
+
assert_equal "01:09", SerialScheduler::Converter.formated_hour_and_minute_string(1, 9)
|
35
|
+
assert_equal "12:00", SerialScheduler::Converter.formated_hour_and_minute_string(12, 0)
|
36
|
+
assert_equal "14:03", SerialScheduler::Converter.formated_hour_and_minute_string(14, 3)
|
37
|
+
assert_equal "23:59", SerialScheduler::Converter.formated_hour_and_minute_string(23, 59)
|
38
|
+
end
|
39
|
+
|
40
|
+
test "should return an array with all hours between two hours" do
|
41
|
+
assert_equal ["23:00", "00:00", "01:00", "02:00"], SerialScheduler::Converter.hours_between("23:00", "02:00")
|
42
|
+
assert_equal ["03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00", "12:00", "13:00", "14:00",
|
43
|
+
"15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00", "00:00", "01:00", "02:00", "03:00"], SerialScheduler::Converter.hours_between("03:00", "03:00")
|
44
|
+
assert_equal ["03:00", "04:00"], SerialScheduler::Converter.hours_between("03:00", "04:00")
|
45
|
+
assert_equal ["03:00"], SerialScheduler::Converter.hours_between("03:00", "03:01")
|
46
|
+
assert_equal ["08:00", "09:00", "10:00", "11:00", "12:00", "13:00"], SerialScheduler::Converter.hours_between("08:00", "13:00")
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
data/test/dsl_test.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DslTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
test "should create time keys by symbol :hour" do
|
6
|
+
assert_equal ["00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00",
|
7
|
+
"12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"], SerialScheduler::Dsl.new.time_keys_by_symbol(:hour)
|
8
|
+
|
9
|
+
assert_equal ["08:00", "09:00", "10:00", "11:00", "12:00", "13:00",
|
10
|
+
"14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00"], SerialScheduler::Dsl.new.time_keys_by_symbol(:hour, :from => "8:00", :to => "20:00")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should create time keys by time step size (duration)" do
|
14
|
+
expected_time_keys = []
|
15
|
+
SerialScheduler::Converter.for_each_time_with_step("00:00", "23:55", 5.minutes) { |time_key| expected_time_keys << time_key }
|
16
|
+
assert_equal expected_time_keys, SerialScheduler::Dsl.new.time_keys_by_duration(5.minutes)
|
17
|
+
|
18
|
+
assert_equal ["11:23", "11:28", "11:33"], SerialScheduler::Dsl.new.time_keys_by_duration(5.minutes, :from => "11:23", :to => "11:37")
|
19
|
+
end
|
20
|
+
|
21
|
+
test "should create time key for a day symbol with 'at' condition" do
|
22
|
+
assert_equal ["5:00:00"], SerialScheduler::Dsl.new.time_keys_by_symbol(:friday)
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should extract from and to from options or return defaults" do
|
26
|
+
dsl = SerialScheduler::Dsl.new
|
27
|
+
assert_equal ["08:00", "09:00"], dsl.extract_from_to_or_default({:from => "8:00", :to => "9:00"})
|
28
|
+
assert_equal ["12:00", "13:00"], dsl.extract_from_to_or_default({:from => "12:00", :to => "13:00"})
|
29
|
+
assert_equal ["00:00", "23:59"], dsl.extract_from_to_or_default(nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
test "should extract at from options or return default" do
|
33
|
+
dsl = SerialScheduler::Dsl.new
|
34
|
+
assert_equal "00:00", dsl.extract_at_or_default({:at => "0:00"})
|
35
|
+
assert_equal "12:00", dsl.extract_at_or_default({:at => "12:00"})
|
36
|
+
assert_equal "00:00", dsl.extract_at_or_default(nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
test "should create time stamps for every :day" do
|
40
|
+
assert_equal "03:00", SerialScheduler::Dsl.new.time_keys_by_symbol(:day, :at => "3:00")
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/test/runner_test.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RunnerTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
class MockReceiver; end
|
6
|
+
|
7
|
+
def setup
|
8
|
+
SerialScheduler::TimeTable.clear
|
9
|
+
SerialScheduler.define :test do
|
10
|
+
schedule RunnerTest::MockReceiver do
|
11
|
+
lastminute { every :hour, :from => "8:00", :to => "20:00" }
|
12
|
+
availability { every 2.hours, :from => "0:00", :to => "4:00" }
|
13
|
+
houses { every :monday, :at => "0:00" }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should run all relevant jobs for given time" do
|
19
|
+
MockReceiver.expects(:houses)
|
20
|
+
MockReceiver.expects(:availability)
|
21
|
+
SerialScheduler::Runner.run("27.12.2010 00:00".to_datetime)
|
22
|
+
end
|
23
|
+
|
24
|
+
test "should dry-run all relevant jobs for given time" do
|
25
|
+
MockReceiver.expects(:houses).never
|
26
|
+
MockReceiver.expects(:availability).never
|
27
|
+
SerialScheduler::Runner.dry_run("27.12.2010 00:00".to_datetime)
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should catch all exceptions during job execution and send an email to developers" do
|
31
|
+
if defined?(Rails)
|
32
|
+
MockReceiver.expects(:lastminute).raises(ArgumentError, "Hello from test")
|
33
|
+
Notifier.expects(:deliver_scheduler_error)
|
34
|
+
SerialScheduler::Runner.run("27.12.2010 8:00".to_datetime)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SerialSchedulerTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
SerialScheduler::TimeTable.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
test "should evaluate a definition with every hour" do
|
10
|
+
SerialScheduler.define :test do
|
11
|
+
schedule "some importer" do
|
12
|
+
lastminute { every :hour }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
assert_equal(
|
16
|
+
["00:00", "01:00", "02:00", "03:00", "04:00", "05:00", "06:00", "07:00", "08:00", "09:00", "10:00", "11:00",
|
17
|
+
"12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00", "21:00", "22:00", "23:00"],
|
18
|
+
SerialScheduler::TimeTable.timing.keys.sort
|
19
|
+
)
|
20
|
+
SerialScheduler::TimeTable.timing.values.each do |vals|
|
21
|
+
assert_kind_of Array, vals
|
22
|
+
assert vals.all? { |val| val.kind_of?(SerialScheduler::Dsl::CallWrapper) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/test_case'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
+
require 'serial-scheduler'
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: serial-scheduler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Andi Bade
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-02-28 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 3
|
29
|
+
- 0
|
30
|
+
- 0
|
31
|
+
version: 3.0.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: "Schedule Task \xE2\x80\x93 execute all tasks at the same time one after another"
|
35
|
+
email: andi@galaxycats.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .document
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- lib/serial-scheduler.rb
|
51
|
+
- lib/serial_scheduler.rb
|
52
|
+
- lib/serial_scheduler/converter.rb
|
53
|
+
- lib/serial_scheduler/date_day_names_core_extension.rb
|
54
|
+
- lib/serial_scheduler/dsl.rb
|
55
|
+
- lib/serial_scheduler/runner.rb
|
56
|
+
- lib/serial_scheduler/time_table.rb
|
57
|
+
- serial-scheduler.gemspec
|
58
|
+
- test/converter_test.rb
|
59
|
+
- test/dsl_test.rb
|
60
|
+
- test/runner_test.rb
|
61
|
+
- test/serial_scheduler_test.rb
|
62
|
+
- test/test_helper.rb
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/thyphoon/serial-scheduler
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options:
|
69
|
+
- --charset=UTF-8
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirements: []
|
87
|
+
|
88
|
+
rubyforge_project:
|
89
|
+
rubygems_version: 1.3.6
|
90
|
+
signing_key:
|
91
|
+
specification_version: 3
|
92
|
+
summary: "Schedule Task \xE2\x80\x93 execute all tasks at the same time one after another"
|
93
|
+
test_files:
|
94
|
+
- test/converter_test.rb
|
95
|
+
- test/dsl_test.rb
|
96
|
+
- test/runner_test.rb
|
97
|
+
- test/serial_scheduler_test.rb
|
98
|
+
- test/test_helper.rb
|