moment 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 ADDED
@@ -0,0 +1,5 @@
1
+ This is a scheduling system in pure ruby. It runs jobs at specified intervals.
2
+ It has a server component that you can interface with via DRb, which makes
3
+ admining it from a Rails environment easy.
4
+
5
+ See Moment for more documentation.
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/ruby
2
+
3
+ #
4
+ # == Synopsis
5
+ #
6
+ # moment_server: scheduling server, accessible via DRb
7
+ #
8
+ # $ moment_server -p 9901 saved_jobs
9
+ # Loading previous jobs from saved_jobs..
10
+ # Loaded 0 jobs:
11
+ # []
12
+ # Starting service on druby://localhost:9901 ...
13
+ # Ready.<Ctrl-C>
14
+ # Saving 0 jobs to saved_jobs..
15
+ # Exiting..
16
+ #
17
+ # == Usage
18
+ #
19
+ # moment_server [@options] [job_file]
20
+ #
21
+ # -h, --help:
22
+ # show help
23
+ #
24
+ # -p, --port: (default 9000)
25
+ # port to run druby instance on
26
+ #
27
+ # -q, --[no-]quiet: (default false)
28
+ # run verbosely
29
+ #
30
+ # job_file
31
+ # if specified, will save still running jobs in the given file on
32
+ # exit to be read in again on next start.
33
+ #
34
+
35
+ require 'rubygems'
36
+ require 'moment'
37
+ require 'drb'
38
+ require 'ostruct'
39
+ require 'optparse'
40
+ require 'rdoc/usage'
41
+
42
+ @options = OpenStruct.new :port => 9000, :quiet => false, :file => nil
43
+ OptionParser.new do |o|
44
+ o.on '-p', '--port [port]', Integer do |port| @options.port = port end
45
+ o.on '-q', '--[no-]quiet' do |quiet| @options.quiet = quiet end
46
+ o.on '-h', '--help' do
47
+ RDoc::usage
48
+ exit
49
+ end
50
+ end.order! do |file|
51
+ @options.file ||= file
52
+ end
53
+
54
+ def putsv(*args)
55
+ puts args unless @options.quiet
56
+ end
57
+
58
+ url = "druby://localhost:#{@options.port}"
59
+ server = Moment::Server.new
60
+
61
+ unless @options.file.nil? or !File.exists?(@options.file)
62
+ putsv "Loading previous jobs from #{@options.file}.."
63
+ server.load(File.open(@options.file))
64
+ putsv "Loaded #{server.jobs.size} jobs:", server.jobs.inspect
65
+ end
66
+
67
+ server.run
68
+
69
+ putsv "Starting service on #{url} ..."
70
+ DRb.start_service(url, server)
71
+ begin
72
+ putsv "Ready."
73
+ DRb.thread.join
74
+ rescue Interrupt => e
75
+ if @options.file
76
+ putsv "Saving #{server.jobs.size} jobs to #{@options.file}.."
77
+ server.dump(File.open(@options.file, 'w'))
78
+ end
79
+
80
+ putsv "Exiting.."
81
+ end
@@ -0,0 +1,24 @@
1
+ require 'drb'
2
+ require 'shell_job'
3
+ require 'simple_trigger'
4
+
5
+ class Printer
6
+
7
+ include DRbUndumped
8
+ attr_accessor :name, :counter
9
+
10
+ def initialize(name = nil)
11
+ self.name = name
12
+ self.counter = 0
13
+ end
14
+
15
+ def execute
16
+ puts self.inspect, Time.now
17
+ @counter += 1
18
+ end
19
+
20
+ end
21
+
22
+ DRb.start_service
23
+ scheduler = DRbObject.new(nil, 'druby://localhost:9000')
24
+ scheduler.schedule(Moment::ShellJob.new('ls'), Moment::SimpleTrigger.new(Time.now, Time.now + 10, 2))
@@ -0,0 +1,161 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+ require File.dirname(__FILE__) + '/trigger'
3
+
4
+ class Moment::CronTrigger < Moment::Trigger
5
+
6
+ attr_reader :sec, :min, :hour, :day, :month, :wday, :year, :cron_expr
7
+
8
+ def initialize(expr)
9
+ super()
10
+ self.cron_expr = expr
11
+ end
12
+
13
+ def cron_expr=(expr)
14
+ @cron_expr = expr
15
+ self.sec, self.min, self.hour, self.day, self.month, self.wday, self.year = @cron_expr.split(' ')
16
+ #puts inspect
17
+ end
18
+
19
+ def fire_time_after(time)
20
+ sec, min, hour, day, month, year, wday, yday, isdst, zone = time.to_a
21
+
22
+ loop do
23
+ # year
24
+ unless @year.nil? or @year.include?(year)
25
+ return nil if year > @year.max
26
+ year = @year.detect do |y| y > year end # next allowable year
27
+ end
28
+
29
+ # month
30
+ unless @month.include?(month)
31
+ # next allowable month
32
+ next_month = @month.detect(lambda { @month.min }) do |m| m > month end
33
+ # reset everything lower
34
+ day, hour, min, sec = @day.min, @hour.min, @min.min, @sec.min
35
+ # carry case
36
+ if next_month < month
37
+ month = next_month
38
+ year += 1
39
+ retry
40
+ end
41
+ month = next_month
42
+ end
43
+
44
+ # day
45
+ month_days = (1 .. month_days(year, month))
46
+ days = @day.select do |d| month_days === d end
47
+ unless days.include?(day)
48
+ next_day = days.detect(lambda { days.min }) do |d| d > day end
49
+ hour, min, sec = @hour.min, @min.min, @sec.min
50
+ if next_day.nil? or next_day < day
51
+ day = next_day.nil? ? @day.min : next_day
52
+ month += 1
53
+ retry
54
+ end
55
+ day = next_day
56
+ end
57
+
58
+ # hour
59
+ unless @hour.include?(hour)
60
+ next_hour = @hour.detect(lambda { @hour.min }) do |h| h > hour end
61
+ min, sec = @min.min, @sec.min
62
+ if next_hour < hour
63
+ hour = next_hour
64
+ day += 1
65
+ retry
66
+ end
67
+ hour = next_hour
68
+ end
69
+
70
+ # min
71
+ unless @min.include?(min)
72
+ next_min = @min.detect(lambda { @min.min }) do |m| m > min end
73
+ sec = @sec.min
74
+ if next_min < min
75
+ min = next_min
76
+ hour += 1
77
+ retry
78
+ end
79
+ min = next_min
80
+ end
81
+
82
+ # sec
83
+ unless @sec.include?(sec)
84
+ next_sec = @sec.detect(lambda { @sec.min }) do |s| s > sec end
85
+ if next_sec < sec
86
+ sec = next_sec
87
+ min += 1
88
+ retry
89
+ end
90
+ sec = next_sec
91
+ end
92
+
93
+ break
94
+ end
95
+
96
+ Time.local sec, min, hour, day, month, year, wday, yday, isdst, zone
97
+ end
98
+
99
+ # TODO: mimic attr_reader to define all of these
100
+ def sec=(sec)
101
+ @sec = parse_part(sec, 0 .. 59)
102
+ end
103
+
104
+ def min=(min)
105
+ @min = parse_part(min, 0 .. 59)
106
+ end
107
+
108
+ def hour=(hour)
109
+ @hour = parse_part(hour, 0 .. 23)
110
+ end
111
+
112
+ def day=(day)
113
+ @day = parse_part(day, 1 .. 31)
114
+ end
115
+
116
+ def month=(month)
117
+ @month = parse_part(month, 1 .. 12)
118
+ end
119
+
120
+ def year=(year)
121
+ @year = parse_part(year)
122
+ end
123
+
124
+ def wday=(wday)
125
+ @wday = parse_part(wday, 0 .. 6)
126
+ end
127
+
128
+ LeapYearMonthDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
129
+ CommonYearMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
130
+ private
131
+ def month_days(y, m)
132
+ if ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)
133
+ LeapYearMonthDays[m-1]
134
+ else
135
+ CommonYearMonthDays[m-1]
136
+ end
137
+ end
138
+
139
+ # 0-5,8,10; 0-5; *; */5
140
+ def parse_part(part, range=nil)
141
+ return range if part.nil? or part == '*' or part =~ /[*0]\/1/
142
+
143
+ r = Array.new
144
+ part.split(',').each do |p|
145
+ if p =~ /-/ # 0-5
146
+ r << Range.new(*p.scan(/\d+/)).to_a.map do |x| x.to_i end
147
+ elsif p =~ /(\*|\d+)\/(\d+)/ and not range.nil? # */5, 2/10
148
+ min = $1 == '*' ? 0 : $1.to_i
149
+ inc = $2.to_i
150
+ (min .. range.end).each_with_index do |x, i|
151
+ r << x if i % inc == 0
152
+ end
153
+ else
154
+ r << p.to_i
155
+ end
156
+ end
157
+
158
+ r.flatten
159
+ end
160
+
161
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+
3
+ class Moment::Job
4
+
5
+ def execute
6
+ puts "Implement me"
7
+ end
8
+
9
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # == Moment. a scheduler.
3
+ #
4
+ # Author:: visnup <visnux@swivel.com>
5
+ # Started:: 8 March 2006
6
+ # Copyright:: Copyright (c) 2006 Swivel, LLC
7
+ # License:: Ruby License
8
+ #
9
+ # i just ate some doritos and need to drink more water.
10
+ #
11
+ # === Introduction
12
+ #
13
+ # Moment is a scheduling service that handles running given jobs at specified
14
+ # intervals.
15
+ #
16
+ # === Features
17
+ #
18
+ # Built-in triggers include a cron implementation (CronTrigger) and simple
19
+ # reptition (SimpleTrigger). Users-written triggers are of course supported by
20
+ # extending Trigger. A Server is also provided that handles multithreading.
21
+ # There is also a seperate executable +moment_server+ that can be run and
22
+ # scheduled to via DRb. Built-in job types include a shell execution job
23
+ # (ShellJob), or you can extend Job.
24
+ #
25
+ # === Example
26
+ #
27
+ # require 'rubygems'
28
+ # require 'moment'
29
+ # require 'drb'
30
+ #
31
+ # class Printer
32
+ #
33
+ # include DRbUndumped
34
+ # attr_accessor :name, :counter
35
+ #
36
+ # def initialize(name = nil)
37
+ # self.name = name
38
+ # self.counter = 0
39
+ # end
40
+ #
41
+ # def execute
42
+ # puts self.inspect, Time.now
43
+ # @counter += 1
44
+ # end
45
+ #
46
+ # end
47
+ #
48
+ # DRb.start_service
49
+ # scheduler = DRbObject.new(nil, 'druby://localhost:9901')
50
+ # scheduler.schedule(Moment::ShellJob.new('ls'),
51
+ # Moment::SimpleTrigger.new(Time.now, Time.now + 10, 2)) # from now till 10 seconds from now, every 2 seconds
52
+ # scheduler.schedule(Moment::ShellJob.new('date'),
53
+ # Moment::CronTrigger.new('0 0/5 * * 3,5') # every 5 minutes during March and May
54
+ #
55
+ # === Server
56
+ #
57
+ # The above example assumes the included server +moment_server+ is running on
58
+ # port 9901. See the documentation on running +moment_server+.
59
+ #
60
+
61
+ module Moment
62
+ end
63
+
64
+ require File.dirname(__FILE__) + '/cron_trigger.rb'
65
+ require File.dirname(__FILE__) + '/job.rb'
66
+ require File.dirname(__FILE__) + '/scheduler.rb'
67
+ require File.dirname(__FILE__) + '/server.rb'
68
+ require File.dirname(__FILE__) + '/shell_job.rb'
69
+ require File.dirname(__FILE__) + '/simple_trigger.rb'
70
+ require File.dirname(__FILE__) + '/trigger.rb'
@@ -0,0 +1,78 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+
3
+ class Moment::Scheduler
4
+
5
+ Event = Struct.new(:job, :trigger)
6
+
7
+ attr_accessor :exit_on_idle
8
+ attr_reader :jobs
9
+
10
+ def initialize(exit_on_idle = true)
11
+ self.exit_on_idle = exit_on_idle
12
+ @jobs = Array.new
13
+ @now = Time.now
14
+ end
15
+
16
+ def schedule(job, trigger = nil)
17
+ job_id = @jobs.size
18
+ @jobs[job_id] = Event.new(job, trigger)
19
+
20
+ job_id
21
+ end
22
+
23
+ def unschedule(job_id)
24
+ @jobs.delete_at(job_id)
25
+ end
26
+
27
+ def clean_up
28
+ @jobs.delete_if do |entry| entry.trigger.fire_time_after(@now).nil? end
29
+ end
30
+
31
+ def run
32
+ loop do
33
+ # find the next firing triggers
34
+ todo = next_jobs
35
+ if todo.empty? # nothing to do, so we wait
36
+ break if @exit_on_idle
37
+ sleep
38
+ retry # check again if we're woken up
39
+ end
40
+
41
+ # sleep until the next event; if we get woken up, retry from the start
42
+ pause_time = todo.first.trigger.fire_time_after(@now) - Time.now
43
+ retry if (pause_time > 0 and sleep(pause_time) < pause_time)
44
+
45
+ todo.each do |entry|
46
+ next unless @jobs.index(entry) # make sure since we were sleeping
47
+ entry.job.execute rescue puts $!
48
+ end
49
+
50
+ # make sure next call to next_jobs won't return the exact same result
51
+ sleep 0.5 # TODO this is dangerous; could skip over some jobs
52
+ end
53
+ end
54
+
55
+ # find the next wakeup triggers in our job/trigger list
56
+ def next_jobs
57
+ @now = Time.now
58
+ earliest = nil
59
+ jobs = Array.new
60
+
61
+ @jobs.each do |entry|
62
+ job, trigger = entry.job, entry.trigger
63
+ time = trigger.fire_time_after(@now)
64
+ case
65
+ when time.nil?
66
+ # do nothing
67
+ when earliest.nil?, time < earliest
68
+ earliest = time
69
+ jobs = [ entry ]
70
+ when time == earliest
71
+ jobs << entry
72
+ end
73
+ end
74
+
75
+ jobs
76
+ end
77
+
78
+ end # class Moment::Scheduler
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+ require File.dirname(__FILE__) + '/scheduler'
3
+ require File.dirname(__FILE__) + '/trigger'
4
+ require File.dirname(__FILE__) + '/job'
5
+ require File.dirname(__FILE__) + '/shell_job'
6
+ require File.dirname(__FILE__) + '/simple_trigger'
7
+ require File.dirname(__FILE__) + '/cron_trigger'
8
+
9
+ class Moment::Server < Moment::Scheduler
10
+
11
+ def initialize
12
+ super(false) # don't exit on idle
13
+ @threads = Array.new
14
+ end
15
+
16
+ def run
17
+ @threads << Thread.new do
18
+ super
19
+ end
20
+ end
21
+
22
+ def schedule(job, trigger)
23
+ puts 'scheduling:', job.inspect, trigger.inspect
24
+ retval = super
25
+ @threads.each do |t| t.wakeup end
26
+ retval
27
+ end
28
+
29
+ def dump(file)
30
+ file << Marshal.dump(@jobs)
31
+ end
32
+
33
+ def load(file)
34
+ @jobs = Marshal.load(file.read)
35
+ rescue ArgumentError
36
+ # ignore if the file is empty
37
+ end
38
+
39
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/job'
2
+
3
+ class Moment::ShellJob < Moment::Job
4
+
5
+ attr_accessor :command
6
+ attr_reader :last_result
7
+
8
+ def initialize(command)
9
+ self.command = command
10
+ @last_result = nil
11
+ end
12
+
13
+ def execute
14
+ @last_result = `#{command}`
15
+ end
16
+
17
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+ require File.dirname(__FILE__) + '/trigger'
3
+
4
+ class Moment::SimpleTrigger < Moment::Trigger
5
+
6
+ attr_accessor :start_time, :end_time, :repeat_interval
7
+
8
+ def initialize(start_time = nil, end_time = nil, repeat_interval = nil)
9
+ super()
10
+ self.start_time = start_time
11
+ self.end_time = end_time
12
+ self.repeat_interval = repeat_interval
13
+ end
14
+
15
+ def fire_time_after(time)
16
+ @start_time = time if not @start_time
17
+
18
+ case
19
+ when end_time && time > end_time
20
+ nil
21
+ when time < start_time
22
+ start_time
23
+ when repeat_interval != nil && repeat_interval > 0
24
+ time + repeat_interval - ((time - start_time) % repeat_interval)
25
+ else
26
+ nil
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/moment'
2
+
3
+ class Moment::Trigger
4
+
5
+ def fire_time_after(time)
6
+ nil
7
+ end
8
+
9
+ end
@@ -0,0 +1,105 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/cron_trigger'
4
+
5
+ class CronTriggerTest < Test::Unit::TestCase
6
+
7
+ def test_create
8
+ expr = '0 0 0 * *'
9
+ cron = Moment::CronTrigger.new(expr)
10
+ assert_equal(expr, cron.cron_expr)
11
+ end
12
+
13
+ def test_explicit_no_carry
14
+ input = Time.local(0, 0, 0, 1, 1, 2004, nil, nil, false, 'UTC')
15
+ cron = Moment::CronTrigger.new('10 5 4 3 2')
16
+ expected = Time.local(10, 5, 4, 3, 2, 2004, nil, nil, false, 'UTC')
17
+ assert_equal(expected, cron.fire_time_after(input))
18
+ end
19
+
20
+ def test_explicit_no_carry_day
21
+ input = Time.local(0, 0, 0, 1, 1, 2004, nil, nil, false, 'UTC')
22
+ cron = Moment::CronTrigger.new('10 5 4 3 1')
23
+ expected = Time.local(10, 5, 4, 3, 1, 2004, nil, nil, false, 'UTC')
24
+ assert_equal(expected, cron.fire_time_after(input))
25
+ end
26
+
27
+ def test_explicit_no_carry_hour
28
+ input = Time.local(0, 0, 0, 1, 1, 2004, nil, nil, false, 'UTC')
29
+ cron = Moment::CronTrigger.new('10 5 4 1 1')
30
+ expected = Time.local(10, 5, 4, 1, 1, 2004, nil, nil, false, 'UTC')
31
+ assert_equal(expected, cron.fire_time_after(input))
32
+ end
33
+
34
+ def test_explicit_no_carry_min
35
+ input = Time.local(0, 0, 0, 1, 1, 2004, nil, nil, false, 'UTC')
36
+ cron = Moment::CronTrigger.new('10 5 0 1 1')
37
+ expected = Time.local(10, 5, 0, 1, 1, 2004, nil, nil, false, 'UTC')
38
+ assert_equal(expected, cron.fire_time_after(input))
39
+ end
40
+
41
+ def test_explicit_with_carry
42
+ input = Time.local(0, 1, 23, 1, 1, 2004, nil, nil, false, 'UTC')
43
+ cron = Moment::CronTrigger.new('0 0 23 1 1')
44
+ expected = Time.local(0, 0, 23, 1, 1, 2005, nil, nil, false, 'UTC')
45
+ assert_equal(expected, cron.fire_time_after(input))
46
+ end
47
+
48
+ def test_implicit_with_carry
49
+ input = Time.local(0, 1, 23, 1, 1, 2004, nil, nil, false, 'UTC')
50
+ cron = Moment::CronTrigger.new('0 0 * * *')
51
+ expected = Time.local(0, 0, 0, 2, 1, 2004, nil, nil, false, 'UTC')
52
+ assert_equal(expected, cron.fire_time_after(input))
53
+ end
54
+
55
+ def test_year_before
56
+ input = Time.local(0, 1, 23, 1, 1, 2004, nil, nil, false, 'UTC')
57
+ cron = Moment::CronTrigger.new('0 15 10 * * * 2003')
58
+ expected = nil
59
+ assert_equal(expected, cron.fire_time_after(input))
60
+ end
61
+
62
+ def test_year_after
63
+ input = Time.local(0, 1, 23, 1, 1, 2004, nil, nil, false, 'UTC')
64
+ cron = Moment::CronTrigger.new('0 15 10 * * * 2005')
65
+ expected = Time.local(0, 15, 10, 2, 1, 2005, nil, nil, false, 'UTC')
66
+ assert_equal(expected, cron.fire_time_after(input))
67
+ end
68
+
69
+ def test_month_over
70
+ input = Time.local(0, 1, 23, 2, 12, 2004, nil, nil, false, 'UTC')
71
+ cron = Moment::CronTrigger.new('0 15 10 1 * * *')
72
+ expected = Time.local(0, 15, 10, 1, 1, 2005, nil, nil, false, 'UTC')
73
+ assert_equal(expected, cron.fire_time_after(input))
74
+ end
75
+
76
+ def test_day_over
77
+ input = Time.local(0, 1, 23, 30, 11, 2004, nil, nil, false, 'UTC')
78
+ cron = Moment::CronTrigger.new('* * * 31 * * *')
79
+ expected = Time.local(0, 0, 0, 31, 12, 2004, nil, nil, false, 'UTC')
80
+ assert_equal(expected, cron.fire_time_after(input))
81
+ end
82
+
83
+ def test_min_over
84
+ input = Time.local(2, 59, 12, 30, 11, 2004, nil, nil, false, 'UTC')
85
+ cron = Moment::CronTrigger.new('1 * * * * * *')
86
+ expected = Time.local(1, 0, 13, 30, 11, 2004, nil, nil, false, 'UTC')
87
+ assert_equal(expected, cron.fire_time_after(input))
88
+ end
89
+
90
+ def test_range
91
+ input0 = Time.local(2, 59, 12, 30, 11, 2004, nil, nil, false, 'UTC')
92
+ cron = Moment::CronTrigger.new('28/5,59 1-4,6,20 */1 * 5,0/1 * *')
93
+ assert_equal([28,33,38,43,48,53,58,59], cron.sec)
94
+ assert_equal([1,2,3,4,6,20], cron.min)
95
+ assert_equal((0 .. 23), cron.hour)
96
+ assert_equal((1 .. 12), cron.month)
97
+
98
+ expected0 = Time.local(28, 1, 13, 30, 11, 2004, nil, nil, false, 'UTC')
99
+ input1 = Time.local(29, 1, 13, 30, 11, 2004, nil, nil, false, 'UTC')
100
+ expected1 = Time.local(33, 1, 13, 30, 11, 2004, nil, nil, false, 'UTC')
101
+ assert_equal(expected0, cron.fire_time_after(input0))
102
+ assert_equal(expected1, cron.fire_time_after(input1))
103
+ end
104
+
105
+ end
@@ -0,0 +1,13 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/job'
4
+
5
+ class JobTest < Test::Unit::TestCase
6
+
7
+ def test_execute
8
+ job = Moment::Job.new
9
+ assert_respond_to(job, :execute)
10
+ job.execute
11
+ end
12
+
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ require File.dirname(__FILE__) + '/cron_trigger_test'
4
+ require File.dirname(__FILE__) + '/job_test'
5
+ require File.dirname(__FILE__) + '/scheduler_test'
6
+ require File.dirname(__FILE__) + '/server_test'
7
+ require File.dirname(__FILE__) + '/shell_job_test'
8
+ require File.dirname(__FILE__) + '/simple_trigger_test'
9
+ require File.dirname(__FILE__) + '/trigger_test'
@@ -0,0 +1,64 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/scheduler'
4
+ require File.dirname(__FILE__) + '/../lib/simple_trigger'
5
+ require File.dirname(__FILE__) + '/../lib/job'
6
+
7
+ class SchedulerTest < Test::Unit::TestCase
8
+
9
+ def test_initialize
10
+ scheduler = Moment::Scheduler.new
11
+ assert_equal([], scheduler.jobs)
12
+ end
13
+
14
+ class CountExecuted < Moment::Job
15
+ attr_reader :executed
16
+ def initialize
17
+ @executed = 0
18
+ end
19
+ def execute
20
+ @executed += 1
21
+ end
22
+ end
23
+
24
+ def test_schedule
25
+ scheduler = Moment::Scheduler.new
26
+ simple = Moment::SimpleTrigger.new(Time.now+1, Time.now+2, nil)
27
+ job = CountExecuted.new
28
+
29
+ scheduler.schedule(job, simple)
30
+ scheduler.schedule(job, simple)
31
+ scheduler.run
32
+
33
+ assert_not_equal(0, job.executed)
34
+ end
35
+
36
+ def test_unschedule
37
+ scheduler = Moment::Scheduler.new
38
+ simple = Moment::SimpleTrigger.new(nil, nil, 1)
39
+ job = CountExecuted.new
40
+
41
+ job_id = scheduler.schedule(job, simple)
42
+
43
+ assert_equal(1, scheduler.jobs.size)
44
+ assert_equal(Moment::Scheduler::Event.new(job, simple),
45
+ scheduler.unschedule(job_id))
46
+ assert_equal(0, scheduler.jobs.size)
47
+ end
48
+
49
+ def test_cleanup
50
+ scheduler = Moment::Scheduler.new
51
+ dead = Moment::SimpleTrigger.new(nil, Time.now-2, 1)
52
+ alive = Moment::SimpleTrigger.new(nil, nil, 1)
53
+
54
+ scheduler.schedule(Moment::Job.new, dead)
55
+ scheduler.schedule(Moment::Job.new, alive)
56
+
57
+ scheduler.clean_up
58
+
59
+ assert_equal(1, scheduler.jobs.size)
60
+ assert_equal(alive, scheduler.jobs.first[:trigger])
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,53 @@
1
+
2
+ require 'test/unit'
3
+ require 'stringio'
4
+ require File.dirname(__FILE__) + '/../lib/server'
5
+ require File.dirname(__FILE__) + '/../lib/simple_trigger'
6
+ require File.dirname(__FILE__) + '/../lib/job'
7
+
8
+ class ServerTest < Test::Unit::TestCase
9
+
10
+ def test_initialize
11
+ server = Moment::Server.new
12
+ assert_equal([], server.jobs)
13
+ end
14
+
15
+ class CountExecuted < Moment::Job
16
+ attr_reader :executed
17
+ def initialize
18
+ @executed = 0
19
+ end
20
+ def execute
21
+ @executed += 1
22
+ end
23
+ end
24
+
25
+ def test_threading
26
+ scheduler = Moment::Server.new
27
+ simple = Moment::SimpleTrigger.new(Time.now, nil, 1)
28
+ job = CountExecuted.new
29
+
30
+ scheduler.run # start running first
31
+ scheduler.schedule(job, simple)
32
+ sleep 3 # make sure to wait for the job to run at least once
33
+
34
+ assert_not_equal(0, job.executed)
35
+ end
36
+
37
+ def test_serialization
38
+ scheduler = Moment::Server.new
39
+ scheduler.schedule(CountExecuted.new, Moment::SimpleTrigger.new(nil, nil, 1))
40
+ file = StringIO.new
41
+ scheduler.dump(file)
42
+ assert_not_equal(0, file.size)
43
+ old_jobs = scheduler.jobs
44
+ assert_not_equal(0, scheduler.jobs.size)
45
+
46
+ file.rewind
47
+ scheduler = Moment::Server.new
48
+ assert_equal(0, scheduler.jobs.size)
49
+ scheduler.load(file)
50
+ assert_equal(old_jobs.size, scheduler.jobs.size)
51
+ end
52
+
53
+ end
@@ -0,0 +1,19 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/shell_job'
4
+
5
+ class ShellJobTest < Test::Unit::TestCase
6
+
7
+ def test_initialize
8
+ job = Moment::ShellJob.new('cmd')
9
+ assert_not_nil(job)
10
+ assert_equal('cmd', job.command)
11
+ end
12
+
13
+ def test_execute
14
+ job = Moment::ShellJob.new('ls')
15
+ job.execute
16
+ assert_equal(`ls`, job.last_result)
17
+ end
18
+
19
+ end
@@ -0,0 +1,16 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/simple_trigger'
4
+
5
+ class SimpleTriggerTest < Test::Unit::TestCase
6
+
7
+ def test_create
8
+ trig = Moment::SimpleTrigger.new
9
+ assert_not_nil(trig)
10
+ end
11
+
12
+ def test_start_time
13
+ trig = Moment::SimpleTrigger.new(nil, Time.now, nil)
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/trigger'
4
+
5
+ class CronTriggerTest < Test::Unit::TestCase
6
+
7
+ def test_fire_time_after
8
+ assert_nil(Moment::Trigger.new.fire_time_after(Time.now))
9
+ end
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: moment
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-04-25 00:00:00 -07:00
8
+ summary: A scheduler (like cron) that will run specified ruby code at given intervals or times
9
+ require_paths:
10
+ - lib
11
+ email: visnux@swivel.com
12
+ homepage: http://visnup.com
13
+ rubyforge_project:
14
+ description:
15
+ autorequire: moment
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Visnu Pitiyanuvath
30
+ files:
31
+ - bin/moment_server
32
+ - lib/server.rb
33
+ - lib/client.rb
34
+ - lib/trigger.rb
35
+ - lib/shell_job.rb
36
+ - lib/moment.rb
37
+ - lib/cron_trigger.rb
38
+ - lib/simple_trigger.rb
39
+ - lib/scheduler.rb
40
+ - lib/job.rb
41
+ - test/server_test.rb
42
+ - test/simple_trigger_test.rb
43
+ - test/scheduler_test.rb
44
+ - test/job_test.rb
45
+ - test/cron_trigger_test.rb
46
+ - test/trigger_test.rb
47
+ - test/shell_job_test.rb
48
+ - test/moment_test.rb
49
+ - README
50
+ test_files:
51
+ - test/moment_test.rb
52
+ rdoc_options: []
53
+
54
+ extra_rdoc_files:
55
+ - README
56
+ - bin/moment_server
57
+ executables:
58
+ - moment_server
59
+ extensions: []
60
+
61
+ requirements: []
62
+
63
+ dependencies: []
64
+