cyclical 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.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +85 -0
- data/Rakefile +1 -0
- data/cyclical.gemspec +24 -0
- data/lib/cyclical/filters/monthdays_filter.rb +38 -0
- data/lib/cyclical/filters/months_filter.rb +55 -0
- data/lib/cyclical/filters/weekdays_filter.rb +90 -0
- data/lib/cyclical/filters/yeardays_filter.rb +38 -0
- data/lib/cyclical/occurrence.rb +121 -0
- data/lib/cyclical/rule.rb +235 -0
- data/lib/cyclical/rules/daily_rule.rb +49 -0
- data/lib/cyclical/rules/monthly_rule.rb +56 -0
- data/lib/cyclical/rules/weekly_rule.rb +58 -0
- data/lib/cyclical/rules/yearly_rule.rb +66 -0
- data/lib/cyclical/schedule.rb +125 -0
- data/lib/cyclical/suboccurrence.rb +48 -0
- data/lib/cyclical/version.rb +3 -0
- data/lib/cyclical.rb +12 -0
- data/spec/filters/monthdays_filter_spec.rb +36 -0
- data/spec/filters/months_filter_spec.rb +19 -0
- data/spec/filters/weekdays_filter_spec.rb +108 -0
- data/spec/filters/yeardays_filter_spec.rb +36 -0
- data/spec/occurrence_spec.rb +207 -0
- data/spec/rule_dsl_spec.rb +105 -0
- data/spec/rules/daily_rule_spec.rb +140 -0
- data/spec/rules/monthly_rule_spec.rb +218 -0
- data/spec/rules/weekly_rule_spec.rb +150 -0
- data/spec/rules/yearly_rule_spec.rb +232 -0
- data/spec/schedule_spec.rb +650 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/suboccurrence_spec.rb +51 -0
- metadata +126 -0
@@ -0,0 +1,650 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Schedule do
|
4
|
+
it "should create instances with start date" do
|
5
|
+
t = Time.now
|
6
|
+
s = Schedule.new(t)
|
7
|
+
|
8
|
+
s.start_time.should == t
|
9
|
+
end
|
10
|
+
|
11
|
+
# basic API for listing occurences
|
12
|
+
describe "single date schedule" do
|
13
|
+
before do
|
14
|
+
@time = Time.local(2011, 11, 1, 0, 0, 0)
|
15
|
+
@schedule = Schedule.new(@time)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept end time and return suboccurrence" do
|
19
|
+
@schedule.end_time = Time.local(2011, 11, 1, 1)
|
20
|
+
|
21
|
+
@schedule.end_time.should == Time.local(2011, 11, 1, 1)
|
22
|
+
|
23
|
+
so = @schedule.suboccurrences_between(Time.local(2011, 11, 1, 0, 30), Time.local(2011, 11, 2))
|
24
|
+
so.first.start.should == Time.local(2011, 11, 1, 0, 30)
|
25
|
+
so.first.occurrence_start?.should_not be_true
|
26
|
+
so.first.end.should == @schedule.end_time
|
27
|
+
so.first.occurrence_end?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should list the single occurrence" do
|
31
|
+
@schedule.first(1).should include(Time.local(2011, 11, 1))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should find next occurrence" do
|
35
|
+
t = Time.local(2011, 11, 1)
|
36
|
+
|
37
|
+
@schedule.next_occurrence(t - 1.second).should == t
|
38
|
+
@schedule.next_occurrence(t + 1.second).should be_nil
|
39
|
+
|
40
|
+
@schedule.next_occurrence(t).should == t
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should find previous occurrence" do
|
44
|
+
t = Time.local(2011, 11, 1)
|
45
|
+
|
46
|
+
@schedule.previous_occurrence(t + 1.second).should == t
|
47
|
+
@schedule.previous_occurrence(t - 1.second).should be_nil
|
48
|
+
|
49
|
+
@schedule.previous_occurrence(t).should be_nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should list occurrences between dates" do
|
53
|
+
t = Time.local(2011, 11, 1)
|
54
|
+
|
55
|
+
@schedule.occurrences_between(t - 10.minutes, t - 1.second).should be_empty
|
56
|
+
@schedule.occurrences_between(t + 1.second, t + 10.minutes).should be_empty
|
57
|
+
|
58
|
+
@schedule.occurrences_between(t - 1.second, t + 1.second).should include(t)
|
59
|
+
|
60
|
+
@schedule.occurrences_between(t - 10.minutes, t).should be_empty
|
61
|
+
@schedule.occurrences_between(t, t + 10.minutes).should include(t)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should list occurrences up to a date" do
|
65
|
+
t = Time.local(2011, 11, 1)
|
66
|
+
|
67
|
+
@schedule.occurrences(t - 1.second).should be_empty
|
68
|
+
@schedule.occurrences(t).should be_empty
|
69
|
+
@schedule.occurrences(t + 1.second).should include(t)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should list all occurences" do
|
73
|
+
@schedule.occurrences.should include(Time.local(2011, 11, 1))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "advanced case" do
|
78
|
+
before do
|
79
|
+
@time = Time.local(2011, 9, 1, 10)
|
80
|
+
@schedule = Schedule.new @time, Rule.monthly(2).weekdays(:mon => 2).count(5)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should list first 3 occurrences" do
|
84
|
+
expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10), Time.local(2012, 1, 9, 10)]
|
85
|
+
|
86
|
+
@schedule.first(3).should == expected
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should find next occurrence" do
|
90
|
+
@schedule.next_occurrence(Time.local(2011, 10, 1)).should == Time.local(2011, 11, 14, 10)
|
91
|
+
@schedule.next_occurrence(Time.local(2011, 11, 14)).should == Time.local(2011, 11, 14, 10)
|
92
|
+
@schedule.next_occurrence(Time.local(2011, 11, 14, 10)).should == Time.local(2011, 11, 14, 10)
|
93
|
+
@schedule.next_occurrence(Time.local(2011, 11, 14, 10, 0, 1)).should == Time.local(2012, 1, 9, 10)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should find previous occurrence" do
|
97
|
+
@schedule.previous_occurrence(Time.local(2011, 12, 1)).should == Time.local(2011, 11, 14, 10)
|
98
|
+
@schedule.previous_occurrence(Time.local(2011, 11, 14, 10, 0, 1)).should == Time.local(2011, 11, 14, 10)
|
99
|
+
@schedule.previous_occurrence(Time.local(2011, 11, 14, 10)).should == Time.local(2011, 9, 12, 10)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should list occurrences between dates" do
|
103
|
+
@schedule.occurrences_between(Time.local(2011, 10, 1), Time.local(2011, 11, 14, 10, 0, 1)).should include(Time.local(2011, 11, 14, 10))
|
104
|
+
@schedule.occurrences_between(Time.local(2011, 11, 14, 10), Time.local(2011, 11, 14, 11)).should include(Time.local(2011, 11, 14, 10))
|
105
|
+
|
106
|
+
@schedule.occurrences_between(Time.local(2011, 11, 14, 10, 0, 1), Time.local(2011, 12, 1)).should be_empty
|
107
|
+
@schedule.occurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 14, 10)).should be_empty
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should list occurrences upto a date" do
|
111
|
+
expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10), Time.local(2012, 1, 9, 10)]
|
112
|
+
|
113
|
+
@schedule.occurrences(Time.local(2012, 1, 9, 10)).should == expected[0..1]
|
114
|
+
@schedule.occurrences(Time.local(2012, 1, 9, 10, 0, 1)).should == expected
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should list all occurrences" do
|
118
|
+
expected = [Time.local(2011, 9, 12, 10), Time.local(2011, 11, 14, 10),
|
119
|
+
Time.local(2012, 1, 9, 10), Time.local(2012, 3, 12, 10), Time.local(2012, 5, 14, 10)]
|
120
|
+
|
121
|
+
@schedule.occurrences.should == expected
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "multiple-day schedule" do
|
126
|
+
before do
|
127
|
+
@time = Time.local(2011, 11, 1, 15)
|
128
|
+
@schedule = Schedule.new @time, Rule.daily(4).count(5)
|
129
|
+
@schedule.end_time = Time.local(2011, 11, 3, 8) # two days later at 8 AM
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return single occurrence span between dates" do
|
133
|
+
so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 2))
|
134
|
+
so.first.start.should == Time.local(2011, 11, 1, 15)
|
135
|
+
so.first.end.should == Time.local(2011, 11, 2)
|
136
|
+
so.first.occurrence_start?.should be_true
|
137
|
+
so.first.occurrence_end?.should_not be_true
|
138
|
+
|
139
|
+
so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 3, 10))
|
140
|
+
so.first.start.should == Time.local(2011, 11, 1, 15)
|
141
|
+
so.first.end.should == Time.local(2011, 11, 3, 8)
|
142
|
+
so.first.occurrence_start?.should be_true
|
143
|
+
so.first.occurrence_end?.should be_true
|
144
|
+
|
145
|
+
so = @schedule.suboccurrences_between(Time.local(2011, 11, 2, 5), Time.local(2011, 11, 3, 10))
|
146
|
+
so.first.start.should == Time.local(2011, 11, 2, 5)
|
147
|
+
so.first.end.should == Time.local(2011, 11, 3, 8)
|
148
|
+
so.first.occurrence_start?.should_not be_true
|
149
|
+
so.first.occurrence_end?.should be_true
|
150
|
+
|
151
|
+
@schedule.suboccurrences_between(Time.local(2011, 11, 3, 9), Time.local(2011, 11, 4, 11)).should be_empty
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should return multiple occurrence spans" do
|
155
|
+
so = @schedule.suboccurrences_between(Time.local(2011, 11, 1), Time.local(2011, 11, 8))
|
156
|
+
|
157
|
+
so.length.should == 2
|
158
|
+
so.first.start.should == Time.local(2011, 11, 1, 15)
|
159
|
+
so.first.end.should == Time.local(2011, 11, 3, 8)
|
160
|
+
so.first.occurrence_start?.should be_true
|
161
|
+
so.first.occurrence_end?.should be_true
|
162
|
+
|
163
|
+
so[1].start.should == Time.local(2011, 11, 5, 15)
|
164
|
+
so[1].end.should == Time.local(2011, 11, 7, 8)
|
165
|
+
so[1].occurrence_start?.should be_true
|
166
|
+
so[1].occurrence_end?.should be_true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "self overlapping schedule" do
|
171
|
+
before do
|
172
|
+
@time = Time.local(2011, 11, 1, 15)
|
173
|
+
@schedule = Schedule.new @time, Rule.daily.count(5)
|
174
|
+
@schedule.end_time = Time.local(2011, 11, 3, 8) # two days later at 8 AM
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should find two occurrences in a single day" do
|
178
|
+
s = @schedule.suboccurrences_between(Time.local(2011, 11, 2), Time.local(2011, 11, 3))
|
179
|
+
|
180
|
+
s.length.should == 2
|
181
|
+
|
182
|
+
s.first.start.should == Time.local(2011, 11, 2)
|
183
|
+
s.first.occurrence_start?.should_not be_true
|
184
|
+
s.first.end.should == Time.local(2011, 11, 3)
|
185
|
+
s.first.occurrence_end?.should_not be_true
|
186
|
+
|
187
|
+
s.last.start.should == Time.local(2011, 11, 2, 15)
|
188
|
+
s.last.occurrence_start?.should be_true
|
189
|
+
s.last.end.should == Time.local(2011, 11, 3)
|
190
|
+
s.last.occurrence_end?.should_not be_true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Examples from RFC 5545 except excluded and included dates
|
195
|
+
describe "rfc 5545 examples" do
|
196
|
+
before do
|
197
|
+
@time = Time.local(1997, 9, 2, 9, 0, 0)
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "daily for 10 occurrences" do
|
201
|
+
before do
|
202
|
+
@schedule = Schedule.new @time, Rule.daily.count(10)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should list occurences" do
|
206
|
+
expected = (2..11).map { |i| Time.local(1997, 9, i, 9, 0, 0) }
|
207
|
+
@schedule.occurrences.should == expected
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe "daily until December 24, 1997" do
|
212
|
+
before do
|
213
|
+
@schedule = Schedule.new @time, Rule.daily.stop(Time.local(1997, 12, 24))
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should list occurrences" do
|
217
|
+
expected = (2..30).map { |i| Time.local(1997, 9, i, 9, 0, 0) } +
|
218
|
+
(1..31).map { |i| Time.local(1997, 10, i, 9, 0, 0) } +
|
219
|
+
(1..30).map { |i| Time.local(1997, 11, i, 9, 0, 0) } +
|
220
|
+
(1..23).map { |i| Time.local(1997, 12, i, 9, 0, 0) }
|
221
|
+
|
222
|
+
@schedule.occurrences.should == expected
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "every other day - forever" do
|
227
|
+
before do
|
228
|
+
@schedule = Schedule.new @time, Rule.daily(2)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should list first 45 occurrences" do
|
232
|
+
expected = (1..15).map { |i| Time.local(1997, 9, 2*i, 9, 0, 0)} +
|
233
|
+
(1..15).map { |i| Time.local(1997, 10, 2*i, 9, 0, 0)} +
|
234
|
+
(1..15).map { |i| Time.local(1997, 11, 2*i - 1, 9, 0, 0)}
|
235
|
+
|
236
|
+
@schedule.first(45).should == expected
|
237
|
+
@schedule.occurrences(Time.local(1997, 12, 1)).should == expected
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "every 10 days, 5 occurrences" do
|
242
|
+
before do
|
243
|
+
@schedule = Schedule.new @time, Rule.daily(10).count(5)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should list occurrences" do
|
247
|
+
expected = [2, 12, 22].map { |i| Time.local(1997, 9, i, 9, 0, 0)} +
|
248
|
+
[2, 12].map { |i| Time.local(1997, 10, i, 9, 0, 0)}
|
249
|
+
|
250
|
+
@schedule.occurrences.should == expected
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "every day in January, for 3 years" do
|
255
|
+
before do
|
256
|
+
@schedule_1 = Schedule.new Time.local(1998, 1, 1, 9), Rule.daily.month(:january).stop(Time.local(2000, 1, 31, 14))
|
257
|
+
|
258
|
+
rule = Rule.yearly.month(:january).weekdays(1, :tu, :we, :th, 5, 6, :su).stop(Time.local(2000, 1, 31, 14))
|
259
|
+
@schedule_2 = Schedule.new Time.local(1998, 1, 1, 9), rule
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should list occurrences" do
|
263
|
+
expected = (1..31).map { |i| Time.local(1998, 1, i, 9) } +
|
264
|
+
(1..31).map { |i| Time.local(1999, 1, i, 9) } +
|
265
|
+
(1..31).map { |i| Time.local(2000, 1, i, 9) }
|
266
|
+
|
267
|
+
@schedule_1.occurrences.length.should == expected.length
|
268
|
+
@schedule_1.occurrences.should == expected
|
269
|
+
|
270
|
+
@schedule_2.occurrences.length.should == expected.length
|
271
|
+
@schedule_2.occurrences.should == expected
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe "weekly for 10 occurrences" do
|
276
|
+
before do
|
277
|
+
@schedule = Schedule.new @time, Rule.weekly.count(10)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should list occurrences" do
|
281
|
+
expected = [2, 9, 16, 23, 30].map { |i| Time.local(1997, 9, i, 9)} +
|
282
|
+
[7, 14, 21, 28].map { |i| Time.local(1997, 10, i, 9)} +
|
283
|
+
[4].map { |i| Time.local(1997, 11, i, 9)}
|
284
|
+
|
285
|
+
@schedule.occurrences.should == expected
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "weekly until December 24, 1997" do
|
290
|
+
before do
|
291
|
+
@schedule = Schedule.new @time, Rule.weekly.stop(Time.local(1997, 12, 24))
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should list occurrences" do
|
295
|
+
expected = [2, 9, 16, 23, 30].map { |i| Time.local(1997, 9, i, 9) } +
|
296
|
+
[7, 14, 21, 28].map { |i| Time.local(1997, 10, i, 9) } +
|
297
|
+
[4, 11, 18, 25].map { |i| Time.local(1997, 11, i, 9) } +
|
298
|
+
[2, 9, 16, 23].map { |i| Time.local(1997, 12, i, 9) }
|
299
|
+
|
300
|
+
@schedule.occurrences.should == expected
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe "every other week forever" do
|
305
|
+
before do
|
306
|
+
@schedule = Schedule.new @time, Rule.weekly(2)
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should list first 14 occurrences" do
|
310
|
+
expected = [2, 16, 30].map { |i| Time.local(1997, 9, i, 9) } +
|
311
|
+
[14, 28].map { |i| Time.local(1997, 10, i, 9) } +
|
312
|
+
[11, 25].map { |i| Time.local(1997, 11, i, 9) } +
|
313
|
+
[9, 23].map { |i| Time.local(1997, 12, i, 9) } +
|
314
|
+
[6, 20].map { |i| Time.local(1998, 1, i, 9) } +
|
315
|
+
[3, 17].map { |i| Time.local(1998, 2, i, 9) }
|
316
|
+
|
317
|
+
@schedule.first(13).should == expected
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "weekly on tuesday and thursday for five weeks" do
|
322
|
+
before do
|
323
|
+
@stop_schedule = Schedule.new @time, Rule.weekly.weekdays(:tue, :thu).stop(Time.local(1997, 10, 7))
|
324
|
+
@count_schedule = Schedule.new @time, Rule.weekly.weekdays(:tue, :thu).count(10)
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should list occurrences" do
|
328
|
+
expected = [2, 4, 9, 11, 16, 18, 23, 25, 30].map { |i| Time.local(1997, 9, i, 9) } +
|
329
|
+
[2].map { |i| Time.local(1997, 10, i, 9) }
|
330
|
+
|
331
|
+
@stop_schedule.occurrences.should == expected
|
332
|
+
@count_schedule.occurrences.should == expected
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe "every other week on Monday, Wednesday, and Friday until December 24, 1997" do
|
337
|
+
# starting on Monday, September 1, 1997
|
338
|
+
before do
|
339
|
+
@time = Time.local(1997, 9, 1, 9)
|
340
|
+
@schedule = Schedule.new @time, Rule.weekly(2).weekdays(:mon, :wed, :fri).stop(Time.local(1997, 12, 24))
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should list occurrences" do
|
344
|
+
expected = [1, 3, 5, 15, 17, 19, 29].map { |i| Time.local(1997, 9, i, 9) } +
|
345
|
+
[1, 3, 13, 15, 17, 27, 29, 31].map { |i| Time.local(1997, 10, i, 9) } +
|
346
|
+
[10, 12, 14, 24, 26, 28].map { |i| Time.local(1997, 11, i, 9) } +
|
347
|
+
[8, 10, 12, 22].map { |i| Time.local(1997, 12, i, 9) }
|
348
|
+
|
349
|
+
@schedule.occurrences.should == expected
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe "every other week on Tuesday and Thursday, for 8 occurrences" do
|
354
|
+
before do
|
355
|
+
@schedule = Schedule.new @time, Rule.weekly(2).weekdays(:tu, :th).count(8)
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should list occurrences" do
|
359
|
+
expected = [2, 4, 16, 18, 30].map { |i| Time.local(1997, 9, i, 9) } +
|
360
|
+
[2, 14, 16].map { |i| Time.local(1997, 10, i, 9) }
|
361
|
+
|
362
|
+
@schedule.occurrences.should == expected
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
describe "monthly on the first friday for 10 occurrences" do
|
367
|
+
before do
|
368
|
+
@schedule = Schedule.new @time, Rule.monthly.weekdays(:friday => 1).count(10)
|
369
|
+
end
|
370
|
+
|
371
|
+
it "should list occurrences" do
|
372
|
+
expected = [[9, 5], [10, 3], [11, 7], [12, 5]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
373
|
+
[[1, 2], [2, 6], [3, 6], [4, 3], [5, 1], [6, 5]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
374
|
+
|
375
|
+
@schedule.occurrences.should == expected
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe "monthly on the first friday until December 24, 1997" do
|
380
|
+
before do
|
381
|
+
@schedule = Schedule.new @time, Rule.monthly.weekdays(:friday => 1).stop(Time.local(1997, 12, 24))
|
382
|
+
end
|
383
|
+
|
384
|
+
it "should list occurrences" do
|
385
|
+
expected = [[9, 5], [10, 3], [11, 7], [12, 5]].map {|d| Time.local(1997, d[0], d[1], 9) }
|
386
|
+
|
387
|
+
@schedule.occurrences.should == expected
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "every other month on the first and last Sunday of the month for 10 occurrences" do
|
392
|
+
before do
|
393
|
+
@schedule = Schedule.new @time, Rule.monthly(2).weekdays(:sunday => [1, -1]).count(10)
|
394
|
+
end
|
395
|
+
|
396
|
+
it "should list occurrences" do
|
397
|
+
expected = [[9, 7], [9, 28], [11, 2], [11, 30]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
398
|
+
[[1, 4], [1, 25], [3, 1], [3, 29], [5, 3], [5, 31]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
399
|
+
|
400
|
+
@schedule.occurrences.should == expected
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
describe "monthly on the second-to-last monday of the month for 6 months" do
|
405
|
+
before do
|
406
|
+
@schedule = Schedule.new @time, Rule.monthly.weekdays(:monday => -2).count(6)
|
407
|
+
end
|
408
|
+
|
409
|
+
it "should list occurrences" do
|
410
|
+
expected = [[9, 22], [10, 20], [11, 17], [12, 22]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
411
|
+
[[1, 19], [2, 16]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
412
|
+
|
413
|
+
@schedule.occurrences.should == expected
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
describe "monthly on the third-to-the-last day of the month, forever" do
|
418
|
+
before do
|
419
|
+
@schedule = Schedule.new @time, Rule.monthly.monthday(-3)
|
420
|
+
end
|
421
|
+
|
422
|
+
it "should list 6 occurrences" do
|
423
|
+
expected = [[9, 28], [10, 29], [11, 28], [12, 29]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
424
|
+
[[1, 29], [2, 26]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
425
|
+
|
426
|
+
@schedule.first(6).should == expected
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe "monthly on the 2nd and 15th of the month for 10 occurrences" do
|
431
|
+
before do
|
432
|
+
@schedule = Schedule.new @time, Rule.monthly.monthdays(2, 15).count(10)
|
433
|
+
end
|
434
|
+
|
435
|
+
it "should list occurrences" do
|
436
|
+
expected = [[9, 2], [9, 15], [10, 2], [10, 15], [11, 2], [11, 15], [12, 2], [12, 15]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
437
|
+
[[1, 2], [1, 15]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
438
|
+
|
439
|
+
@schedule.occurrences.should == expected
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
describe "monthly on the first and last day of the month for 10 occurrences" do
|
444
|
+
before do
|
445
|
+
@schedule = Schedule.new @time, Rule.monthly.monthdays(1, -1).count(10)
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should list occurrences" do
|
449
|
+
expected = [[9, 30], [10, 1], [10, 31], [11, 1], [11, 30], [12, 1], [12, 31]].map {|d| Time.local(1997, d[0], d[1], 9) } +
|
450
|
+
[[1, 1], [1, 31], [2, 1]].map {|d| Time.local(1998, d[0], d[1], 9) }
|
451
|
+
|
452
|
+
@schedule.occurrences.should == expected
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
describe "every 18 months on the 10th thru 15th of the month for 10 occurrences" do
|
457
|
+
before do
|
458
|
+
@schedule = Schedule.new @time, Rule.monthly(18).monthdays(*(10..15).to_a).count(10)
|
459
|
+
end
|
460
|
+
|
461
|
+
it "should list occurrences" do
|
462
|
+
expected = (10..15).map {|d| Time.local(1997, 9, d, 9) } +
|
463
|
+
(10..13).map {|d| Time.local(1999, 3, d, 9) }
|
464
|
+
|
465
|
+
@schedule.occurrences.should == expected
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe "every Tuesday, every other month" do
|
470
|
+
before do
|
471
|
+
@schedule = Schedule.new @time, Rule.monthly(2).weekdays(:tue)
|
472
|
+
end
|
473
|
+
|
474
|
+
it "should list 18 occurrences" do
|
475
|
+
expected = [2, 9, 16, 23, 30].map {|d| Time.local(1997, 9, d, 9)} +
|
476
|
+
[4, 11, 18, 25].map {|d| Time.local(1997, 11, d, 9)} +
|
477
|
+
[6, 13, 20, 27].map {|d| Time.local(1998, 1, d, 9)} +
|
478
|
+
[3, 10, 17, 24, 31].map {|d| Time.local(1998, 3, d, 9)}
|
479
|
+
|
480
|
+
@schedule.first(18).should == expected
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe "yearly in June and July for 10 occurrences" do
|
485
|
+
before do
|
486
|
+
@schedule = Schedule.new Time.local(1997, 6, 10, 9), Rule.yearly.months(6, 7).count(10)
|
487
|
+
end
|
488
|
+
|
489
|
+
it "shoul lists occurrences" do
|
490
|
+
expected = [1997, 1998, 1999, 2000, 2001].map {|y| [Time.local(y, 6, 10, 9), Time.local(y, 7, 10, 9)] }.flatten
|
491
|
+
|
492
|
+
@schedule.occurrences.should == expected
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe "every other year on January, February, and March for 10 occurrences" do
|
497
|
+
before do
|
498
|
+
@schedule = Schedule.new Time.local(1997, 3, 10, 9), Rule.yearly(2).months(1, 2, 3).count(10)
|
499
|
+
end
|
500
|
+
|
501
|
+
it "should list occurrences" do
|
502
|
+
expected = [Time.local(1997, 3, 10, 9)] + [1999, 2001, 2003].map {|y| [1, 2, 3].map {|m| Time.local(y, m, 10, 9)}}.flatten
|
503
|
+
|
504
|
+
@schedule.occurrences.should == expected
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
describe "every third year on the 1st, 100th, and 200th day for 10 occurrences" do
|
509
|
+
before do
|
510
|
+
@schedule = Schedule.new Time.local(1997, 1, 1, 9), Rule.yearly(3).yeardays(1, 100, 200).count(10)
|
511
|
+
end
|
512
|
+
|
513
|
+
it "should list occurrences" do
|
514
|
+
expected = [[1997, 1, 1, 9], [1997, 4, 10, 9], [1997, 7, 19, 9], [2000, 1, 1, 9], [2000, 4, 9, 9],
|
515
|
+
[2000, 7, 18, 9], [2003, 1, 1, 9], [2003, 4, 10, 9], [2003, 7, 19, 9], [2006, 1, 1, 9]].map {|d| Time.local(d[0], d[1], d[2], d[3])}
|
516
|
+
|
517
|
+
@schedule.occurrences.should == expected
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
describe "every 20th Monday of the year, forever" do
|
522
|
+
before do
|
523
|
+
@schedule = Schedule.new Time.local(1997, 5, 19, 9), Rule.yearly.weekdays(:mon => 20)
|
524
|
+
end
|
525
|
+
|
526
|
+
it "should list 3 occurrences" do
|
527
|
+
expected = [Time.local(1997, 5, 19, 9), Time.local(1998, 5, 18, 9), Time.local(1999, 5, 17, 9)]
|
528
|
+
|
529
|
+
@schedule.first(3).should == expected
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
# skipped: Monday of week number 20 (where the default start of the week is Monday), forever
|
534
|
+
|
535
|
+
describe "every Thursday in March, forever" do
|
536
|
+
before do
|
537
|
+
@schedule = Schedule.new Time.local(1997, 3, 13, 9), Rule.yearly.month(:march).weekday(:thursday)
|
538
|
+
end
|
539
|
+
|
540
|
+
it "should list 11 occurrences" do
|
541
|
+
expected = [13, 20, 27].map {|d| Time.local(1997, 3, d, 9)} +
|
542
|
+
[5, 12, 19, 26].map {|d| Time.local(1998, 3, d, 9)} +
|
543
|
+
[4, 11, 18, 25].map {|d| Time.local(1999, 3, d, 9)}
|
544
|
+
|
545
|
+
@schedule.first(11).should == expected
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
describe "every Thursday, but only during June, July, and August, forever" do
|
550
|
+
before do
|
551
|
+
@schedule = Schedule.new Time.local(1997, 6, 5, 9), Rule.yearly.months(6, 7, 8).weekday(:thu)
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should list first X occurrences" do
|
555
|
+
expected = [5, 12, 19, 26].map {|d| Time.local(1997, 6, d, 9)} +
|
556
|
+
[3, 10, 17, 24, 31].map {|d| Time.local(1997, 7, d, 9)} +
|
557
|
+
[7, 14, 21, 28].map {|d| Time.local(1997, 8, d, 9)} +
|
558
|
+
[4, 11, 18, 25].map {|d| Time.local(1998, 6, d, 9)} +
|
559
|
+
[2, 9, 16, 23, 30].map {|d| Time.local(1998, 7, d, 9)} +
|
560
|
+
[6, 13, 20, 27].map {|d| Time.local(1998, 8, d, 9)} +
|
561
|
+
[3, 10, 17, 24].map {|d| Time.local(1999, 6, d, 9)} +
|
562
|
+
[1, 8, 15, 22, 29].map {|d| Time.local(1999, 7, d, 9)} +
|
563
|
+
[5, 12, 19, 26].map {|d| Time.local(1999, 8, d, 9)}
|
564
|
+
|
565
|
+
@schedule.first(39).should == expected
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
describe "every Friday the 13th, forever" do
|
570
|
+
before do
|
571
|
+
@schedule = Schedule.new Time.local(1997, 9, 2, 9), Rule.monthly.weekday(:fri).monthday(13)
|
572
|
+
end
|
573
|
+
|
574
|
+
it "should list 5 occurrences" do
|
575
|
+
expected = [Time.local(1998, 2, 13, 9), Time.local(1998, 3, 13, 9), Time.local(1998, 11, 13, 9),
|
576
|
+
Time.local(1999, 8, 13, 9), Time.local(2000, 10, 13, 9)]
|
577
|
+
|
578
|
+
@schedule.first(5).should == expected
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
describe "the first Saturday that follows the first Sunday of the month, forever" do
|
583
|
+
before do
|
584
|
+
@schedule = Schedule.new Time.local(1997, 9, 13, 9), Rule.monthly.weekday(:sat).monthdays(*(7..13))
|
585
|
+
end
|
586
|
+
|
587
|
+
it "should list 10 occurrences" do
|
588
|
+
expected = [[9, 13], [10, 11], [11, 8], [12, 13]].map { |d| Time.local(1997, d[0], d[1], 9) } +
|
589
|
+
[[1, 10], [2, 7], [3, 7], [4, 11], [5, 9], [6, 13]].map { |d| Time.local(1998, d[0], d[1], 9) }
|
590
|
+
|
591
|
+
@schedule.first(10).should == expected
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
describe "every 4 years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day):" do
|
596
|
+
before do
|
597
|
+
@schedule = Schedule.new Time.local(1996, 11, 5, 9), Rule.yearly(4).month(11).weekday(:tue).monthdays(*(2..8))
|
598
|
+
end
|
599
|
+
|
600
|
+
it "should list first 3 election days" do
|
601
|
+
expected = [Time.local(1996, 11, 5, 9), Time.local(2000, 11, 7, 9), Time.local(2004, 11, 2, 9)]
|
602
|
+
|
603
|
+
@schedule.first(3).should == expected
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
# skipped most of the the rest - i.e. we're missing implementation of BYSETPOS and less-than-a-day recurrence rules
|
608
|
+
|
609
|
+
describe "ignoring an invalid date (i.e., February 30)" do
|
610
|
+
before do
|
611
|
+
@schedule = Schedule.new Time.local(2007, 1, 15, 9), Rule.monthly.monthdays(15, 30).count(5)
|
612
|
+
end
|
613
|
+
|
614
|
+
it "should list occurrences" do
|
615
|
+
expected = [[1, 15], [1, 30], [2, 15], [3, 15], [3, 30]].map {|d| Time.local(2007, d[0], d[1], 9) }
|
616
|
+
|
617
|
+
@schedule.occurrences.should == expected
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
describe "serialization" do
|
623
|
+
before do
|
624
|
+
@schedule = Schedule.new(Time.local(2000, 1, 1, 10), Rule.monthly.monthdays(1).count(10))
|
625
|
+
@schedule.end_time = (Time.local(2000, 1, 1, 10, 10))
|
626
|
+
end
|
627
|
+
|
628
|
+
it "should do a hash round trip" do
|
629
|
+
h = @schedule.to_hash
|
630
|
+
s = Schedule.from_hash(h)
|
631
|
+
|
632
|
+
s.start_time.should == @schedule.start_time
|
633
|
+
s.end_time.should == @schedule.end_time
|
634
|
+
|
635
|
+
s.rule.class.should == @schedule.rule.class
|
636
|
+
s.rule.step.should == @schedule.rule.step
|
637
|
+
end
|
638
|
+
|
639
|
+
it "should do a JSON round trip" do
|
640
|
+
j = @schedule.to_json
|
641
|
+
s = Schedule.from_json(j)
|
642
|
+
|
643
|
+
s.start_time.should == @schedule.start_time
|
644
|
+
s.end_time.should == @schedule.end_time
|
645
|
+
|
646
|
+
s.rule.class.should == @schedule.rule.class
|
647
|
+
s.rule.step.should == @schedule.rule.step
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|