vrinek-periodicity 0.1.1

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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.1 2009-08-06
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/periodicity.rb
7
+ lib/periodicity/period.rb
8
+ script/console
9
+ script/destroy
10
+ script/generate
11
+ spec/periodicity_spec.rb
12
+ spec/spec.opts
13
+ spec/spec_helper.rb
14
+ tasks/rspec.rake
data/PostInstall.txt ADDED
@@ -0,0 +1,2 @@
1
+
2
+ For more information on periodicity, see http://github.com/vrinek/periodicity/
data/README.rdoc ADDED
@@ -0,0 +1,33 @@
1
+ = periodicity
2
+
3
+ * http://github.com/vrinek/periodicity
4
+
5
+ == DESCRIPTION:
6
+
7
+ Helps calculate the next run for schedulers using a human readable syntax.
8
+
9
+ == SYNOPSIS:
10
+
11
+ period = Period.new(Time.now).every(2).hours.at(20).from(10).to(15)
12
+ means: every 2 hours at :20 from 10:00 to 15:00 (10:20, 12:20, 14:20)
13
+
14
+ period.next_run # returns the next calculated time as a Time object
15
+ period.next_run(1) # skips 1 run (as of 0.1, it behaves buggy with from and to limits)
16
+
17
+ Period.new.every(:half).hour # every 30 minutes
18
+ Period.every.week # every week
19
+ Period.every(5).seconds # every 5 seconds
20
+
21
+ == REQUIREMENTS:
22
+
23
+ * active support (not sure about the exact version yet)
24
+
25
+ == INSTALL:
26
+
27
+ * gem sources -a http://gems.github.com
28
+ * sudo gem install vrinek-periodicity
29
+
30
+ == TODO:
31
+
32
+ * support "overnight" limits (from 22:00 tonight to 02:00 tomorrow) like:
33
+ Period.every.hour.from(20).to(2)
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/periodicity'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'periodicity' do
14
+ self.developer 'Kostas Karachalios', 'kostas.karachalios@me.com'
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.extra_deps = [['activesupport','>= 2.3.2']]
17
+ self.extra_rdoc_files = ['README.rdoc']
18
+ self.url = 'http://github.com/vrinek/periodicity'
19
+
20
+ end
21
+
22
+ require 'newgem/tasks'
23
+ Dir['tasks/**/*.rake'].each { |t| load t }
24
+
25
+ # TODO - want other tests/tasks run by default? Add them to the list
26
+ remove_task :default
27
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'rubygems'
4
+ require 'activesupport'
5
+ require 'periodicity/period'
6
+
7
+ module Periodicity
8
+ VERSION = '0.1.1'
9
+ end
@@ -0,0 +1,265 @@
1
+ class Period
2
+ DAY_PERIODS = {
3
+ "morning" => 9,
4
+ "noon" => 12,
5
+ "afternoon" => 19,
6
+ "midnight" => 0
7
+ }
8
+
9
+ =begin rdoc
10
+ Initializes a new Period object with the last_run used for the next_run calculations
11
+
12
+ A Period object needs at least an every and time period (minutes, hours etc) to functions properly:
13
+ Period.new.every.day
14
+ Period.new(2.hours.ago).every(4).hours
15
+
16
+ If last_run is nil, next_run will always return Time.now
17
+ =end
18
+ def initialize(last_run = nil)
19
+ @last_run = last_run
20
+ @at = 0
21
+ end
22
+
23
+ =begin rdoc
24
+ Sets the interval at which the runs should be calculated:
25
+
26
+ every.day
27
+ every(20).minutes
28
+ every(2).hours
29
+ =end
30
+ def every(int = 1)
31
+ @interval = int
32
+ return self
33
+ end
34
+
35
+ =begin rdoc
36
+ Sets the scope to one week so that <tt>every(2).weeks</tt> means "every 2 weeks"
37
+ =end
38
+ def weeks
39
+ @scope = 1.week
40
+ reset_on_base 7
41
+
42
+ return self
43
+ end
44
+ alias_method :week, :weeks
45
+
46
+ =begin rdoc
47
+ Sets the scope to one day so that <tt>every(4).days</tt> means "every 4 days"
48
+ =end
49
+ def days
50
+ @scope = 1.days
51
+ reset_on_base 24
52
+
53
+ return self
54
+ end
55
+ alias_method :day, :days
56
+
57
+ =begin rdoc
58
+ Sets the scope to one hour so that <tt>every(6).hours</tt> means "every 6 hours"
59
+ =end
60
+ def hours
61
+ @scope = 1.hour
62
+ reset_on_base 60
63
+
64
+ return self
65
+ end
66
+ alias_method :hour, :hours
67
+
68
+ =begin rdoc
69
+ Sets the scope to one minute so that <tt>every(20).minutes</tt> means "every 20 minutes"
70
+ =end
71
+ def minutes
72
+ @scope = 1.minute
73
+ reset_on_base 60
74
+
75
+ return self
76
+ end
77
+ alias_method :minute, :minutes
78
+
79
+ =begin rdoc
80
+ Sets the scope to one second so that <tt>every(5).seconds</tt> means "every 5 seconds"
81
+ =end
82
+ def seconds
83
+ @scope = 1.second
84
+ reset_on_base false
85
+
86
+ return self
87
+ end
88
+ alias_method :second, :seconds
89
+
90
+ =begin rdoc
91
+ Sets the from limit based on the scope:
92
+ every(2).hours.from(8) # means "every 2 hours beginning from 8:00"
93
+ =end
94
+ def from(time)
95
+ raise "From can't be after to" if @to and @to < time
96
+
97
+ @from = time
98
+ return self
99
+ end
100
+
101
+ =begin rdoc
102
+ Sets the to limit based on the scope:
103
+ every(2).hours.to(12) # means "every 2 hours until 12:00"
104
+ =end
105
+ def to(time)
106
+ raise "To can't be before from" if @from and time < @from
107
+
108
+ @to = time
109
+ return self
110
+ end
111
+
112
+ =begin rdoc
113
+ Sets a specific "sub-time" for the next_run:
114
+ every(2).hours.at(15) # means "every 2 hours at the first quarter of each" e.g. 12:15, 14:15, 16:15
115
+
116
+ NOTE: when using at and from, to limits together the limits *always* calculate at 0 "sub-time":
117
+ every(2).hours.at(15).from(12).to(16) # will return 12:15, 14:15 but not 16:15 because the to limit ends at 16:00
118
+ =end
119
+ def at(time)
120
+ unless time.is_a?(Integer)
121
+ time = case time.to_s
122
+ when /^\d+:00$/
123
+ time[/^\d+/].to_i
124
+ when /^\d+:\d{2}$/
125
+ raise 'Precise timing like "20:15" is not yet supported'
126
+ when /^(noon|afternoon|midnight|morning)$/
127
+ DAY_PERIODS[time.to_s]
128
+ else
129
+ time.to_i
130
+ end
131
+ end
132
+
133
+ @at = time
134
+ return self
135
+ end
136
+
137
+ =begin rdoc
138
+ This return a Time object for the next calculated time:
139
+ now = "Aug 05 14:40:23 2009".to_time
140
+ Period.new(now).every(2).hours.next_run # => Wed Aug 05 16:00:00 UTC 2009
141
+
142
+ Note that it round all "sub-time" to 0 by default (can be overriden with at)
143
+ =end
144
+ def next_run(skip = 0)
145
+ return Time.now unless @last_run
146
+
147
+ @next_run = @last_run
148
+
149
+ if @scope and @interval
150
+ @next_run += @interval * @scope
151
+
152
+ calc_precision
153
+
154
+ calc_limits
155
+
156
+ unless skip.zero?
157
+ @next_run += @interval * @scope * skip
158
+
159
+ calc_limits
160
+ end
161
+ else
162
+ raise 'No proper period specified'
163
+ end
164
+
165
+ return @next_run
166
+ end
167
+
168
+ private
169
+
170
+ def reset_on_base(base)
171
+ @interval = 1 unless base
172
+
173
+ @interval = case @interval.to_s
174
+ when /half/i
175
+ @scope = @scope/base
176
+ base/2
177
+ when /quarter/i
178
+ @scope = @scope/base
179
+ base/4
180
+ when /other/i
181
+ 2
182
+ else
183
+ @interval
184
+ end
185
+ end
186
+
187
+ def calc_precision(scope = nil)
188
+ @at = 0 if scope
189
+
190
+ case scope || @scope
191
+ when 1.minute
192
+ unless @next_run.sec == @at
193
+ @next_run += (@at - @next_run.sec)
194
+ end
195
+ when 1.hour
196
+ unless @next_run.min == @at
197
+ @next_run += (@at - @next_run.min).minutes
198
+ end
199
+ calc_precision 1.minute
200
+ when 1.day
201
+ unless @next_run.hour == @at
202
+ @next_run += (@at - @next_run.hour).hours
203
+ end
204
+ calc_precision 1.hour
205
+ when 1.week
206
+ unless @next_run.hour == @at
207
+ @next_run += (@at - @next_run.hour).hours
208
+ end
209
+ calc_precision 1.hour
210
+ end
211
+ end
212
+
213
+ def calc_limits
214
+ if @from or @to
215
+ now = case @scope
216
+ when 1.second
217
+ @next_run.sec
218
+ when 1.minute
219
+ @next_run.min
220
+ when 1.hour
221
+ @next_run.hour
222
+ when 1.day
223
+ @next_run.day
224
+ end
225
+
226
+ if @from and now < @from
227
+ @next_run += (@from - now) * @scope
228
+ elsif @to and now > @to
229
+ @next_run += ((@from || 0) - now) * @scope + uptime
230
+ end
231
+ end
232
+ end
233
+
234
+ def uptime
235
+ case @scope
236
+ when 1.second
237
+ 1.minute
238
+ when 1.minute
239
+ 1.hour
240
+ when 1.hour
241
+ 1.day
242
+ when 1.day
243
+ days_of_month.days
244
+ when 1.week
245
+ days_of_month.days
246
+ end
247
+ end
248
+
249
+ def downtime
250
+ case @scope
251
+ when 1.minute
252
+ 1.second
253
+ when 1.hour
254
+ 1.minute
255
+ when 1.day
256
+ 1.hour
257
+ when 1.week
258
+ 1.hour
259
+ end
260
+ end
261
+
262
+ def days_of_month
263
+ (Date.new(Time.now.year, 12, 31).to_date << (12 - @last_run.month)).day
264
+ end
265
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
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/periodicity.rb'}"
9
+ puts "Loading periodicity gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
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 ADDED
@@ -0,0 +1,14 @@
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)
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Period do
4
+ it "should return now if not run before" do
5
+ Period.new.next_run.to_i.should == Time.now.to_i
6
+ end
7
+
8
+ it "should raise when period not set properly" do
9
+ lambda {Period.new(Time.now).next_run}.should raise_error
10
+ lambda {Period.new(Time.now).every(12).next_run}.should raise_error
11
+ lambda {Period.new(Time.now).minutes.next_run}.should raise_error
12
+ end
13
+
14
+ it "should return correct time on simple stuff" do
15
+ period.every(3).weeks.next_run.should == "Aug 26 00:00:00 2009".to_time
16
+ period.every(4).days.next_run.should == "Aug 09 00:00:00 2009".to_time
17
+ period.every(2).hours.next_run.should == "Aug 05 16:00:00 2009".to_time
18
+ period.every(30).minutes.next_run.should == "Aug 05 15:10:00 2009".to_time
19
+ end
20
+
21
+ it "should return correct time on a little bit more complex stuff" do
22
+ period.every.day.next_run.should == "Aug 06 00:00:00 2009".to_time
23
+ period.every(:quarter).hour.next_run.should == "Aug 05 14:55:00 2009".to_time
24
+ period.every(:half).minute.next_run.should == "Aug 05 14:40:53 2009".to_time
25
+ period.every(:other).hour.next_run.should == period.every(2).hours.next_run
26
+
27
+ period.every.day.at(:morning).next_run.should == "Aug 06 09:00:00 2009".to_time
28
+ period.every.day.at(:noon).next_run.should == "Aug 06 12:00:00 2009".to_time
29
+ period.every.day.at(:afternoon).next_run.should == "Aug 06 19:00:00 2009".to_time
30
+
31
+ period.every.day.at(:midnight).next_run.should == "Aug 06 00:00:00 2009".to_time
32
+ period.every.day.at(:midnight).next_run(1).should == "Aug 07 00:00:00 2009".to_time
33
+ period.every.day.at(:midnight).next_run(2).should == "Aug 08 00:00:00 2009".to_time
34
+
35
+ period.every.day.at('5').next_run.should == "Aug 06 05:00:00 2009".to_time
36
+ period.every.day.at('5:00').next_run.should == "Aug 06 05:00:00 2009".to_time
37
+ period.every.day.at('05:00').next_run.should == "Aug 06 05:00:00 2009".to_time
38
+
39
+ lambda {period.every.day.at('05:30').next_run}.should raise_error # not yet implemented
40
+ end
41
+
42
+ it "should return correct time with precision" do
43
+ period.every.week.at(20).next_run.should == "Aug 12 20:00:00 2009".to_time
44
+ period.every.day.at(14).next_run.should == "Aug 06 14:00:00 2009".to_time
45
+
46
+ period.every.hour.at(30).next_run.should == "Aug 05 15:30:00 2009".to_time
47
+ period.every.hour.at(30).next_run(1).should == "Aug 05 16:30:00 2009".to_time
48
+ period.every.hour.at(30).next_run(2).should == "Aug 05 17:30:00 2009".to_time
49
+ end
50
+
51
+ it "should take into account the limits" do
52
+ period.every(2).hours.from(18).next_run.should == "Aug 05 18:00:00 2009".to_time
53
+ period.every.hour.from(13).next_run.should == "Aug 05 15:00:00 2009".to_time
54
+ period.every(2).hours.from(12).next_run.should == "Aug 05 16:00:00 2009".to_time
55
+
56
+ period.every(2).hours.from(21).next_run.should == "Aug 05 21:00:00 2009".to_time
57
+ period.every(2).hours.from(21).next_run(1).should == "Aug 05 23:00:00 2009".to_time
58
+ period.every(2).hours.from(21).next_run(2).should == "Aug 06 21:00:00 2009".to_time
59
+
60
+ period.every(2).hours.to(12).next_run.should == "Aug 06 00:00:00 2009".to_time
61
+ period.every(2).hours.to(12).next_run(1).should == "Aug 06 02:00:00 2009".to_time
62
+ period.every(2).hours.to(12).next_run(2).should == "Aug 06 04:00:00 2009".to_time
63
+
64
+ period.every(5).minutes.from(15).to(50).next_run(0).should == "Aug 05 14:45:00 2009".to_time
65
+ period.every(5).minutes.from(15).to(50).next_run(1).should == "Aug 05 14:50:00 2009".to_time
66
+ period.every(5).minutes.from(15).to(50).next_run(2).should == "Aug 05 15:15:00 2009".to_time
67
+ # period.every(5).minutes.from(15).to(50).next_run(3).should == "Aug 05 15:20:00 2009".to_time # skip has limits...
68
+
69
+ period.every(2).hours.from(8).to(12).next_run(0).should == "Aug 06 08:00:00 2009".to_time
70
+ period.every(2).hours.from(8).to(12).next_run(1).should == "Aug 06 10:00:00 2009".to_time
71
+ period.every(2).hours.from(8).to(12).next_run(2).should == "Aug 06 12:00:00 2009".to_time
72
+ period.every(2).hours.from(8).to(12).next_run(3).should == "Aug 07 08:00:00 2009".to_time
73
+ # period.every(2).hours.from(8).to(12).next_run(4).should == "Aug 07 10:00:00 2009".to_time # skip has limits...
74
+
75
+ period.every(3).days.from(5).to(20).next_run(0).should == "Aug 08 00:00:00 2009".to_time
76
+ period.every(3).days.from(5).to(20).next_run(1).should == "Aug 11 00:00:00 2009".to_time
77
+ period.every(3).days.from(5).to(20).next_run(2).should == "Aug 14 00:00:00 2009".to_time
78
+ period.every(3).days.from(5).to(20).next_run(3).should == "Aug 17 00:00:00 2009".to_time
79
+ period.every(3).days.from(5).to(20).next_run(4).should == "Aug 20 00:00:00 2009".to_time
80
+ period.every(3).days.from(5).to(20).next_run(5).should == "Sep 05 00:00:00 2009".to_time
81
+ end
82
+
83
+ it "should avoid some pitfalls" do
84
+ Period.new("Aug 05 14:41:23 2009".to_time).every.hour.at(40).next_run.should == "Aug 05 15:40:00 2009".to_time
85
+ Period.new("Aug 05 14:39:23 2009".to_time).every.hour.at(40).next_run.should == "Aug 05 15:40:00 2009".to_time
86
+
87
+ lambda {period.from(5).to(1)}.should raise_error
88
+ lambda {period.to(1).from(5)}.should raise_error
89
+ end
90
+
91
+ def period
92
+ return Period.new("Aug 05 14:40:23 2009".to_time)
93
+ end
94
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'periodicity'
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vrinek-periodicity
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Kostas Karachalios
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-06 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.2
34
+ version:
35
+ description: Helps calculate the next run for schedulers using a human readable syntax.
36
+ email:
37
+ - kostas.karachalios@me.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - PostInstall.txt
46
+ - README.rdoc
47
+ files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ - PostInstall.txt
51
+ - README.rdoc
52
+ - Rakefile
53
+ - lib/periodicity.rb
54
+ - lib/periodicity/period.rb
55
+ - script/console
56
+ - script/destroy
57
+ - script/generate
58
+ - spec/periodicity_spec.rb
59
+ - spec/spec.opts
60
+ - spec/spec_helper.rb
61
+ - tasks/rspec.rake
62
+ has_rdoc: false
63
+ homepage: http://github.com/vrinek/periodicity
64
+ licenses:
65
+ post_install_message: PostInstall.txt
66
+ rdoc_options:
67
+ - --main
68
+ - README.rdoc
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project: periodicity
86
+ rubygems_version: 1.3.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Helps calculate the next run for schedulers using a human readable syntax.
90
+ test_files: []
91
+