startat 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +6 -0
- data/Manifest.txt +8 -0
- data/README.txt +68 -0
- data/Rakefile +12 -0
- data/examples/print-to-the-future.rb +70 -0
- data/examples/twitter-schedule-bot.rb +113 -0
- data/lib/startat.rb +121 -0
- data/test/test_startat.rb +142 -0
- metadata +106 -0
- metadata.gz.sig +3 -0
data.tar.gz.sig
ADDED
Binary file
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
= startat
|
2
|
+
|
3
|
+
* http://startat.rubyforge.org/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
StartAt is a simple class for future code execution. It is designed
|
8
|
+
to execute a block of code at a specific point in time in the future.
|
9
|
+
|
10
|
+
StartAt works by spawning a new thread, determining how long it must wait (in
|
11
|
+
seconds) until the future date and time is reached, calling sleep with the
|
12
|
+
exact number of seconds to wait, and then executing the code block.
|
13
|
+
|
14
|
+
StartAt was derived from a script written to post schedule information to
|
15
|
+
Twitter for a symposium. The schedule robot posted event details exactly
|
16
|
+
five minutes in advance of the event.
|
17
|
+
|
18
|
+
== FEATURES/PROBLEMS:
|
19
|
+
|
20
|
+
Issues:
|
21
|
+
* This is an alpha release. The developer is interested in feedback on the API.
|
22
|
+
* StartAt currently operates with whole seconds, not microseconds.
|
23
|
+
* A running (waiting) StartAt object can be interrupted early with SIGALARM.
|
24
|
+
* Thread safety issues have not been examined or addressed yet.
|
25
|
+
|
26
|
+
== SYNOPSIS:
|
27
|
+
|
28
|
+
require 'date'
|
29
|
+
require 'startat'
|
30
|
+
|
31
|
+
future_time = DateTime.parse("2010-03-24 03:34:45 PM EDT")
|
32
|
+
action = lambda { print "The time is now!" }
|
33
|
+
future_event = StartAt.new(future_time, &action)
|
34
|
+
future_event.start
|
35
|
+
future_event.join
|
36
|
+
|
37
|
+
== REQUIREMENTS:
|
38
|
+
|
39
|
+
* date
|
40
|
+
|
41
|
+
== INSTALL:
|
42
|
+
|
43
|
+
* sudo gem install startat
|
44
|
+
|
45
|
+
== LICENSE:
|
46
|
+
|
47
|
+
(The MIT License)
|
48
|
+
|
49
|
+
Copyright (c) 2009 Keith A. Watson <ikawnoclast@interocitry.com>
|
50
|
+
|
51
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
52
|
+
a copy of this software and associated documentation files (the
|
53
|
+
'Software'), to deal in the Software without restriction, including
|
54
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
55
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
56
|
+
permit persons to whom the Software is furnished to do so, subject to
|
57
|
+
the following conditions:
|
58
|
+
|
59
|
+
The above copyright notice and this permission notice shall be
|
60
|
+
included in all copies or substantial portions of the Software.
|
61
|
+
|
62
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
63
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
64
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
65
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
66
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
67
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
68
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.spec 'startat' do |p|
|
7
|
+
# self.rubyforge_name = 'startatx' # if different than 'startat'
|
8
|
+
p.developer('Keith Watson', 'ikawnoclast@interocitry.com')
|
9
|
+
p.remote_rdoc_dir = '' # Release to root
|
10
|
+
end
|
11
|
+
|
12
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#! /usr/bin/env ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# print-to-the-future.rb -- Schedules and prints messages in the future.
|
5
|
+
#
|
6
|
+
# Copyright (c) 2009 Keith A. Watson <ikawnoclast@interocitry.com>
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
# a copy of this software and associated documentation files (the
|
10
|
+
# 'Software'), to deal in the Software without restriction, including
|
11
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
13
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
14
|
+
# the following conditions:
|
15
|
+
#
|
16
|
+
# The above copyright notice and this permission notice shall be
|
17
|
+
# included in all copies or substantial portions of the Software.
|
18
|
+
#
|
19
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
22
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
23
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
24
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
25
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#
|
27
|
+
|
28
|
+
require 'date'
|
29
|
+
require 'startat'
|
30
|
+
|
31
|
+
# The following is borrowed from the _Ruby Cookbook_, Lucas Carlson & Leonard
|
32
|
+
# Richardson, O'Reilly Press, page 111.
|
33
|
+
# Time.to_datetime exists -- it's private though.
|
34
|
+
class Time
|
35
|
+
def to_datetime
|
36
|
+
seconds = sec + Rational(usec, 10**6)
|
37
|
+
offset = Rational(utc_offset, 60 * 60 * 24)
|
38
|
+
DateTime.new(year, month, day, hour, min, seconds, offset)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Time_format = "%b %e %H:%M:%S"
|
43
|
+
|
44
|
+
puts "Start time: #{DateTime.now.strftime(Time_format)}"
|
45
|
+
|
46
|
+
schedule = {
|
47
|
+
(Time.now + 5).to_datetime => "5 seconds later",
|
48
|
+
(Time.now + 10).to_datetime => "10 seconds later",
|
49
|
+
(Time.now + 33).to_datetime => "33 seconds later",
|
50
|
+
(Time.now + 1 * 60).to_datetime => "1 minute later",
|
51
|
+
(Time.now + 2 * 60).to_datetime => "2 minutes later and done."
|
52
|
+
}
|
53
|
+
|
54
|
+
scheduled_events = Array.new
|
55
|
+
|
56
|
+
schedule.each { | event_time, msg |
|
57
|
+
action = lambda {
|
58
|
+
puts "#{DateTime.now.strftime(Time_format)} #{msg}"
|
59
|
+
}
|
60
|
+
|
61
|
+
# create and start the scheduler for the future event
|
62
|
+
future_event = StartAt.new(event_time, &action)
|
63
|
+
future_event.start
|
64
|
+
|
65
|
+
# store the event
|
66
|
+
scheduled_events.push(future_event)
|
67
|
+
}
|
68
|
+
|
69
|
+
# wait out the longest thread
|
70
|
+
sleep 2 * 60 + 1
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/opt/local/bin/ruby -w
|
2
|
+
|
3
|
+
#
|
4
|
+
# twitter-schedule-bot.rb -- A code example for using StartAt for posting
|
5
|
+
# a schedule of events for an event to Twitter.
|
6
|
+
#
|
7
|
+
# Copyright (c) 2009 Keith A. Watson <ikawnoclast@interocitry.com>
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# 'Software'), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
23
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
24
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
25
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
26
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
|
29
|
+
#
|
30
|
+
# NOTE: This is an updated version of the code CERIAS used to post the
|
31
|
+
# schedule of events for the 20009 CERIAS Information Security Symposium.
|
32
|
+
# This code is missing the Twitter account details (username and password)
|
33
|
+
# and the events have already elapsed. This is only an example, but is
|
34
|
+
# easily updated for your own events.
|
35
|
+
#
|
36
|
+
|
37
|
+
require 'rubygems'
|
38
|
+
require 'twitter'
|
39
|
+
require 'date'
|
40
|
+
require 'startat'
|
41
|
+
|
42
|
+
# message added to the beginning and end of the post
|
43
|
+
header="CERIAS Symposium Schedule:"
|
44
|
+
footer="#cerias"
|
45
|
+
|
46
|
+
# amount of time before the actual event to post info (in seconds)
|
47
|
+
leadTime = 5 * 60;
|
48
|
+
|
49
|
+
# Twitter account object creation
|
50
|
+
postService = Twitter::Base.new('myTwitterAccount', 'myTwitterPassword')
|
51
|
+
|
52
|
+
# schedule array containing event times and messages
|
53
|
+
schedule = {
|
54
|
+
DateTime.parse('2009-03-24 09:30:00 AM EDT') \
|
55
|
+
=> "Registration and Coffee in LWSN Commons",
|
56
|
+
DateTime.parse('2009-03-24 10:30:00 AM EDT') \
|
57
|
+
=> "Welcome in LWSN 1142",
|
58
|
+
DateTime.parse('2009-03-24 10:35:00 AM EDT') \
|
59
|
+
=> "Panel #1: Transitive Security & Standards Adoption in LWSN 1142",
|
60
|
+
DateTime.parse('2009-03-24 12:15:00 PM EDT') \
|
61
|
+
=> "Lunch in LWSN 1190",
|
62
|
+
DateTime.parse('2009-03-24 01:45:00 PM EDT') \
|
63
|
+
=> "Panel #2: Security in the Cloud in LWSN 1142",
|
64
|
+
DateTime.parse('2009-03-24 03:30:00 PM EDT') \
|
65
|
+
=> "Fireside Chat in LWSN 1142",
|
66
|
+
DateTime.parse('2009-03-24 07:00:00 PM EDT') \
|
67
|
+
=> "John Thompson, CEO Symantec in STEW Fowler Hall",
|
68
|
+
DateTime.parse('2009-03-25 08:00:00 AM EDT') \
|
69
|
+
=> "Registration and Coffee in LWSN Commons",
|
70
|
+
DateTime.parse('2009-03-25 08:30:00 AM EDT') \
|
71
|
+
=> "Morning Keynote Address: Dr. Ronald Ritchey in LWSN 1142",
|
72
|
+
DateTime.parse('2009-03-25 09:45:00 AM EDT') \
|
73
|
+
=> "Panel #3: Unsecured Economies in LWSN 1142",
|
74
|
+
DateTime.parse('2009-03-25 11:15:00 AM EDT') \
|
75
|
+
=> "Tech Talk Poster Preview in LWSN 1142",
|
76
|
+
DateTime.parse('2009-03-25 12:30:00 PM EDT') \
|
77
|
+
=> "Lunch and Awards in LWSN 1142",
|
78
|
+
DateTime.parse('2009-03-25 02:00:00 PM EDT') \
|
79
|
+
=> "Poster Session & CERIAS Anniversary Cake in LWSN Commons",
|
80
|
+
DateTime.parse('2009-03-25 04:30:00 PM EDT') \
|
81
|
+
=> "CERIAS Seminar in PFEN 241",
|
82
|
+
}
|
83
|
+
|
84
|
+
schedEvents = Array.new
|
85
|
+
|
86
|
+
schedule.keys.each { | event |
|
87
|
+
# cacluate the time to post the message before the actual event
|
88
|
+
postTime = StartAt.get_time_before(event, leadTime)
|
89
|
+
# create the action of posting a message; pasted to the scheduler
|
90
|
+
action = lambda {
|
91
|
+
msg = "#{header} #{schedule[event]} starts at #{event.strftime("%l:%M")} #{footer}"
|
92
|
+
puts "#{DateTime.now.strftime("%b %e %H:%M:%S")} Posting the following message: \"#{msg}\" (length=#{msg.length})"
|
93
|
+
puts postService.post(msg).inspect
|
94
|
+
}
|
95
|
+
|
96
|
+
# create and start the scheduler for the future event
|
97
|
+
futureEvent = StartAt.new(postTime, &action)
|
98
|
+
futureEvent.start
|
99
|
+
|
100
|
+
# store the event
|
101
|
+
schedEvents.push(futureEvent)
|
102
|
+
}
|
103
|
+
|
104
|
+
# find the longest running thread and join it
|
105
|
+
longestEvent = StartAt.new(DateTime.now)
|
106
|
+
schedEvents.each { | eventPostTime |
|
107
|
+
if eventPostTime > longestEvent
|
108
|
+
longestEvent = eventPostTime
|
109
|
+
end
|
110
|
+
}
|
111
|
+
longestEvent.join
|
112
|
+
|
113
|
+
exit 0
|
data/lib/startat.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
#
|
4
|
+
# StartAt is class for executing code at a specific point in the future.
|
5
|
+
# The object is initialized with a #DateTime object with a point of time in the
|
6
|
+
# future and a code block to be executed. Calling #start will calculate the
|
7
|
+
# wait time (in seconds), initialize a #Thread that will then +sleep+ for the
|
8
|
+
# appropriate amount of time, and then the code block will be executed.
|
9
|
+
#
|
10
|
+
# For example:
|
11
|
+
#
|
12
|
+
# require 'date'
|
13
|
+
# require 'startat'
|
14
|
+
#
|
15
|
+
# bday = DateTime.parse("2009-08-20 08:30:00 AM EDT")
|
16
|
+
# action = lambda { puts "Happy Birthday, Dad!" }
|
17
|
+
# wait_for_it = StartAt.new(bday, &action)
|
18
|
+
# wait_for_it.start
|
19
|
+
# wait_for_it.join
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# StartAt was originally written to post schedule information for a symposium to
|
23
|
+
# Twitter (see the examples directory).
|
24
|
+
#
|
25
|
+
class StartAt
|
26
|
+
VERSION = '0.1.0'
|
27
|
+
include Comparable
|
28
|
+
|
29
|
+
Seconds_per_day = 24 * 60 * 60 # :nodoc:
|
30
|
+
|
31
|
+
attr_reader :start_datetime
|
32
|
+
|
33
|
+
# Returns a new future action object that will execute the code block
|
34
|
+
# at a point in the future as defined by #start_datetime.
|
35
|
+
def initialize(start_datetime, &block)
|
36
|
+
@thread_started = false
|
37
|
+
if start_datetime.class == DateTime
|
38
|
+
@start_datetime = start_datetime
|
39
|
+
else
|
40
|
+
raise ArgumentError.new("must have a DateTime object")
|
41
|
+
end
|
42
|
+
if block
|
43
|
+
@action = block
|
44
|
+
elsif block_given?
|
45
|
+
@action = lambda { yield }
|
46
|
+
else
|
47
|
+
raise ArgumentError.new("missing a block")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Compares the start #DateTime of the current future action with the
|
52
|
+
# start time of the #other future action.
|
53
|
+
def <=> (other)
|
54
|
+
@start_datetime <=> other.start_datetime
|
55
|
+
end
|
56
|
+
|
57
|
+
# Starts the future action thread by determining the time needed to wait
|
58
|
+
# based on the start date and then executes the code block once the time
|
59
|
+
# has elapsed.
|
60
|
+
def start
|
61
|
+
if ! @thread_started
|
62
|
+
@sleeper = Thread.new do
|
63
|
+
wait = get_wait_time()
|
64
|
+
# Check that the wait time is positive, otherwise the future
|
65
|
+
# event time has already past. We skip it, since a warning
|
66
|
+
# message was produced in wait_time.
|
67
|
+
if wait > 0
|
68
|
+
sleep wait
|
69
|
+
@action.call
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@thread_started = true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Cancels a running future action object thread.
|
77
|
+
def cancel
|
78
|
+
if ! @thread_started
|
79
|
+
@sleeper.exit
|
80
|
+
@thread_started = false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
alias kill cancel
|
85
|
+
|
86
|
+
# Returns the status of a future action object thread. See #Thread#status
|
87
|
+
def status
|
88
|
+
@sleeper.status
|
89
|
+
end
|
90
|
+
|
91
|
+
# Joins the current thread to the future action thread. See #Thread.join
|
92
|
+
def join
|
93
|
+
@sleeper.join
|
94
|
+
end
|
95
|
+
|
96
|
+
# Takes a future #DateTime object and returns a #DateTime object for the time
|
97
|
+
# before as specified in +time_before+ in seconds.
|
98
|
+
def self.get_time_before(future_datetime, time_before)
|
99
|
+
future_datetime - Rational(time_before, Seconds_per_day)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Takes a future #DateTime object and returns a #DateTime object for the time
|
103
|
+
# after as specified in +time_before+ in seconds.
|
104
|
+
def self.get_time_after(future_datetime, time_after)
|
105
|
+
future_datetime + Rational(time_after, Seconds_per_day)
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def get_wait_time
|
111
|
+
# 1. Find the difference between the time now and the future time.
|
112
|
+
# 2. DateTime objects store time in whole seconds, so convert to the
|
113
|
+
# number of seconds in a day and return an interger.
|
114
|
+
wait_time = (((@start_datetime - DateTime.now) * Seconds_per_day) + 1).to_i
|
115
|
+
# send a warning message if the wait time is negative since the future
|
116
|
+
# time is no in the past.
|
117
|
+
warn "Specified date and time of #{@start_datetime.to_s} has already elapsed" if wait_time <= 0
|
118
|
+
return wait_time
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "date"
|
3
|
+
require "startat"
|
4
|
+
|
5
|
+
# The following is borrowed from the _Ruby Cookbook_, Lucas Carlson & Leonard
|
6
|
+
# Richardson, O'Reilly Press, page 111.
|
7
|
+
# Time.to_datetime exists -- it's private though.
|
8
|
+
class Time
|
9
|
+
def to_datetime
|
10
|
+
seconds = sec + Rational(usec, 10**6)
|
11
|
+
offset = Rational(utc_offset, 60 * 60 * 24)
|
12
|
+
DateTime.new(year, month, day, hour, min, seconds, offset)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Unit test suite
|
17
|
+
class TestStartat < Test::Unit::TestCase
|
18
|
+
def test_initialization1
|
19
|
+
action = lambda { val = true }
|
20
|
+
test = nil
|
21
|
+
start = DateTime.now
|
22
|
+
assert_nothing_raised(ArgumentError) {
|
23
|
+
test = StartAt.new(start, &action)
|
24
|
+
}
|
25
|
+
assert_not_nil test
|
26
|
+
assert_equal(start, test.start_datetime)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_initialization2
|
30
|
+
test = nil
|
31
|
+
start = DateTime.now
|
32
|
+
assert_nothing_raised(ArgumentError) {
|
33
|
+
test = StartAt.new(start) { val = true }
|
34
|
+
}
|
35
|
+
assert_not_nil test
|
36
|
+
assert_same(start, test.start_datetime)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_initialization3
|
40
|
+
test = StartAt
|
41
|
+
assert_raise(ArgumentError) {
|
42
|
+
test.new(DateTime.now)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_initialization4
|
47
|
+
assert_raise(ArgumentError) {
|
48
|
+
test = StartAt.new(Time.now) { val = true }
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_initialization5
|
53
|
+
assert_raise(ArgumentError) {
|
54
|
+
test = StartAt.new("This should fail") { val = true }
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_start1
|
59
|
+
action = lambda { val = true }
|
60
|
+
test = StartAt.new((Time.now + 5).to_datetime, &action)
|
61
|
+
test.start
|
62
|
+
assert_equal("sleep", test.status)
|
63
|
+
test.cancel
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_stop1
|
67
|
+
action = lambda { val = true }
|
68
|
+
test = StartAt.new((Time.now + 5).to_datetime, &action)
|
69
|
+
test.start
|
70
|
+
test.cancel
|
71
|
+
assert(test.status)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_stop2
|
75
|
+
action = lambda { val = true }
|
76
|
+
test = StartAt.new((Time.now + 5).to_datetime, &action)
|
77
|
+
test.start
|
78
|
+
test.kill
|
79
|
+
assert(test.status)
|
80
|
+
end
|
81
|
+
|
82
|
+
def comparison1
|
83
|
+
test1 = StartAt
|
84
|
+
test2 = StartAt
|
85
|
+
test1.new((Time.now + 2).to_datetime) { val1 = true }
|
86
|
+
test2.new((Time.now + 3).to_datetime) { val2 = true }
|
87
|
+
assert_not_equal(test1, test2)
|
88
|
+
end
|
89
|
+
|
90
|
+
def comparison2
|
91
|
+
date_time = (Time.now + 2).to_datetime
|
92
|
+
test1 = StartAt.new(date_time) { val1 = true }
|
93
|
+
test2 = StartAt.new(date_time) { val2 = true }
|
94
|
+
assert_equal(test1, test2)
|
95
|
+
end
|
96
|
+
|
97
|
+
def comparison3
|
98
|
+
test1 = StartAt
|
99
|
+
test2 = StartAt
|
100
|
+
test1.new((Time.now + 2).to_datetime) { val1 = true }
|
101
|
+
test2.new((Time.now + 3).to_datetime) { val2 = true }
|
102
|
+
assert_operator(test1, :<, test2)
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_invalid_time1
|
106
|
+
action = lambda { val = true }
|
107
|
+
test = StartAt.new((Time.now - 2).to_datetime, &action)
|
108
|
+
test.start
|
109
|
+
assert(!test.status)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_timing1
|
113
|
+
val = true
|
114
|
+
test = StartAt.new((Time.now + 2).to_datetime) { val = false }
|
115
|
+
test.start
|
116
|
+
assert(val)
|
117
|
+
sleep 1
|
118
|
+
assert(val)
|
119
|
+
sleep 2
|
120
|
+
assert(!val)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_get_time_before1
|
124
|
+
time_now = Time.now
|
125
|
+
datetime_now = time_now.to_datetime
|
126
|
+
lead_times = [ 5, 30, 120, 3600, 43200, 86400, 604800 ]
|
127
|
+
lead_times.each { | lead_time |
|
128
|
+
time_before = StartAt.get_time_before(datetime_now, lead_time)
|
129
|
+
assert_equal((time_now - lead_time).to_datetime, time_before)
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_get_time_after1
|
134
|
+
time_now = Time.now
|
135
|
+
datetime_now = time_now.to_datetime
|
136
|
+
lead_times = [ 5, 30, 120, 3600, 43200, 86400, 604800 ]
|
137
|
+
lead_times.each { | lead_time |
|
138
|
+
time_before = StartAt.get_time_after(datetime_now, lead_time)
|
139
|
+
assert_equal((time_now + lead_time).to_datetime, time_before)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: startat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Keith Watson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDRDCCAiygAwIBAgIBADANBgkqhkiG9w0BAQUFADBIMRQwEgYDVQQDDAtpa2F3
|
14
|
+
bm9jbGFzdDEbMBkGCgmSJomT8ixkARkWC2ludGVyb2NpdHJ5MRMwEQYKCZImiZPy
|
15
|
+
LGQBGRYDY29tMB4XDTA5MDcyOTEzNTcxOFoXDTEwMDcyOTEzNTcxOFowSDEUMBIG
|
16
|
+
A1UEAwwLaWthd25vY2xhc3QxGzAZBgoJkiaJk/IsZAEZFgtpbnRlcm9jaXRyeTET
|
17
|
+
MBEGCgmSJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
18
|
+
ggEBAM6Pn7BiDNnNRWUQzmuPBr3vNgp9TIB1SrJ3a9c1iJY6sYaTZD4M+JUmMpfo
|
19
|
+
FzQGdAAASOY0aJpBaZdA13fEC7fCed7pr44sf5mxZv1cNsUrXJbw9ZNBQFHyouz6
|
20
|
+
YTvO7pd24F2/DmxngutwlgCZlXwvkB5mrEfNbqIof3UhYkvK7mHBEJ5AWlGQ1nft
|
21
|
+
K0x72nTi3ZIg0mufz2kMadWcwK8I7A74FuGdFbMcLmoXJb4DwxCKb3APnS8UzoYV
|
22
|
+
/y5ddiogWbSEi+coOY0t6BkkS0kAEPf1tW6UtWB8yI+/807kyP/BQ3sQ2IoBAi04
|
23
|
+
+khoWTEyqkYzWmH2zRYSuh5U7RMCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8E
|
24
|
+
BAMCBLAwHQYDVR0OBBYEFMSjxPX6jYn3js6kC8BqsQH1c3+VMA0GCSqGSIb3DQEB
|
25
|
+
BQUAA4IBAQCE9cS2qD2MGQ4mWHIQOeJk9y7mQle7L25Z3tuMVNG0tPwmnZlaXpeE
|
26
|
+
DSYDKVCCoJQCH6m0H5KX7Jns0Y3/4unzQzfGVEh1JNojay5f2sV4YwRO+33/Ymxd
|
27
|
+
hhqmbytCY5ie36XoOC0aayVulcqMaHfgUVAUPN3bBudPANpVdF79yqBhjuNuCl3R
|
28
|
+
AwTBaYaGwOoSCGMUpOh0F4ADlqFKlZZMlxr2WXQJD2cqfMnLuzKgoL5V/Hkes8OL
|
29
|
+
Wh0bTgBFH0mpCwLpypJgZiuM6xhz90YTH7WzLSz9itIx2Vhrvylvxq4ePPfuefPX
|
30
|
+
Ia165vMqyt5W7zBTANDsPWi6m1DiaDTD
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
|
33
|
+
date: 2009-07-29 00:00:00 -04:00
|
34
|
+
default_executable:
|
35
|
+
dependencies:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: hoe
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 2.3.2
|
45
|
+
version:
|
46
|
+
description: |-
|
47
|
+
StartAt is a simple class for future code execution. It is designed
|
48
|
+
to execute a block of code at a specific point in time in the future.
|
49
|
+
|
50
|
+
StartAt works by spawning a new thread, determining how long it must wait (in
|
51
|
+
seconds) until the future date and time is reached, calling sleep with the
|
52
|
+
exact number of seconds to wait, and then executing the code block.
|
53
|
+
|
54
|
+
StartAt was derived from a script written to post schedule information to
|
55
|
+
Twitter for a symposium. The schedule robot posted event details exactly
|
56
|
+
five minutes in advance of the event.
|
57
|
+
email:
|
58
|
+
- ikawnoclast@interocitry.com
|
59
|
+
executables: []
|
60
|
+
|
61
|
+
extensions: []
|
62
|
+
|
63
|
+
extra_rdoc_files:
|
64
|
+
- History.txt
|
65
|
+
- Manifest.txt
|
66
|
+
- README.txt
|
67
|
+
files:
|
68
|
+
- History.txt
|
69
|
+
- Manifest.txt
|
70
|
+
- README.txt
|
71
|
+
- Rakefile
|
72
|
+
- examples/print-to-the-future.rb
|
73
|
+
- examples/twitter-schedule-bot.rb
|
74
|
+
- lib/startat.rb
|
75
|
+
- test/test_startat.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://startat.rubyforge.org/
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --main
|
83
|
+
- README.txt
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project: startat
|
101
|
+
rubygems_version: 1.3.4
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: StartAt is a simple class for future code execution
|
105
|
+
test_files:
|
106
|
+
- test/test_startat.rb
|
metadata.gz.sig
ADDED