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,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
|