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.
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe MonthsFilter do
4
+ before do
5
+ @filter = MonthsFilter.new(:january, :feb, 3)
6
+ end
7
+
8
+ it "should accept months as constructor arguments" do
9
+ @filter.months.should == [1, 2, 3]
10
+ end
11
+
12
+ it "should provide next valid date" do
13
+ @filter.next(Time.utc(2009, 5, 10, 21, 13, 11)).should == Time.utc(2010, 1, 1, 21, 13, 11)
14
+ @filter.next(Time.utc(2009, 12, 10, 21, 13, 11)).should == Time.utc(2010, 1, 1, 21, 13, 11)
15
+ @filter.next(Time.utc(2010, 1, 10, 21, 13, 11)).should == Time.utc(2010, 1, 10, 21, 13, 11)
16
+ @filter.next(Time.utc(2010, 2, 14, 21, 13, 11)).should == Time.utc(2010, 2, 14, 21, 13, 11)
17
+ @filter.next(Time.utc(2010, 3, 14, 21, 13, 11)).should == Time.utc(2010, 3, 14, 21, 13, 11)
18
+ end
19
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe WeekdaysFilter do
4
+ before do
5
+ @filter = WeekdaysFilter.new(:monday, :tue, 5)
6
+ end
7
+
8
+ it "should accept weekdays as constructor arguments" do
9
+ @filter.weekdays.should == [1, 2, 5]
10
+ end
11
+
12
+ it "should accept weekdays with order as well" do
13
+ rule = MonthlyRule.new
14
+ filter = WeekdaysFilter.new(rule, :tue, :monday => 1, :tuesday => [1, -1])
15
+
16
+ filter.weekdays.should == [2]
17
+ filter.ordered_weekdays.should == {:mo => [1], :tu => [-1, 1]}
18
+ end
19
+
20
+ it "should match only the selected weekdays" do
21
+ filter = WeekdaysFilter.new(:mon, :fri)
22
+
23
+ filter.match?(Time.utc(2000, 1, 3)).should be_true
24
+ filter.match?(Time.utc(2000, 1, 7)).should be_true
25
+ filter.match?(Time.utc(2000, 1, 10)).should be_true
26
+
27
+ filter.match?(Time.utc(2000, 1, 2)).should be_false
28
+ filter.match?(Time.utc(2000, 1, 4)).should be_false
29
+ filter.match?(Time.utc(2000, 1, 5)).should be_false
30
+ filter.match?(Time.utc(2000, 1, 6)).should be_false
31
+ filter.match?(Time.utc(2000, 1, 8)).should be_false
32
+ filter.match?(Time.utc(2000, 1, 9)).should be_false
33
+ end
34
+
35
+ it "should provide next valid date" do
36
+ @filter.next(Time.utc(2010, 1, 1, 21, 13, 11)).should == Time.utc(2010, 1, 1, 21, 13, 11)
37
+ @filter.next(Time.utc(2010, 1, 2, 21, 13, 11)).should == Time.utc(2010, 1, 4, 21, 13, 11)
38
+ @filter.next(Time.utc(2010, 1, 4, 21, 13, 11)).should == Time.utc(2010, 1, 4, 21, 13, 11)
39
+ @filter.next(Time.utc(2010, 1, 5, 21, 13, 11)).should == Time.utc(2010, 1, 5, 21, 13, 11)
40
+ @filter.next(Time.utc(2010, 1, 6, 21, 13, 11)).should == Time.utc(2010, 1, 8, 21, 13, 11)
41
+ end
42
+
43
+ describe "with MonthlyRule" do
44
+ before do
45
+ rule = MonthlyRule.new
46
+ @filter = WeekdaysFilter.new(rule, :mon => [-1, 1])
47
+ end
48
+
49
+ it "should store the rule" do
50
+ @filter.rule.should be_a(MonthlyRule)
51
+ end
52
+
53
+ it "should match ordered weekdays" do
54
+ # January
55
+ # wrong weekday
56
+ @filter.match?(Time.utc(2000, 1, 1)).should be_false
57
+ @filter.match?(Time.utc(2000, 1, 2)).should be_false
58
+
59
+ # right day right order
60
+ @filter.match?(Time.utc(2000, 1, 3)).should be_true
61
+ @filter.match?(Time.utc(2000, 1, 31)).should be_true
62
+
63
+ # right day wrong order
64
+ @filter.match?(Time.utc(2000, 1, 10)).should be_false
65
+ @filter.match?(Time.utc(2000, 1, 17)).should be_false
66
+ @filter.match?(Time.utc(2000, 1, 24)).should be_false
67
+
68
+ # February
69
+ # wrong weekday
70
+ @filter.match?(Time.utc(2000, 2, 1)).should be_false
71
+ @filter.match?(Time.utc(2000, 2, 2)).should be_false
72
+
73
+ # right day right order
74
+ @filter.match?(Time.utc(2000, 2, 7)).should be_true
75
+ @filter.match?(Time.utc(2000, 2, 28)).should be_true
76
+
77
+ # right day wrong order
78
+ @filter.match?(Time.utc(2000, 2, 14)).should be_false
79
+ @filter.match?(Time.utc(2000, 2, 21)).should be_false
80
+ end
81
+ end
82
+
83
+ describe "with YearlyRule" do
84
+ before do
85
+ rule = YearlyRule.new
86
+ @filter = WeekdaysFilter.new(rule, :mon => [-1, 1])
87
+ end
88
+
89
+ it "should store the rule" do
90
+ @filter.rule.should be_a(YearlyRule)
91
+ end
92
+
93
+ it "should match ordered weekdays" do
94
+ # wrong weekday
95
+ @filter.match?(Time.utc(2000, 1, 1)).should be_false
96
+ @filter.match?(Time.utc(2000, 1, 2)).should be_false
97
+
98
+ # right day right order
99
+ @filter.match?(Time.utc(2000, 1, 3)).should be_true
100
+ @filter.match?(Time.utc(2000, 12, 25)).should be_true
101
+
102
+ # right day wrong order
103
+ @filter.match?(Time.utc(2000, 1, 10)).should be_false
104
+ @filter.match?(Time.utc(2000, 1, 17)).should be_false
105
+ @filter.match?(Time.utc(2000, 1, 24)).should be_false
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe do
4
+ before do
5
+ @filter = YeardaysFilter.new(1, 50, -50)
6
+ end
7
+
8
+ it "should accept weekdays as constructor arguments" do
9
+ @filter.yeardays.should == [-50, 1, 50]
10
+ end
11
+
12
+
13
+ it "should match only the selected monthdays" do
14
+ filter = YeardaysFilter.new(1, 100, 200, -10)
15
+
16
+ filter.match?(Time.utc(2000, 1, 1)).should be_true
17
+ filter.match?(Time.utc(2000, 4, 9)).should be_true
18
+ filter.match?(Time.utc(2000, 7, 18)).should be_true
19
+ filter.match?(Time.utc(2000, 12, 22)).should be_true
20
+
21
+ filter.match?(Time.utc(2000, 2, 26)).should be_false
22
+
23
+ filter.match?(Time.utc(2000, 1, 2)).should be_false
24
+ filter.match?(Time.utc(2000, 4, 4)).should be_false
25
+ filter.match?(Time.utc(2000, 8, 5)).should be_false
26
+ filter.match?(Time.utc(2000, 11, 6)).should be_false
27
+ filter.match?(Time.utc(2000, 11, 8)).should be_false
28
+ filter.match?(Time.utc(2000, 12, 27)).should be_false
29
+ end
30
+
31
+ it "should provide next valid date" do
32
+ @filter.next(Time.utc(2010, 1, 1, 21, 13, 11)).should == Time.utc(2010, 1, 1, 21, 13, 11)
33
+ @filter.next(Time.utc(2010, 1, 2, 21, 13, 11)).should == Time.utc(2010, 2, 19, 21, 13, 11)
34
+ @filter.next(Time.utc(2010, 2, 30, 21, 13, 11)).should == Time.utc(2010, 11, 12, 21, 13, 11)
35
+ end
36
+ end
@@ -0,0 +1,207 @@
1
+ require 'spec_helper'
2
+
3
+ describe Occurrence do
4
+ before do
5
+ @start_time = Time.utc(2000, 1, 1, 9, 30)
6
+ @occurrence = Occurrence.new(Rule.daily, @start_time)
7
+ end
8
+
9
+ it "should have read only rule attribute" do
10
+ @occurrence.rule.should be_a(Rule)
11
+ end
12
+
13
+ it "should find next occurrence" do
14
+ # simple cases
15
+ @occurrence.next_occurrence(Time.utc(2010, 1, 1)).should == Time.utc(2010, 1, 1, 9, 30)
16
+ @occurrence.next_occurrence(Time.utc(2010, 1, 1, 10)).should == Time.utc(2010, 1, 2, 9, 30)
17
+
18
+ # boundary cases
19
+ @occurrence.next_occurrence(Time.utc(2010, 1, 1, 9, 30)).should == Time.utc(2010, 1, 1, 9, 30)
20
+ @occurrence.next_occurrence(@start_time).should == @start_time
21
+
22
+ # limit case
23
+ @occurrence.next_occurrence(@start_time).should == @start_time
24
+ @occurrence.next_occurrence(Time.utc(1990, 1, 1)).should == @start_time
25
+ end
26
+
27
+ it "should find next n occurrences" do
28
+ # simple cases
29
+ expected = [Time.utc(2010, 1, 1, 9, 30), Time.utc(2010, 1, 2, 9, 30), Time.utc(2010, 1, 3, 9, 30), Time.utc(2010, 1, 4, 9, 30)]
30
+ first_three = [@start_time, @start_time + 1.day, @start_time + 2.days]
31
+
32
+ @occurrence.next_occurrences(3, Time.utc(2010, 1, 1)).should == expected[0..2]
33
+ @occurrence.next_occurrences(3, Time.utc(2010, 1, 1, 10)).should == expected[1..3]
34
+
35
+ # boundary cases
36
+ @occurrence.next_occurrences(3, Time.utc(2010, 1, 1, 9, 30)).should == expected[0..2]
37
+ @occurrence.next_occurrences(3, @start_time).should == first_three
38
+
39
+ # limit case
40
+ @occurrence.next_occurrences(3, @start_time).should == first_three
41
+ @occurrence.next_occurrences(3, Time.utc(1990, 1, 1)).should == first_three
42
+
43
+ # other lengths
44
+ @occurrence.next_occurrences(5, Time.utc(2010, 1, 1)).length.should == 5
45
+ @occurrence.next_occurrences(15, Time.utc(2010, 1, 1)).length.should == 15
46
+ end
47
+
48
+ it "should find previous occurrence" do
49
+ base = Time.utc(2000, 1, 1, 9, 30)
50
+
51
+ # simple cases
52
+ @occurrence.previous_occurrence(Time.utc(2010, 1, 1)).should == Time.utc(2009, 12, 31, 9, 30)
53
+ @occurrence.previous_occurrence(Time.utc(2010, 1, 1, 10)).should == Time.utc(2010, 1, 1, 9, 30)
54
+
55
+ # boundary cases
56
+ @occurrence.previous_occurrence(Time.utc(2010, 1, 1, 9, 30)).should == Time.utc(2009, 12, 31, 9, 30)
57
+
58
+ # limit cases
59
+ @occurrence.previous_occurrence(@start_time).should == nil
60
+ @occurrence.previous_occurrence(Time.utc(1990, 1, 1)).should == nil
61
+ end
62
+
63
+ it "should find previous n occurrences" do
64
+ # simple cases
65
+ expected = [Time.utc(2009, 12, 29, 9, 30), Time.utc(2009, 12, 30, 9, 30), Time.utc(2009, 12, 31, 9, 30), Time.utc(2010, 1, 1, 9, 30)]
66
+
67
+ # simple cases
68
+ @occurrence.previous_occurrences(3, Time.utc(2010, 1, 1)).should == expected[0..2]
69
+ @occurrence.previous_occurrences(3, Time.utc(2010, 1, 1, 10)).should == expected[1..3]
70
+
71
+ # boundary cases
72
+ @occurrence.previous_occurrences(3, Time.utc(2010, 1, 1, 9, 30)).should == expected[0..2]
73
+
74
+ # limit case
75
+ @occurrence.previous_occurrences(3, @start_time).should be_empty
76
+ @occurrence.previous_occurrences(3, Time.utc(1990, 1, 1)).should be_empty
77
+
78
+ # other lengths
79
+ @occurrence.previous_occurrences(5, Time.utc(2010, 1, 1)).length.should == 5
80
+ @occurrence.previous_occurrences(15, Time.utc(2010, 1, 1)).length.should == 15
81
+ end
82
+
83
+ it "should find occurrences between dates" do
84
+ expected = [Time.utc(2010, 1, 1, 9, 30), Time.utc(2010, 1, 2, 9, 30), Time.utc(2010, 1, 3, 9, 30), Time.utc(2010, 1, 4, 9, 30)]
85
+
86
+ # simple cases
87
+ @occurrence.occurrences_between(Time.utc(2010, 1, 1), Time.utc(2010, 1, 4, 10)).should == expected
88
+ @occurrence.occurrences_between(Time.utc(2010, 1, 1, 10), Time.utc(2010, 1, 4, 10)).should == expected[1..3]
89
+ @occurrence.occurrences_between(Time.utc(2010, 1, 1), Time.utc(2010, 1, 4, 0)).should == expected[0..2]
90
+
91
+ # boundary cases
92
+ @occurrence.occurrences_between(Time.utc(2010, 1, 1, 9, 30), Time.utc(2010, 1, 4, 9, 30)).should == expected[0..2]
93
+
94
+ # limit cases
95
+ @occurrence.occurrences_between(@start_time, Time.utc(2000, 1, 2, 9, 30)).first.should == @start_time
96
+ @occurrence.occurrences_between(Time.utc(1990, 1, 1), Time.utc(2000, 1, 2, 9, 30)).first.should == @start_time
97
+ end
98
+
99
+ describe "finite rules" do
100
+ before do
101
+ @start_time = Time.utc(2000, 1, 1, 9, 30)
102
+ @count_occurrence = Occurrence.new(Rule.daily.count(10), @start_time)
103
+ @stop_occurrence = Occurrence.new(Rule.daily.stop(Time.utc(2000, 1, 15, 10)), @start_time)
104
+ end
105
+
106
+ it "should find all occurrences" do
107
+ @count_occurrence.all.length.should == 10
108
+ @stop_occurrence.all.length.should == 15
109
+ end
110
+
111
+ it "should limit occurrences between" do
112
+ # whole interval
113
+ @count_occurrence.occurrences_between(Time.utc(1990, 1, 1), Time.utc(2001, 1, 1)).length.should == 10
114
+ @stop_occurrence.occurrences_between(Time.utc(1990, 1, 1), Time.utc(2001, 1, 1)).length.should == 15
115
+
116
+ # stop in interval
117
+ @count_occurrence.occurrences_between(Time.utc(1990, 1, 1), Time.utc(2000, 1, 5)).length.should == 4
118
+ @stop_occurrence.occurrences_between(Time.utc(1990, 1, 1), Time.utc(2000, 1, 5)).length.should == 4
119
+
120
+ # start in interval
121
+ @count_occurrence.occurrences_between(Time.utc(2000, 1, 5), Time.utc(2001, 1, 1)).length.should == 6
122
+ @stop_occurrence.occurrences_between(Time.utc(2000, 1, 5), Time.utc(2001, 1, 1)).length.should == 11
123
+ end
124
+
125
+ it "should limit next occurrences" do
126
+ # before interval
127
+ @count_occurrence.next_occurrences(5, Time.utc(1990, 1, 1)).length.should == 5
128
+ @stop_occurrence.next_occurrences(5, Time.utc(1990, 1, 1)).length.should == 5
129
+
130
+ # inside interval
131
+ @count_occurrence.next_occurrences(5, Time.utc(2000, 1, 8)).length.should == 3
132
+ @stop_occurrence.next_occurrences(5, Time.utc(2000, 1, 12)).length.should == 4
133
+ end
134
+
135
+ it "should limit previous occurrences" do
136
+ # inside interval
137
+ @count_occurrence.previous_occurrences(5, Time.utc(2000, 1, 4)).length.should == 3
138
+ @stop_occurrence.previous_occurrences(5, Time.utc(2000, 1, 4)).length.should == 3
139
+
140
+ # after interval
141
+ occ = @count_occurrence.previous_occurrences(5, Time.utc(2001, 1, 1))
142
+ occ.length.should == 5
143
+ occ.last.should == Time.utc(2000, 1, 10, 9, 30)
144
+
145
+ occ = @stop_occurrence.previous_occurrences(5, Time.utc(2001, 1, 1))
146
+ occ.length.should == 5
147
+ occ.last.should == Time.utc(2000, 1, 15, 9, 30)
148
+ end
149
+ end
150
+
151
+ describe "filtered rules" do
152
+ before do
153
+ @start_time = Time.utc(2000, 1, 1, 9, 30)
154
+ @weekday_filtered_occurrence = Occurrence.new(Rule.daily.weekdays(:sat, :mon), @start_time)
155
+ @month_filtered_occurrence = Occurrence.new(Rule.daily.months(:january, :sept), @start_time)
156
+ end
157
+
158
+ it "should filter next occurrences" do
159
+ expected = [1, 3, 8, 10, 15, 17, 22].map { |i| Time.utc(2000, 1, i, 9, 30) }
160
+ @weekday_filtered_occurrence.next_occurrences(7, Time.utc(2000, 1, 1, 2)).should == expected
161
+
162
+ expected = [30, 31].map { |i| Time.utc(2000, 1, i, 9, 30) } +
163
+ (1..5).map { |i| Time.utc(2000, 9, i, 9, 30) }
164
+ @month_filtered_occurrence.next_occurrences(7, Time.utc(2000, 1, 30)).should == expected
165
+ end
166
+
167
+ it "should filter previous occurrences" do
168
+ expected = [1, 3, 8, 10, 15, 17, 22].map { |i| Time.utc(2000, 1, i, 9, 30) }
169
+ @weekday_filtered_occurrence.previous_occurrences(7, Time.utc(2000, 1, 22, 10)).should == expected
170
+
171
+ expected = [30, 31].map { |i| Time.utc(2000, 1, i, 9, 30) } +
172
+ (1..5).map { |i| Time.utc(2000, 9, i, 9, 30) }
173
+ @month_filtered_occurrence.previous_occurrences(7, Time.utc(2000, 9, 6)).should == expected
174
+ end
175
+
176
+ it "should shift bad start time forward" do
177
+ start_time = Time.utc(2000, 1, 1, 10, 0, 0)
178
+ occurrence = Occurrence.new(Rule.yearly.months(3, 5).weekday(:monday), start_time)
179
+
180
+ occurrence.start_time.should_not == start_time
181
+ occurrence.start_time.should == Time.utc(2000, 3, 6, 10)
182
+ end
183
+ end
184
+
185
+ describe "suboccurrences" do
186
+ before do
187
+ @start_time = Time.utc(2000, 1, 1, 9, 30)
188
+ @occurrence = Occurrence.new(Rule.daily(3).count(10), @start_time)
189
+ @occurrence.duration = 24.hours
190
+ end
191
+
192
+ it "should report duration" do
193
+ @occurrence.duration.should == 24.hours
194
+ end
195
+
196
+ it "should find subooccurrences between dates" do
197
+ s = @occurrence.suboccurrences_between(Time.utc(2000, 1, 1, 12), Time.utc(2000, 1, 5, 6))
198
+
199
+ s.length.should == 2
200
+ s.first.start.should == Time.utc(2000, 1, 1, 12)
201
+ s.first.end.should == Time.utc(2000, 1, 2, 9, 30)
202
+
203
+ s[1].start.should == Time.utc(2000, 1, 4, 9, 30)
204
+ s[1].end.should == Time.utc(2000, 1, 5, 6)
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rule do
4
+ describe "rule factories" do
5
+ it "should create daily rule" do
6
+ Rule.daily.should be_a(DailyRule)
7
+
8
+ rule = Rule.daily(5)
9
+ rule.should be_a(DailyRule)
10
+ rule.interval.should == 5
11
+ end
12
+
13
+ it "should create yearly dule" do
14
+ Rule.yearly.should be_a(YearlyRule)
15
+
16
+ rule = Rule.yearly(5)
17
+ rule.should be_a(YearlyRule)
18
+ rule.interval.should == 5
19
+ end
20
+
21
+ it "should create weekly rule" do
22
+ Rule.weekly.should be_a(WeeklyRule)
23
+
24
+ rule = Rule.weekly(5)
25
+ rule.should be_a(WeeklyRule)
26
+ rule.interval.should == 5
27
+ end
28
+
29
+ it "should create monthly rule" do
30
+ Rule.monthly.should be_a(MonthlyRule)
31
+
32
+ rule = Rule.monthly(5)
33
+ rule.should be_a(MonthlyRule)
34
+ rule.interval.should == 5
35
+ end
36
+ end
37
+
38
+ describe "filters" do
39
+ before :each do
40
+ @rule = Rule.daily
41
+ end
42
+
43
+ it "should add single month filter and allow chaining" do
44
+ @rule.month(:january).should be_a(DailyRule)
45
+ @rule.filters.first.should be_a(MonthsFilter)
46
+
47
+ @rule.filters(:months).should_not be_nil
48
+ @rule.filters(:months).months.should == [1]
49
+ end
50
+
51
+ it "should add multiple months filter and allow chaining" do
52
+ @rule.months(:january, :march, 5).should be_a(DailyRule)
53
+ @rule.filters.first.should be_a(MonthsFilter)
54
+
55
+ @rule.filters(:months).should_not be_nil
56
+ @rule.filters(:months).months.should == [1, 3, 5]
57
+ end
58
+
59
+ it "should add single weekday filter and allow chaining" do
60
+ @rule.weekday(:mon).should be_a(DailyRule)
61
+ @rule.filters.first.should be_a(WeekdaysFilter)
62
+
63
+ @rule.filters(:weekdays).should_not be_nil
64
+ @rule.filters(:weekdays).weekdays.should == [1]
65
+ end
66
+
67
+ it "should add multiple weekdays filter and allow chaining" do
68
+ @rule.weekdays(:monday, :tue, 3).should be_a(DailyRule)
69
+ @rule.filters.first.should be_a(WeekdaysFilter)
70
+
71
+ @rule.filters(:weekdays).should_not be_nil
72
+ @rule.filters(:weekdays).weekdays.should == [1, 2, 3]
73
+ end
74
+
75
+ it "should pass rules into weekdays filter" do
76
+ @rule.weekdays(:monday, :tue, 3).should be_a(DailyRule)
77
+
78
+ @rule.filters(:weekdays).rule.should be_a(DailyRule)
79
+ end
80
+
81
+ it "should add monthday filter and allow chaining" do
82
+ @rule.monthday(28).should be_a(DailyRule)
83
+ @rule.filters.first.should be_a(MonthdaysFilter)
84
+
85
+ @rule.filters(:monthdays).should_not be_nil
86
+ @rule.filters(:monthdays).monthdays.should == [28]
87
+ end
88
+
89
+ it "should add monthday filter and allow chaining" do
90
+ @rule.monthday(-20, 13, 25).should be_a(DailyRule)
91
+ @rule.filters.first.should be_a(MonthdaysFilter)
92
+
93
+ @rule.filters(:monthdays).should_not be_nil
94
+ @rule.filters(:monthdays).monthdays.should == [-20, 13, 25]
95
+ end
96
+
97
+ it "should add multiple yeardays filter and allow chaining" do
98
+ @rule.yeardays(-30, 100, 250).should be_a(DailyRule)
99
+ @rule.filters.first.should be_a(YeardaysFilter)
100
+
101
+ @rule.filters(:yeardays).should_not be_nil
102
+ @rule.filters(:yeardays).yeardays.should == [-30, 100, 250]
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ describe DailyRule do
4
+ describe "simple rule" do
5
+ before do
6
+ @rule = DailyRule.new
7
+ end
8
+
9
+ it "should return one day as step" do
10
+ @rule.step.should == 1.day
11
+ end
12
+
13
+ it "should find next date" do
14
+ base = Time.local(2010, 1, 1, 9, 30, 21)
15
+
16
+ @rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
17
+ @rule.next(Time.local(2010, 1, 1, 9, 30, 21), base).should == Time.local(2010, 1, 2, 9, 30, 21)
18
+ @rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 2, 9, 30, 21)
19
+
20
+ # align forward should include base time
21
+ @rule.next(base, base).should == base + 1.day
22
+ end
23
+
24
+ it "should find previous date" do
25
+ base = Time.local(2010, 1, 1, 9, 30, 21)
26
+
27
+ @rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 12, 31, 9, 30, 21)
28
+ @rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
29
+
30
+ # align back should exclude base time
31
+ @rule.previous(base, base).should == Time.local(2009, 12, 31, 9, 30, 21)
32
+ end
33
+
34
+ it "should match dates" do
35
+ @rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 12, 9, 30)).should be_true
36
+ @rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 12, 9, 29)).should_not be_true
37
+ end
38
+ end
39
+
40
+ describe "finite rules" do
41
+ before do
42
+ @rule = DailyRule.new
43
+ end
44
+
45
+ it "should accept count and allow chaining" do
46
+ @rule.count(10).should be_a(DailyRule)
47
+ @rule.count.should == 10
48
+ end
49
+
50
+ it "should accept stop time" do
51
+ @rule.stop(Time.utc(2020, 1, 1)).should be_a(DailyRule)
52
+ @rule.stop.should == Time.utc(2020, 1, 1)
53
+ end
54
+
55
+ it "should report rule finiteness" do
56
+ DailyRule.new.count(5).should be_finite
57
+ DailyRule.new.stop(Time.utc(2020, 1, 1)).should be_finite
58
+
59
+ DailyRule.new.should_not be_finite
60
+ end
61
+ end
62
+
63
+ describe "rule with interval" do
64
+ before do
65
+ @every_other_day_rule = DailyRule.new(2)
66
+ @every_ten_days_rule = DailyRule.new(10)
67
+ end
68
+
69
+ it "should return corect step" do
70
+ @every_other_day_rule.step.should == 2.days
71
+ @every_ten_days_rule.step.should == 10.days
72
+ end
73
+
74
+ it "should find next date" do
75
+ base = Time.local(2010, 1, 1, 9, 30, 21)
76
+
77
+ @every_other_day_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
78
+ @every_ten_days_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
79
+
80
+ @every_other_day_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 3, 9, 30, 21)
81
+ @every_ten_days_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 11, 9, 30, 21)
82
+
83
+ # align forward should include base time
84
+ @every_other_day_rule.next(base, base).should == base + 2.days
85
+ @every_ten_days_rule.next(base, base).should == base + 10.days
86
+ end
87
+
88
+ it "should align dates back" do
89
+ base = Time.local(2010, 1, 1, 9, 30, 21)
90
+
91
+ @every_other_day_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 12, 30, 9, 30, 21)
92
+ @every_ten_days_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 12, 22, 9, 30, 21)
93
+
94
+ @every_other_day_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
95
+ @every_ten_days_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
96
+
97
+ # align forward should include base time
98
+ @every_other_day_rule.previous(base, base).should == base - 2.days
99
+ @every_ten_days_rule.previous(base, base).should == base - 10.days
100
+ end
101
+
102
+ it "should check date alignment" do
103
+ base = Time.local(2010, 1, 1, 9, 30)
104
+
105
+ @every_other_day_rule.match?(Time.local(2010, 1, 1, 9, 30), base).should be_true
106
+ @every_other_day_rule.match?(Time.local(2010, 1, 3, 9, 30), base).should be_true
107
+
108
+ @every_ten_days_rule.match?(Time.local(2010, 1, 1, 9, 30), base).should be_true
109
+ @every_ten_days_rule.match?(Time.local(2010, 1, 11, 9, 30), base).should be_true
110
+
111
+ @every_other_day_rule.match?(Time.local(2010, 1, 2, 9, 30), base).should_not be_true
112
+ @every_ten_days_rule.match?(Time.local(2010, 1, 5, 9, 30), base).should_not be_true
113
+ end
114
+ end
115
+
116
+ describe "serialization" do
117
+ it "should do a simple hash roundtrip" do
118
+ h = Rule.daily(5).to_hash
119
+ restore = Rule.from_hash(h)
120
+
121
+ restore.class.should == DailyRule
122
+ restore.interval.should == 5
123
+ restore.step.should == 5.days
124
+ end
125
+
126
+ it "should do a hash roundtrip with count" do
127
+ h = Rule.daily.count(10).to_hash
128
+ restore = Rule.from_hash(h)
129
+
130
+ restore.count.should == 10
131
+ end
132
+
133
+ it "should do a hash roundtrip with stop" do
134
+ h = Rule.daily.stop(Time.local(2020, 10, 10)).to_hash
135
+ restore = Rule.from_hash(h)
136
+
137
+ restore.stop.should == Time.local(2020, 10, 10)
138
+ end
139
+ end
140
+ end