recurring 0.1.0 → 0.2.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.
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: