startat 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.
Binary file
@@ -0,0 +1,6 @@
1
+ === 0.1.0 / 2009-07-29
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ examples/print-to-the-future.rb
6
+ examples/twitter-schedule-bot.rb
7
+ lib/startat.rb
8
+ test/test_startat.rb
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ bʼw�!>[�7���
2
+ r���Sb��m)C��?N��$�- �L��%�T6(SVU��[wy���f�v��
3
+ �W0���b ���X�ʟ�H@@^�⠍�����d�%䀫q����sB-@����?t������ۇ�S��!*���m�h� gb�:�"3��x�*���Lc��P��ي�'\��x5ʹC���6����0�� 3 �uz}������6X�3�ܫ���]��� D��,��vDN{B�サ�0�X�q�