em-timers 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fd2dd3c97deb96f7c3d9f2681bc9d7a8f03e5431
4
+ data.tar.gz: aa73bcc16297daddf064b76ec2cb84815d3d7f10
5
+ SHA512:
6
+ metadata.gz: 41c3dc4a4a28c1e8dcc790b5a7410e69d02bb18a08da38dbdb4e2f172987b8f3d5b6265e52070aa605245fb7eb8ce2fce414621d825a4d5071a4cde273550456
7
+ data.tar.gz: 42a47c94053fef639e74d7d952dce3a217a98ceff71f65e1231f5a56d5384481c29983435a8303e9f3b6c26525195388aee25c11bf8d2f4aff4e5c5fb637a6eb
data/README ADDED
@@ -0,0 +1,39 @@
1
+ em-timers
2
+
3
+ helper methods for timers in EventMachine
4
+
5
+ examples:
6
+
7
+ # Once per hour, starting now. Note the :now symbol must be passed for your block to be called immediately.
8
+ EM::Timers.do_hourly(:starting => :now) { puts "drink a beer" }
9
+
10
+ # Once per day, starting in 1 day.
11
+ EM::Timers.do_daily { puts "take a shower" }
12
+
13
+ # Once per week, starting in 10 hours
14
+ EM::Timers.do_weekly(:starting => 10.hours.from_now) { puts "take out the garbage" }
15
+
16
+ # Once per month, starting at this time tomorrow.
17
+ EM::Timers.do_monthly(:starting => 1.day.from_now) { puts "pay the bills" }
18
+
19
+ # Or you can simply pass a number of seconds til starting
20
+ EM::Timers.do_monthly(:starting => 2592000) { puts "pay the bills, in seconds!" }
21
+
22
+ # Leveraging the Chronic time parsing library. em-timers knows if you have required
23
+ # Chronic and uses it to parse strings if you have. Otherwise, it uses Time.parse.
24
+ #
25
+ # Every 2 hours, starting next Tuesday at 9AM.
26
+ require 'chronic'
27
+ EM::Timers.do(:every => 2.hours, :starting => 'next tuesday at 9am') { puts "go pee" }
28
+
29
+ # You can add up units of time as well
30
+ EM::Timers.do(:every => 1.minutes + 3.seconds) { puts "hi!" }
31
+
32
+ # You can list and cancel timers, too
33
+ EM::Timers.list.each { |timer|
34
+ timer.cancel
35
+ }
36
+
37
+
38
+ jakecdouglas@gmail.com
39
+ yakischloba on Freenode #eventmachine
data/lib/em-timers.rb ADDED
@@ -0,0 +1,8 @@
1
+ $:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
2
+ require 'eventmachine'
3
+ require 'time'
4
+ require 'em-timers/em-timers'
5
+ require 'em-timers/numericmixable'
6
+ require 'em-timers/cron_line'
7
+
8
+ Numeric.send :include, NumericMixable
@@ -0,0 +1,251 @@
1
+ #--
2
+ # Copyright (c) 2006-2009, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ module EventMachine
27
+
28
+ module Timers
29
+
30
+ #
31
+ # A 'cron line' is a line in the sense of a crontab
32
+ # (man 5 crontab) file line.
33
+ #
34
+ class CronLine
35
+
36
+ #
37
+ # The string used for creating this cronline instance.
38
+ #
39
+ attr_reader :original
40
+
41
+ attr_reader \
42
+ :seconds,
43
+ :minutes,
44
+ :hours,
45
+ :days,
46
+ :months,
47
+ :weekdays
48
+
49
+ def initialize (line)
50
+
51
+ super()
52
+
53
+ @original = line
54
+
55
+ items = line.split
56
+
57
+ unless items.length == 5 or items.length == 6
58
+ raise(
59
+ "cron '#{line}' string should hold 5 or 6 items, not #{items.length}")
60
+ end
61
+
62
+ offset = items.length - 5
63
+
64
+ @seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ]
65
+ @minutes = parse_item(items[0 + offset], 0, 59)
66
+ @hours = parse_item(items[1 + offset], 0, 24)
67
+ @days = parse_item(items[2 + offset], 1, 31)
68
+ @months = parse_item(items[3 + offset], 1, 12)
69
+ @weekdays = parse_weekdays(items[4 + offset])
70
+ end
71
+
72
+ #
73
+ # Returns true if the given time matches this cron line.
74
+ #
75
+ def matches? (time)
76
+
77
+ time = Time.at(time) unless time.kind_of?(Time)
78
+
79
+ return false unless sub_match?(time.sec, @seconds)
80
+ return false unless sub_match?(time.min, @minutes)
81
+ return false unless sub_match?(time.hour, @hours)
82
+ return false unless sub_match?(time.day, @days)
83
+ return false unless sub_match?(time.month, @months)
84
+ return false unless sub_match?(time.wday, @weekdays)
85
+ true
86
+ end
87
+
88
+ #
89
+ # Returns an array of 6 arrays (seconds, minutes, hours, days,
90
+ # months, weekdays).
91
+ # This method is used by the cronline unit tests.
92
+ #
93
+ def to_array
94
+
95
+ [ @seconds, @minutes, @hours, @days, @months, @weekdays ]
96
+ end
97
+
98
+ #
99
+ # Returns the next time that this cron line is supposed to 'fire'
100
+ #
101
+ # This is raw, 3 secs to iterate over 1 year on my macbook :( brutal.
102
+ #
103
+ # This method accepts an optional Time parameter. It's the starting point
104
+ # for the 'search'. By default, it's Time.now
105
+ #
106
+ # Note that the time instance returned will be in the same time zone that
107
+ # the given start point Time (thus a result in the local time zone will
108
+ # be passed if no start time is specified (search start time set to
109
+ # Time.now))
110
+ #
111
+ # >> EventMachine::Timers::CronLine.new('30 7 * * *').next_time( Time.mktime(2008,10,24,7,29) )
112
+ # => Fri Oct 24 07:30:00 -0500 2008
113
+ #
114
+ # >> EventMachine::Timers::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) )
115
+ # => Fri Oct 24 07:30:00 UTC 2008
116
+ #
117
+ # >> EventMachine::Timers::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) ).localtime
118
+ # => Fri Oct 24 02:30:00 -0500 2008
119
+ #
120
+ # (Thanks to K Liu for the note and the examples)
121
+ #
122
+ def next_time (time=Time.now)
123
+
124
+ time -= time.usec * 1e-6
125
+ time += 1
126
+
127
+ loop do
128
+
129
+ unless date_match?(time)
130
+ time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
131
+ next
132
+ end
133
+
134
+ unless sub_match?(time.hour, @hours)
135
+ time += (60 - time.min) * 60 - time.sec
136
+ next
137
+ end
138
+
139
+ unless sub_match?(time.min, @minutes)
140
+ time += 60 - time.sec
141
+ next
142
+ end
143
+
144
+ unless sub_match?(time.sec, @seconds)
145
+ time += 1
146
+ next
147
+ end
148
+
149
+ break
150
+ end
151
+
152
+ time
153
+ end
154
+
155
+ private
156
+
157
+ WDS = %w[ sun mon tue wed thu fri sat ]
158
+ #
159
+ # used by parse_weekday()
160
+
161
+ def parse_weekdays (item)
162
+
163
+ item = item.downcase
164
+
165
+ WDS.each_with_index { |day, index| item = item.gsub(day, index.to_s) }
166
+
167
+ r = parse_item(item, 0, 7)
168
+
169
+ r.is_a?(Array) ?
170
+ r.collect { |e| e == 7 ? 0 : e }.uniq :
171
+ r
172
+ end
173
+
174
+ def parse_item (item, min, max)
175
+
176
+ return nil if item == '*'
177
+ return parse_list(item, min, max) if item.index(',')
178
+ return parse_range(item, min, max) if item.index('*') or item.index('-')
179
+
180
+ i = Integer(item)
181
+
182
+ i = min if i < min
183
+ i = max if i > max
184
+
185
+ [ i ]
186
+ end
187
+
188
+ def parse_list (item, min, max)
189
+
190
+ item.split(',').inject([]) { |r, i|
191
+ r.push(parse_range(i, min, max))
192
+ }.flatten
193
+ end
194
+
195
+ def parse_range (item, min, max)
196
+
197
+ i = item.index('-')
198
+ j = item.index('/')
199
+
200
+ return item.to_i if (not i and not j)
201
+
202
+ inc = j ? Integer(item[j+1..-1]) : 1
203
+
204
+ istart = -1
205
+ iend = -1
206
+
207
+ if i
208
+
209
+ istart = Integer(item[0..i - 1])
210
+
211
+ if j
212
+ iend = Integer(item[i + 1..j])
213
+ else
214
+ iend = Integer(item[i + 1..-1])
215
+ end
216
+
217
+ else # case */x
218
+
219
+ istart = min
220
+ iend = max
221
+ end
222
+
223
+ istart = min if istart < min
224
+ iend = max if iend > max
225
+
226
+ result = []
227
+
228
+ value = istart
229
+ loop do
230
+ result << value
231
+ value = value + inc
232
+ break if value > iend
233
+ end
234
+
235
+ result
236
+ end
237
+
238
+ def sub_match?(value, values)
239
+ values.nil? || values.include?(value)
240
+ end
241
+
242
+ def date_match?(date)
243
+ return false unless sub_match?(date.day, @days)
244
+ return false unless sub_match?(date.month, @months)
245
+ return false unless sub_match?(date.wday, @weekdays)
246
+ true
247
+ end
248
+ end
249
+
250
+ end
251
+ end
@@ -0,0 +1,164 @@
1
+ module EventMachine
2
+
3
+ module Timers
4
+
5
+ class Timer
6
+ @list = []
7
+ class << self
8
+ attr :list, false
9
+ end
10
+ attr :tag, false
11
+ attr :name, false
12
+ attr :options, true
13
+ attr :reschedule_timer, false
14
+ attr :kickoff_timer, false
15
+ def initialize(options, block)
16
+ @options = options
17
+ if options[:tag]
18
+ @tag = options[:tag]
19
+ end
20
+ if options[:name]
21
+ @name = options[:name]
22
+ end
23
+ @block = block
24
+ @repeats = true
25
+ Timer.list << self
26
+ end
27
+ def cancel
28
+ if @kickoff_timer
29
+ EM::cancel_timer(@kickoff_timer)
30
+ @kickoff_timer = nil
31
+ end
32
+ if @reschedule_timer
33
+ EM::cancel_timer(@reschedule_timer)
34
+ @reschedule_timer = nil
35
+ end
36
+ Timer.list.delete(self)
37
+ end
38
+ def schedule
39
+ increment = @options[:every] || 1
40
+ starting = @options[:starting]
41
+
42
+ if options[:cron]
43
+ starting =
44
+ EventMachine::Timers::CronLine.new(options[:cron]).next_time
45
+ @kickoff_timer = EM.add_timer(calculate_delta_time(starting)) {
46
+ @kickoff_timer = nil
47
+ run_and_reschedule_cron
48
+ }
49
+ return self
50
+ elsif @options[:at] || @options[:in]
51
+ @repeats = false
52
+ if @options[:at]
53
+ starting = parse_time(@options[:at])
54
+ else
55
+ starting = Time.now + parse_time(@options[:in])
56
+ end
57
+ end
58
+
59
+ if starting == :now
60
+ run_and_reschedule(increment)
61
+ return self
62
+ else
63
+ starting = parse_time(starting)
64
+ end
65
+
66
+ time = calculate_delta_time(starting)
67
+
68
+ @kickoff_timer = EM.add_timer(time) {
69
+ @kickoff_timer = nil
70
+ run_and_reschedule(increment)
71
+ }
72
+ self
73
+ end
74
+
75
+ protected
76
+
77
+ def calculate_delta_time(the_time)
78
+ increment = @options[:every] || 1
79
+ if the_time.is_a?(Time)
80
+ time = the_time - Time.now
81
+ else
82
+ time = the_time
83
+ end
84
+ while time < 0
85
+ time += increment
86
+ end
87
+ time
88
+ end
89
+
90
+ def parse_time(some_time)
91
+ if some_time.is_a?(String)
92
+ Object.const_defined?("Chronic") ?
93
+ Chronic.parse(some_time) :
94
+ Time.parse(some_time)
95
+ elsif some_time.is_a?(Time) || some_time.kind_of?(Numeric)
96
+ some_time
97
+ else
98
+ Time.now
99
+ end
100
+ end
101
+
102
+ def run_and_reschedule_cron
103
+ starting =
104
+ EventMachine::Timers::CronLine.new(options[:cron]).next_time
105
+ @reschedule_timer = EM.add_timer(calculate_delta_time(starting)) {
106
+ run_and_reschedule_cron
107
+ }
108
+ @block.call
109
+ end
110
+
111
+ def run_and_reschedule(inc)
112
+ if @repeats
113
+ @reschedule_timer = EM.add_timer(inc) {
114
+ run_and_reschedule(inc)
115
+ }
116
+ end
117
+ @block.call
118
+ end
119
+
120
+ end
121
+
122
+ def self.do(options={}, &blk)
123
+ Timer.new(options, blk).schedule
124
+ end
125
+
126
+ def self.do_hourly(options={}, &blk)
127
+ self.do({:every => 1.hour}.merge(options), &blk)
128
+ end
129
+
130
+ def self.do_daily(options={}, &blk)
131
+ self.do({:every => 1.day}.merge(options), &blk)
132
+ end
133
+
134
+ def self.do_weekly(options={}, &blk)
135
+ self.do({:every => 1.week}.merge(options), &blk)
136
+ end
137
+
138
+ def self.do_monthly(options={}, &blk)
139
+ self.do({:every => 1.month}.merge(options), &blk)
140
+ end
141
+
142
+ def self.cron(spec, &blk)
143
+ self.do({:cron => spec}, &blk)
144
+ end
145
+
146
+ def self.list
147
+ Timer.list
148
+ end
149
+
150
+ def self.find_by_tag(tag)
151
+ Timer.list.find_all { |t|
152
+ t.tag == tag
153
+ }
154
+ end
155
+
156
+ def self.find_by_name(name)
157
+ Timer.list.find_all { |t|
158
+ t.name == name
159
+ }
160
+ end
161
+
162
+ end
163
+
164
+ end
@@ -0,0 +1,56 @@
1
+ # Stolen directly from ramaze/snippets
2
+ module NumericMixable
3
+ def seconds
4
+ self
5
+ end
6
+ alias second seconds
7
+
8
+ # 60 seconds in a minute
9
+ def minutes
10
+ self * 60
11
+ end
12
+ alias minute minutes
13
+
14
+ # 60 minutes in an hour
15
+ def hours
16
+ self * 3600
17
+ end
18
+ alias hour hours
19
+
20
+ # 24 hours in a day
21
+ def days
22
+ self * 86400
23
+ end
24
+ alias day days
25
+
26
+ # 7 days in a week
27
+ def weeks
28
+ self * 604800
29
+ end
30
+ alias week weeks
31
+
32
+ # 30 days in a month
33
+ def months
34
+ self * 2592000
35
+ end
36
+ alias month months
37
+
38
+ # 365.25 days in a year
39
+ def years
40
+ self * 883612800
41
+ end
42
+ alias year years
43
+
44
+ # Time in the past, i.e. 3.days.ago
45
+ def ago t = Time.now
46
+ t - self
47
+ end
48
+ alias before ago
49
+
50
+ # Time in the future, i.e. 3.days.from_now
51
+ def from_now t = Time.now
52
+ t + self
53
+ end
54
+ alias since from_now
55
+
56
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: em-timers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Jake Douglas
8
+ - Colin Steele
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2009-11-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: helper methods for timers in EventMachine
29
+ email: jakecdouglas@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README
35
+ - lib/em-timers.rb
36
+ - lib/em-timers/numericmixable.rb
37
+ - lib/em-timers/cron_line.rb
38
+ - lib/em-timers/em-timers.rb
39
+ homepage: http://www.github.com/yakischloba/em-timers
40
+ licenses: []
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.0.5
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: helper methods for timers in EventMachine
62
+ test_files: []