recurring 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.txt CHANGED
@@ -11,8 +11,13 @@ Schedules can be like:
11
11
  * The 1st and 15th of every other month
12
12
  * Every 2 hours
13
13
  * The first Friday of every month at 11:15am and 4:45:15pm
14
-
15
- == FEATURES/PROBLEMS:
14
+
15
+ = See more examples at Recurring::Schedule documentation
16
+
17
+ # This Schedule will match every Tuesday at 5pm
18
+ @rs = Recurring::Schedule.new :unit => 'weeks', :weekdays => 'tuesday', :times => '5pm'
19
+ @rs.include?(Time.utc(2006,12,12,17)) #=> true
20
+ @rs.include?(Time.utc(2006,12,13,17)) #=> false
16
21
 
17
22
  = Features
18
23
  * Fast searching of long ranges for matching times.
@@ -24,13 +29,6 @@ Schedules can be like:
24
29
  * Untested timezone support
25
30
  * Plans to offer complex Schedule and Masks
26
31
 
27
- == SYNOPSYS:
28
-
29
- # This Schedule will match every Tuesday at 5pm
30
- @rs = Recurring::Schedule.new :unit => 'weeks', :weekdays => 'tuesday', :times => '5pm'
31
- @rs.include?(Time.utc(2006,12,12,17)) #=> true
32
- @rs.include?(Time.utc(2006,12,13,17)) #=> false
33
-
34
32
  == REQUIREMENTS:
35
33
 
36
34
  * RSpec >= 0.7.4 to run the specs.
data/Rakefile CHANGED
@@ -1,23 +1,62 @@
1
1
  # -*- ruby -*-
2
2
 
3
3
  require 'rubygems'
4
- require 'hoe'
4
+ #require 'hoe'
5
5
  #require_gem 'rspec'
6
6
  #require 'rspec/lib/spec/rake/spectask'
7
7
  require './lib/recurring.rb'
8
+ require 'spec/rake/spectask'
9
+ require 'rake/gempackagetask'
8
10
 
9
- Hoe.new('recurring', Recurring::VERSION) do |p|
10
- p.rubyforge_name = 'recurring'
11
- p.summary = 'A scheduling library for recurring events'
12
- p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
13
- p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
- p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
11
+ Spec::Rake::SpecTask.new :spec do |t|
12
+ t.spec_files = FileList['spec/**/*_spec.rb']
13
+ t.spec_opts << "-f s"
15
14
  end
16
15
 
17
- desc "Run the specs"
18
- task :spec do
19
- system "spec spec/recurring_spec.rb -f specdoc"
16
+ task :default => :spec
17
+
18
+ # Hoe.new('recurring', Recurring::VERSION) do |p|
19
+ # p.rubyforge_name = 'recurring'
20
+ # p.summary = 'A scheduling library for recurring events'
21
+ # p.description =<<-DESC
22
+ # Recurring allows you to define Schedules, which can tell you whether or not a given Time falls in the Schedule, as well as being able to return a list of times which match the Schedule within a given range.
23
+ # DESC
24
+ #
25
+ # # p.authors = "Chris Anderson"
26
+ # p.email = 'jchris@mfdz.com'
27
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
28
+ # p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
29
+ # end
30
+
31
+ specification = Gem::Specification.new do |s|
32
+ s.name = %q{recurring}
33
+ s.version = "0.2.0"
34
+ s.platform = Gem::Platform::RUBY
35
+ s.date = %q{2006-12-10}
36
+ s.summary = %q{A scheduling library for recurring events}
37
+ s.email = %q{jchris@mfdz.com}
38
+ s.homepage = %q{http://jchris.mfdz.com}
39
+ s.rubyforge_project = %q{recurring}
40
+ s.autorequire = 'recurring'
41
+ s.description = %q{Recurring allows you to define Schedules, which can tell you whether or not a given Time falls in the Schedule, as well as being able to return a list of times which match the Schedule within a given range.}
42
+ s.authors = ["Chris Anderson"]
43
+ s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/recurring.rb", "spec/recurring_spec.rb"]
44
+ #s.add_dependency(%q<hoe>, [">= 1.1.6"])
45
+ s.add_dependency(%q<rspec>, [">= 0.7.4"])
46
+ s.has_rdoc = true
20
47
  end
48
+
49
+
50
+ Rake::GemPackageTask.new specification do |pkg|
51
+ pkg.need_zip = true
52
+ pkg.need_tar = true
53
+ end
54
+
55
+
56
+ # desc "Run the specs"
57
+ # task :spec do
58
+ # system "spec spec/recurring_spec.rb -f specdoc"
59
+ # end
21
60
  #Spec::Rake::SpecTask.new('spec') do |t|
22
61
  # t.spec_files = FileList['examples/**/*.rb']
23
62
  #end
data/lib/recurring.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Recurring
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
 
4
4
  class << self
5
5
  # returns a number starting with 1
@@ -17,7 +17,11 @@ module Recurring
17
17
  # Initialize a Schedule object with the proper options to calculate occurances in
18
18
  # that schedule. Schedule#new take a hash of options <tt>:unit, :frequency, :anchor, :weeks, :monthdays, :weekdays, :times</tt>
19
19
  #
20
- # = Monthly
20
+ # = Yearly
21
+ # [Every two years from an anchor time] <tt>Recurring::Schedule.new :unit => 'years', :frequency => 2, :anchor => Time.utc(2006,4,15,10,30)</tt>
22
+ # [Every year in February and May on the 1st and 15th] <tt>Recurring::Schedule.new :unit => 'years', :months => ['feb', 'may'], :monthdays => [1,15]</tt>
23
+ # [Every year in February and May on the 1st and 15th] <tt>Recurring::Schedule.new :unit => 'years', :months => ['feb', 'may'], :monthdays => [1,15]</tt>
24
+ # = Monthly
21
25
  # [Every two months from an anchor time] <tt>Recurring::Schedule.new :unit => 'months', :frequency => 2, :anchor => Time.utc(2006,4,15,10,30)</tt>
22
26
  # [The first and fifteenth of every month] <tt>Recurring::Schedule.new :unit => 'months', :monthdays => [1,15]</tt>
23
27
  # [The first and eighteenth of every third month] <tt>Recurring::Schedule.new :unit => 'months', :frequency => 3, :anchor => Time.utc(2006,4,15,10,30), :monthdays => [10,18]</tt>
@@ -39,10 +43,10 @@ module Recurring
39
43
  # See the specs using "rake spec" for even more examples.
40
44
  class Schedule
41
45
 
42
- attr_reader :unit, :frequency, :anchor, :weeks, :monthdays, :weekdays, :times
46
+ attr_reader :unit, :frequency, :anchor, :months, :weeks, :monthdays, :weekdays, :times
43
47
 
44
48
  # * options is a hash with keys <tt>:unit, :frequency, :anchor, :weeks, :monthdays, :weekdays, :times</tt>
45
- # * valid units are <tt>months, weeks, days, hours, and minutes</tt>
49
+ # * valid units are <tt>years, months, weeks, days, hours, and minutes</tt>
46
50
  # * :frequency defaults to 1
47
51
  # * :anchor is required if the frequency is other than one
48
52
  # * :weeks alongside :weekdays is used to specify the nth instance of a weekday in a month.
@@ -51,12 +55,13 @@ module Recurring
51
55
  # * :times takes a string with a simple format. <tt>"4pm 5:15pm 6:45:30pm"</tt>
52
56
  def initialize options
53
57
  raise ArgumentError, 'specify a valid unit' unless options[:unit] &&
54
- %w{months weeks days hours minutes}.include?(options[:unit])
58
+ %w{years months weeks days hours minutes}.include?(options[:unit])
55
59
  raise ArgumentError, 'frequency > 1 requires an anchor Time' if options[:frequency] && !options[:anchor]
56
60
  @unit = options[:unit].to_sym
57
61
  @frequency = options[:frequency] || 1
58
62
  @anchor = options[:anchor]
59
63
  @times = parse_times options[:times]
64
+ @months = Array(options[:months]).collect{|d|d.to_sym} if options[:months]
60
65
  @weeks = Array(options[:weeks]) if options[:weeks]
61
66
  @weekdays = Array(options[:weekdays]).collect{|d|d.to_sym} if options[:weekdays]
62
67
  @monthdays = Array(options[:monthdays]) if options[:monthdays]
@@ -69,9 +74,10 @@ module Recurring
69
74
  def include? date
70
75
  @resolution = nil
71
76
  return true if check_anchor? && date == @anchor
72
- return mismatch(:month) unless month_matches?(date) if @unit == :months
73
- return mismatch(:week) unless week_matches?(date) if [:months, :weeks].include?(@unit)
74
- if [:months, :weeks, :days].include?(@unit)
77
+ return mismatch(:year) unless year_matches?(date) if @unit == :years
78
+ return mismatch(:month) unless month_matches?(date) if [:months, :years].include?(@unit)
79
+ return mismatch(:week) unless week_matches?(date) if [:years, :months, :weeks].include?(@unit)
80
+ if [:years, :months, :weeks, :days].include?(@unit)
75
81
  return mismatch(:day) unless day_matches?(date)
76
82
  return mismatch(:time) unless time_matches?(date)
77
83
  end
@@ -95,7 +101,7 @@ module Recurring
95
101
  end
96
102
  end
97
103
 
98
- # Takes a range, which can specified as two Times, or as an object that returns times from the methods .first and .last
104
+ # Takes a range, which can specified as two Times, or as an object that returns Time objects from the methods <tt>first</tt> and <tt>last</tt>. Really, the argment objects just need to be duck-type compatible with most of the Time instance methods, and the <tt>utc</tt> factory method.
99
105
  #
100
106
  # <tt>rs.find_in_range(Time.now, Time.now+24*60*60)</tt>
101
107
  #
@@ -169,10 +175,23 @@ module Recurring
169
175
  false
170
176
  end
171
177
 
178
+ def year_matches? date
179
+ return true if @frequency == 1
180
+ return (date.year - @anchor.year) % @frequency == 0
181
+ end
182
+
172
183
  def month_matches? date
173
184
  #only concerned with multiples
174
- return true if @frequency == 1
175
- (date.month - @anchor.month) % @frequency == 0
185
+ if @unit == :months
186
+ return true if @frequency == 1
187
+ return (date.month - @anchor.month) % @frequency == 0
188
+ end
189
+ if @months
190
+ month_nums = @months.collect{|w| ordinal_month w}
191
+ return month_nums.include?(date.month)
192
+ else
193
+ true
194
+ end
176
195
  end
177
196
 
178
197
  def week_matches? date
@@ -258,21 +277,21 @@ module Recurring
258
277
  lookup.select{|k,v| v.include?(symbol)}.first.first
259
278
  end
260
279
 
261
- # def ordinal_month symbol
262
- # lookup = {1 => [:january, :jan],
263
- # 2 => [:february, :feb],
264
- # 3 => [:march, :mar],
265
- # 4 => [:april, :apr],
266
- # 5 => [:may],
267
- # 6 => [:june, :jun],
268
- # 7 => [:july, :jul],
269
- # 8 => [:august, :aug],
270
- # 9 => [:september, :sept],
271
- # 10 => [:october, :oct],
272
- # 11 => [:november, :nov],
273
- # 12 => [:december, :dec]}
274
- # lookup.select{|k,v| v.include?(symbol)}.first.first
275
- # end
280
+ def ordinal_month symbol
281
+ lookup = {1 => [:january, :jan],
282
+ 2 => [:february, :feb],
283
+ 3 => [:march, :mar],
284
+ 4 => [:april, :apr],
285
+ 5 => [:may],
286
+ 6 => [:june, :jun],
287
+ 7 => [:july, :jul],
288
+ 8 => [:august, :aug],
289
+ 9 => [:september, :sept],
290
+ 10 => [:october, :oct],
291
+ 11 => [:november, :nov],
292
+ 12 => [:december, :dec]}
293
+ lookup.select{|k,v| v.include?(symbol)}.first.first
294
+ end
276
295
 
277
296
  def parse_times string
278
297
  unless string
@@ -10,9 +10,10 @@ def results_should_be_included results, schedule
10
10
  end
11
11
 
12
12
  def should_find_in_range schedule, length, *range
13
- result = schedule.find_in_range(range.first, range.last)
13
+ result = schedule.find_in_range(range)
14
14
  result.length.should_be length
15
15
  results_should_be_included result, schedule
16
+ result
16
17
  end
17
18
 
18
19
  context "Initializing a Schedule" do
@@ -48,6 +49,11 @@ context "Initializing a Schedule" do
48
49
  rs.weekdays.should == [:monday, :wednesday]
49
50
  end
50
51
 
52
+ specify "should accept months params" do
53
+ rs = Recurring::Schedule.new :unit => 'years', :months => 'feb', :monthdays => [4]
54
+ rs.months.should == [:feb]
55
+ end
56
+
51
57
  specify "should accept days as integers" do
52
58
  rs = Recurring::Schedule.new :unit => 'months', :monthdays => [1,15]
53
59
  rs.monthdays.should == [1,15]
@@ -82,6 +88,71 @@ context "A complex Schedule" do
82
88
 
83
89
  end
84
90
 
91
+ #YEARS
92
+ context "A yearly schedule with month and monthdays" do
93
+ setup do
94
+ @rs = Recurring::Schedule.new :unit => 'years', :months => 'feb', :monthdays => [4]
95
+ end
96
+
97
+ specify "should include the day of the month in any year" do
98
+ @rs.should_include Time.utc(1980,2,4)
99
+ @rs.should_include Time.utc(1989,2,4)
100
+ end
101
+ specify "should not include other days of the month in any year" do
102
+ @rs.should_not_include Time.utc(1989,4,4)
103
+ @rs.should_not_include Time.utc(1980,2,5)
104
+ end
105
+
106
+ specify "should find 20 times in 20 years" do
107
+ should_find_in_range(@rs, 20, Time.utc(2006,12,12,1), Time.utc(2026,12,12,1,45))
108
+ end
109
+ end
110
+
111
+ context "A yearly schedule with months and weekdays" do
112
+ setup do
113
+ @rs = Recurring::Schedule.new :unit => 'years', :months => 'feb', :weekdays => ['tuesday', 'weds']
114
+ end
115
+ specify "should match every tuesday and wednesday in febraury" do
116
+ @rs.should_include Time.utc(2007,2,6)
117
+ @rs.should_include Time.utc(2007,2,7)
118
+ @rs.should_include Time.utc(2007,2,20)
119
+ @rs.should_include Time.utc(2007,2,21)
120
+ end
121
+ specify "should not include other days of the week in feburary" do
122
+ @rs.should_not_include Time.utc(2007,2,8)
123
+ @rs.should_not_include Time.utc(2007,2,9)
124
+ @rs.should_not_include Time.utc(2007,2,23)
125
+ @rs.should_not_include Time.utc(2007,2,24)
126
+ end
127
+ specify "should not include tuesday and wednesday outside of febraury" do
128
+ @rs.should_not_include Time.utc(2007,3,6)
129
+ @rs.should_not_include Time.utc(2007,3,7)
130
+ @rs.should_not_include Time.utc(2007,3,20)
131
+ @rs.should_not_include Time.utc(2007,3,21)
132
+ end
133
+ specify "should find 20 times in 20 years" do
134
+ should_find_in_range(@rs, 161, Time.utc(2006,12,12,1), Time.utc(2026,12,12,1,45))
135
+ end
136
+ end
137
+
138
+ context "A bi-yearly schedule with months, weeks, weekdays, and times" do
139
+ setup do
140
+ @rs = Recurring::Schedule.new :unit => 'years', :frequency => 2, :anchor => Time.utc(2006,6), :months => ['feb', 'may'], :weeks => [2], :weekdays => 'fri', :times => '5:15'
141
+ end
142
+
143
+ specify "should include the correct days at the right times" do
144
+ @rs.should_include Time.utc(2006,2,10,5,15)
145
+ @rs.should_include Time.utc(2006,5,12,5,15)
146
+ @rs.should_include Time.utc(2008,2,8,5,15)
147
+ @rs.should_include Time.utc(2008,5,9,5,15)
148
+ end
149
+
150
+ specify "should not include good times in bad years" do
151
+ @rs.should_not_include Time.utc(2007,2,9,5,15)
152
+ @rs.should_not_include Time.utc(2007,5,11,5,15)
153
+ end
154
+ end
155
+
85
156
  #MONTHS
86
157
 
87
158
  context "A bi-monthly Schedule without more params" do
@@ -395,6 +466,19 @@ context "A fourth-daily schedule with only the unit and frequency != 1" do
395
466
  end
396
467
  end
397
468
 
469
+ context "A daily schedule with a sub-second precise anchor" do
470
+ setup do
471
+ @rs = Recurring::Schedule.new :unit => 'days', :anchor => Time.utc(2006,11,27,0,0,30,45)
472
+ end
473
+ specify "should include times equal up to second accuracy" do
474
+ @rs.should_include Time.utc(2006,11,27,0,0,30)
475
+ @rs.should_include Time.utc(2006,11,27,0,0,30,55)
476
+ end
477
+ specify "should not include other times" do
478
+ @rs.should_not_include Time.utc(2006,11,27,0,0,31,45)
479
+ end
480
+ end
481
+
398
482
  context "A fourth-daily schedule with no other params" do
399
483
 
400
484
  setup do
metadata CHANGED
@@ -3,16 +3,16 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: recurring
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
6
+ version: 0.2.0
7
7
  date: 2006-12-10 00:00:00 -08:00
8
8
  summary: A scheduling library for recurring events
9
9
  require_paths:
10
10
  - lib
11
- email: ryand-ruby@zenspider.com
12
- homepage: " by Chris Anderson"
11
+ email: jchris@mfdz.com
12
+ homepage: http://jchris.mfdz.com
13
13
  rubyforge_project: recurring
14
- description: "Schedules can be like: * Every Tuesday at 5pm * The 1st and 15th of every other month * Every 2 hours * The first Friday of every month at 11:15am and 4:45:15pm == FEATURES/PROBLEMS: = Features * Fast searching of long ranges for matching times. * Ability to define Schedules in a few flexible ways. * Extensive RSpec specifications (run \"rake spec\" to see them) * Extensive RSpec specifications (run \"rake spec\" to see them) = Problems / Todo * Untested timezone support * Plans to offer complex Schedule and Masks == SYNOPSYS:"
15
- autorequire:
14
+ description: Recurring allows you to define Schedules, which can tell you whether or not a given Time falls in the Schedule, as well as being able to return a list of times which match the Schedule within a given range.
15
+ autorequire: recurring
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
@@ -26,7 +26,7 @@ platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
28
  authors:
29
- - Ryan Davis
29
+ - Chris Anderson
30
30
  files:
31
31
  - History.txt
32
32
  - Manifest.txt
@@ -48,11 +48,11 @@ requirements: []
48
48
 
49
49
  dependencies:
50
50
  - !ruby/object:Gem::Dependency
51
- name: hoe
51
+ name: rspec
52
52
  version_requirement:
53
53
  version_requirements: !ruby/object:Gem::Version::Requirement
54
54
  requirements:
55
55
  - - ">="
56
56
  - !ruby/object:Gem::Version
57
- version: 1.1.6
57
+ version: 0.7.4
58
58
  version: