chrono_trigger 0.2.1 → 1.0.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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +167 -0
- data/LICENSE.txt +21 -0
- data/README.md +2 -0
- data/Rakefile +3 -25
- data/SUMMARY.md +4 -0
- data/bin/console +7 -0
- data/bin/loc +3 -0
- data/bin/setup +8 -0
- data/bin/standardize +4 -0
- data/lib/chrono_trigger.rb +28 -12
- data/lib/chrono_trigger/clock.rb +50 -0
- data/lib/chrono_trigger/config.rb +12 -0
- data/lib/chrono_trigger/event.rb +118 -0
- data/lib/chrono_trigger/schedule.rb +71 -0
- data/lib/chrono_trigger/timeline.rb +28 -0
- data/lib/chrono_trigger/version.rb +2 -3
- data/lib/chrono_trigger/worker.rb +10 -0
- metadata +159 -155
- data/.project +0 -11
- data/History.txt +0 -21
- data/Manifest.txt +0 -25
- data/PostInstall.txt +0 -1
- data/README.rdoc +0 -61
- data/VERSION.yml +0 -4
- data/bin/chrono_trigger +0 -7
- data/chrono_trigger.gemspec +0 -72
- data/lib/chrono_trigger/cron_entry.rb +0 -71
- data/lib/chrono_trigger/process.rb +0 -37
- data/lib/chrono_trigger/runner.rb +0 -293
- data/lib/chrono_trigger/shell.rb +0 -36
- data/lib/chrono_trigger/tasks.rb +0 -3
- data/lib/chrono_trigger/trigger.rb +0 -127
- data/lib/tasks/chrono_trigger.rake +0 -14
- data/lib/triggers/test_triggers.rb +0 -31
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/test/test_chrono_trigger.rb +0 -11
- data/test/test_cron_entry.rb +0 -198
- data/test/test_helper.rb +0 -14
- data/test/test_shell.rb +0 -52
- data/test/test_trigger.rb +0 -170
- data/test/triggers.rb +0 -17
data/lib/chrono_trigger/shell.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module ChronoTrigger
|
2
|
-
|
3
|
-
class ConfigurationException < Exception; end
|
4
|
-
|
5
|
-
class Shell
|
6
|
-
|
7
|
-
DEFAULT_TRIGGERS = "lib/triggers/*.rb"
|
8
|
-
#Load triggers defined in the trigger files by evaluating them in the context of this Shell instance.
|
9
|
-
def load_triggers(files = Dir.glob("#{DEFAULT_TRIGGERS}"))
|
10
|
-
files.each { |file| self.instance_eval(File.read(file), file) }
|
11
|
-
end
|
12
|
-
|
13
|
-
#Instantiate a trigger and evaluate the passed in block in the context of the trigger.
|
14
|
-
#This is the initial method call when setting up a configuration using the DSL.
|
15
|
-
def trigger(name, &block)
|
16
|
-
raise ConfigurationException.new("No configuration specified for trigger #{name}") unless block_given?
|
17
|
-
|
18
|
-
trigger = Trigger.new(name)
|
19
|
-
trigger.instance_eval(&block)
|
20
|
-
|
21
|
-
triggers << trigger
|
22
|
-
trigger
|
23
|
-
end
|
24
|
-
|
25
|
-
#Run execute on any trigger who's cron entry matches the current time.
|
26
|
-
def execute_triggers
|
27
|
-
now = Time.now
|
28
|
-
triggers.map {|trigger| trigger.execute_on_match(now)}
|
29
|
-
end
|
30
|
-
|
31
|
-
def triggers
|
32
|
-
@triggers ||= []
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
data/lib/chrono_trigger/tasks.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
module ChronoTrigger
|
2
|
-
class Trigger
|
3
|
-
|
4
|
-
attr_accessor :name
|
5
|
-
|
6
|
-
def initialize(name)
|
7
|
-
self.name = name
|
8
|
-
end
|
9
|
-
|
10
|
-
#Define the code to be run when the cron job is ready to be executed.
|
11
|
-
def runs(&block)
|
12
|
-
@exec_block = block
|
13
|
-
end
|
14
|
-
|
15
|
-
#Specify what days the task should run on.
|
16
|
-
#Values are :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
|
17
|
-
def on(*days)
|
18
|
-
cron_entry.set_days(days)
|
19
|
-
end
|
20
|
-
|
21
|
-
#Specify what calendar day the task should run on; think monthly jobs.
|
22
|
-
#Values are 1-31
|
23
|
-
def monthly_on(*calendar_day)
|
24
|
-
cron_entry.set_calendar_days(calendar_day)
|
25
|
-
end
|
26
|
-
|
27
|
-
#Specify what hours and minutes the task should run at
|
28
|
-
#(e.g. :minute=>10, :hour=>3 or :minute=>[10,20,30], :hour=>[1,2,3])
|
29
|
-
def at(options={})
|
30
|
-
validate_hours_or_minutes!(options)
|
31
|
-
|
32
|
-
if hour = (options[:hour] || options[:hours])
|
33
|
-
cron_entry.set_hours(hour)
|
34
|
-
end
|
35
|
-
|
36
|
-
if minute = (options[:minute] || options[:minutes])
|
37
|
-
cron_entry.set_minutes(minute)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#Specify a repeating interval of hours and minutes to run.
|
42
|
-
#Specifying minutes not divisible by 60 result in an exception, use #at instead.
|
43
|
-
def every(options={})
|
44
|
-
validate_hours_or_minutes!(options)
|
45
|
-
if minutes = (options[:minutes] || options[:minute])
|
46
|
-
cron_entry.set_minutes(extract_minutes_for_every(minutes))
|
47
|
-
end
|
48
|
-
|
49
|
-
if hours = (options[:hours] || options[:hour])
|
50
|
-
cron_entry.set_hours(extract_hours_for_every(hours))
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def dates
|
55
|
-
@dates ||= []
|
56
|
-
end
|
57
|
-
|
58
|
-
#Execute this Trigger's code block if the datetime param matches this Trigger's cron entry.
|
59
|
-
def execute_on_match(datetime)
|
60
|
-
self.execute if cron_entry.matches?(datetime)
|
61
|
-
end
|
62
|
-
|
63
|
-
def execute
|
64
|
-
defined?(ActiveRecord) ? execute_with_active_record : execute_without_active_record
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
private
|
69
|
-
def cron_entry
|
70
|
-
@cron_entry ||= CronEntry.new
|
71
|
-
end
|
72
|
-
|
73
|
-
#Raise an exception unless minutes and hours are set.
|
74
|
-
def validate_hours_or_minutes!(options={})
|
75
|
-
unless (options[:hours] || options[:hour]) || (options[:minutes] || options[:minute])
|
76
|
-
raise ChronoTrigger::ConfigurationException.new("Hours or minutes not specified in call to 'at' or 'every' method.")
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def extract_minutes_for_every(minutes)
|
81
|
-
extract_for_every(minutes, 60)
|
82
|
-
end
|
83
|
-
|
84
|
-
def extract_hours_for_every(hours)
|
85
|
-
extract_for_every(hours, 24)
|
86
|
-
end
|
87
|
-
|
88
|
-
#Extract an array of integers representing the minutes or hours a task should be run at.
|
89
|
-
#Raise an exception if time_value is not evenly divisible by base.
|
90
|
-
def extract_for_every(time_value, base)
|
91
|
-
unless (base % time_value == 0)
|
92
|
-
raise ChronoTrigger::ConfigurationException.new("#{time_value} is not evenly divisible by #{base}. Consider using #at instead.")
|
93
|
-
end
|
94
|
-
|
95
|
-
(0...base).select {|num| num % time_value == 0}
|
96
|
-
end
|
97
|
-
|
98
|
-
# When ActiveRecord is defined, attempt to rescue ConnectionNotEstablished errors once,
|
99
|
-
# and reestablish the connection to the database. If this fails, normal exception logging will take place.
|
100
|
-
#
|
101
|
-
def execute_with_active_record
|
102
|
-
begin
|
103
|
-
@exec_block.call
|
104
|
-
rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::StatementInvalid
|
105
|
-
ActiveRecord::Base.connection.reconnect!
|
106
|
-
execute_without_active_record
|
107
|
-
rescue Exception
|
108
|
-
log_exception
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# Execute the execution block and log all exceptions.
|
113
|
-
#
|
114
|
-
def execute_without_active_record
|
115
|
-
begin
|
116
|
-
@exec_block.call
|
117
|
-
rescue Exception
|
118
|
-
log_exception
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def log_exception
|
123
|
-
STDERR.puts "Exception #{$!.inspect} caught in Trigger##{self.name}. Backtrace:"
|
124
|
-
STDERR.puts $!.backtrace
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
namespace :chrono_trigger do
|
2
|
-
|
3
|
-
run_task_name = defined?(RAILS_ROOT) ? {:run => :environment} : :run
|
4
|
-
desc "Execute all triggers in loop, sleeping 1 minute between checks."
|
5
|
-
task run_task_name do
|
6
|
-
shell = ChronoTrigger::Shell.new
|
7
|
-
shell.load_triggers
|
8
|
-
loop do
|
9
|
-
shell.execute_triggers
|
10
|
-
sleep 1.minute.to_i
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
trigger "trigger1" do
|
2
|
-
runs { puts "trigger 1 runs every 1 minutes; executed at #{Time.now}" }
|
3
|
-
every :minutes=>1
|
4
|
-
end
|
5
|
-
|
6
|
-
trigger "exception trigger" do
|
7
|
-
runs { raise Exception.new("test exception")}
|
8
|
-
every :minutes=>2
|
9
|
-
end
|
10
|
-
|
11
|
-
trigger "trigger2" do
|
12
|
-
runs { puts "trigger 2 runs every 5 minutes; executed at #{Time.now}"}
|
13
|
-
every :minutes=>5
|
14
|
-
end
|
15
|
-
|
16
|
-
trigger "trigger3" do
|
17
|
-
runs { puts "trigger 3 runs at 9:48; executed at #{Time.now}"}
|
18
|
-
at :hour=>9, :minute=>48
|
19
|
-
end
|
20
|
-
|
21
|
-
trigger "trigger4" do
|
22
|
-
runs { puts "trigger 4 runs on monday at 9:53 and 9:56; executed at #{Time.now}"}
|
23
|
-
on :monday
|
24
|
-
at :hour=>9, :minute=>[53, 56]
|
25
|
-
end
|
26
|
-
|
27
|
-
trigger "trigger5" do
|
28
|
-
runs { puts "trigger 5 runs on thursday at 9:58/59; executed at #{Time.now}"}
|
29
|
-
on :thursday
|
30
|
-
at :hour=>9, :minute=>[58, 59]
|
31
|
-
end
|
data/script/console
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# File: script/console
|
3
|
-
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
-
|
5
|
-
libs = " -r irb/completion"
|
6
|
-
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
-
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
-
libs << " -r #{File.dirname(__FILE__) + '/../lib/chrono_trigger.rb'}"
|
9
|
-
puts "Loading chrono_trigger gem"
|
10
|
-
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/destroy'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rubigen'
|
6
|
-
rescue LoadError
|
7
|
-
require 'rubygems'
|
8
|
-
require 'rubigen'
|
9
|
-
end
|
10
|
-
require 'rubigen/scripts/generate'
|
11
|
-
|
12
|
-
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
-
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
-
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/test/test_chrono_trigger.rb
DELETED
data/test/test_cron_entry.rb
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
class TestCronEntry < Test::Unit::TestCase
|
2
|
-
|
3
|
-
class << self
|
4
|
-
|
5
|
-
def should_match(options={})
|
6
|
-
context "and a datetime of #{options.inspect}" do
|
7
|
-
setup do
|
8
|
-
@datetime = time_from_options(options)
|
9
|
-
end
|
10
|
-
|
11
|
-
should "return true on a call to matches?" do
|
12
|
-
assert @cron.matches?(@datetime)
|
13
|
-
end
|
14
|
-
end #and a datetime of options[]
|
15
|
-
end
|
16
|
-
|
17
|
-
def should_not_match(options={})
|
18
|
-
context "and a datetime of #{options.inspect}" do
|
19
|
-
setup do
|
20
|
-
@datetime = time_from_options(options)
|
21
|
-
end
|
22
|
-
|
23
|
-
should "return false on a call to matches?" do
|
24
|
-
assert !@cron.matches?(@datetime)
|
25
|
-
end
|
26
|
-
end #and a datetime of options[]
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
context "A CronEntry, @cron," do
|
32
|
-
setup do
|
33
|
-
@cron = ChronoTrigger::CronEntry.new
|
34
|
-
end
|
35
|
-
|
36
|
-
context "with a minutes entry of 10 minutes" do
|
37
|
-
setup do
|
38
|
-
@cron.set_minutes(10)
|
39
|
-
end
|
40
|
-
|
41
|
-
should_match(:minutes=>10)
|
42
|
-
|
43
|
-
should_not_match(:minutes=>11)
|
44
|
-
|
45
|
-
context "and 25 minutes" do
|
46
|
-
setup do
|
47
|
-
@cron.set_minutes(10, 25)
|
48
|
-
end
|
49
|
-
|
50
|
-
should_match(:minutes=>25)
|
51
|
-
|
52
|
-
should_match(:minutes=>10)
|
53
|
-
|
54
|
-
should_not_match(:minutes=>12)
|
55
|
-
|
56
|
-
context "and a day entry of monday" do
|
57
|
-
setup do
|
58
|
-
@cron.set_days(:monday)
|
59
|
-
end
|
60
|
-
|
61
|
-
should_match(:minutes=>10, :wday=>1)
|
62
|
-
|
63
|
-
should_not_match(:minutes=>10, :wday=>2)
|
64
|
-
|
65
|
-
context "and wednesday" do
|
66
|
-
setup do
|
67
|
-
@cron.set_days(:monday, :wednesday)
|
68
|
-
end
|
69
|
-
|
70
|
-
should_match(:minutes=>10, :wday=>1)
|
71
|
-
|
72
|
-
should_match(:minutes=>10, :wday=>3)
|
73
|
-
|
74
|
-
should_not_match(:minutes=>25, :wday=>5)
|
75
|
-
|
76
|
-
should_not_match(:minutes=>11, :wday=>3)
|
77
|
-
|
78
|
-
context "and an hour entry of 2" do
|
79
|
-
setup do
|
80
|
-
@cron.set_hours(5)
|
81
|
-
end
|
82
|
-
|
83
|
-
should_match(:minutes=>25, :wday=>3, :hour=>5)
|
84
|
-
|
85
|
-
should_not_match(:minutes=>25, :wday=>3, :hour=>6)
|
86
|
-
end #and an hour entry of 2
|
87
|
-
end #and wednesday
|
88
|
-
end #and a day entry of monday
|
89
|
-
end #and 25 minutes
|
90
|
-
end #with a minutes entry of 10 minutes
|
91
|
-
|
92
|
-
context "with a day entry of monday" do
|
93
|
-
setup do
|
94
|
-
@cron.set_days(:monday)
|
95
|
-
end
|
96
|
-
|
97
|
-
context "and no minutes_entry" do
|
98
|
-
setup do
|
99
|
-
@cron.set_minutes(nil)
|
100
|
-
end
|
101
|
-
|
102
|
-
should "raise a ChronoTrigger::CronEntry:ConfigException exception on matches?" do
|
103
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
104
|
-
@cron.matches?(time_from_options)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end #and no minutes_entry
|
108
|
-
end #with a day entry
|
109
|
-
|
110
|
-
context "with a calendar_day entry of 25" do
|
111
|
-
setup do
|
112
|
-
@cron.set_calendar_days(25)
|
113
|
-
end
|
114
|
-
|
115
|
-
should "raise an exception when setting a calendar_day and no hour and minutes" do
|
116
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
117
|
-
@cron.matches?(time_from_options(:day => 25))
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
context "with a hour entry of 10" do
|
122
|
-
setup do
|
123
|
-
@cron.set_hours(10)
|
124
|
-
end
|
125
|
-
|
126
|
-
should "raise an exception when setting a calendar_day and hour but no minutes" do
|
127
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
128
|
-
@cron.matches?(time_from_options(:hour=> 10, :day => 25))
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
context "with a minutes entry of 5" do
|
133
|
-
setup do
|
134
|
-
@cron.set_minutes(5)
|
135
|
-
end
|
136
|
-
|
137
|
-
should_match(:minutes => 5, :hour => 10, :day => 25)
|
138
|
-
should_not_match(:minutes => 4, :hour => 10, :day => 25)
|
139
|
-
should_not_match(:minutes => 5, :hour => 11, :day => 25)
|
140
|
-
should_not_match(:minutes => 5, :hour => 10, :day => 26)
|
141
|
-
|
142
|
-
context "with an additional calendar_day entry of 26" do
|
143
|
-
setup do
|
144
|
-
@cron.set_calendar_days([25, 26])
|
145
|
-
end
|
146
|
-
|
147
|
-
should_match(:minutes => 5, :hour => 10, :day => 25)
|
148
|
-
should_match(:minutes => 5, :hour => 10, :day => 26)
|
149
|
-
|
150
|
-
should "raise an exception when setting a calendar_day is outside the acceptable range" do
|
151
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
152
|
-
@cron.set_calendar_days(-1)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
context "with a day entry of tuesday" do
|
158
|
-
setup do
|
159
|
-
@cron.set_days(:wednesday)
|
160
|
-
end
|
161
|
-
|
162
|
-
should "raise an exception when setting a calendar_day with a day" do
|
163
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
164
|
-
@cron.matches?(time_from_options(:minutes => 5, :hour => 10, :day => 25, :wday => 3))
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
should "raise an exception when setting an hour entry greater than 25" do
|
173
|
-
assert_raise ChronoTrigger::ConfigurationException do
|
174
|
-
@cron.set_hours(25)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end #A CronEntry, @cron,
|
178
|
-
|
179
|
-
|
180
|
-
private
|
181
|
-
def time_from_options(options={})
|
182
|
-
datetime = Time.utc(options[:year] || 2000,
|
183
|
-
options[:month] || "jan",
|
184
|
-
options[:day]||1,
|
185
|
-
options[:hour]||0,
|
186
|
-
options[:minutes]||0,
|
187
|
-
options[:second]||0)
|
188
|
-
|
189
|
-
if wday = options[:wday]
|
190
|
-
while datetime.wday != wday
|
191
|
-
datetime += 1.day
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
datetime
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|