cyclical 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,218 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe MonthlyRule do
|
6
|
+
describe "simple rule" do
|
7
|
+
before do
|
8
|
+
@rule = MonthlyRule.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return one day as step" do
|
12
|
+
@rule.step.should == 1.month
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should give next date" do
|
16
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
17
|
+
|
18
|
+
@rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
19
|
+
@rule.next(Time.local(2010, 1, 1, 9, 30, 21), base).should == Time.local(2010, 2, 1, 9, 30, 21)
|
20
|
+
@rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 2, 1, 9, 30, 21)
|
21
|
+
|
22
|
+
# align forward should include base time
|
23
|
+
@rule.next(base, base).should == base + 1.month
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should give previous date" do
|
27
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
28
|
+
|
29
|
+
@rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 12, 1, 9, 30, 21)
|
30
|
+
@rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
31
|
+
|
32
|
+
# align back should exclude base time
|
33
|
+
@rule.previous(base, base).should == Time.local(2009, 12, 1, 9, 30, 21)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should match dates" do
|
37
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
38
|
+
|
39
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 29)).should_not be_true
|
40
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 8, 9, 30)).should_not be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "finite rules" do
|
45
|
+
before do
|
46
|
+
@rule = MonthlyRule.new
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should accept count and allow chaining" do
|
50
|
+
@rule.count(10).should be_a(MonthlyRule)
|
51
|
+
@rule.count.should == 10
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should accept stop time" do
|
55
|
+
@rule.stop(Time.utc(2020, 1, 1)).should be_a(MonthlyRule)
|
56
|
+
@rule.stop.should == Time.utc(2020, 1, 1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should report rule finiteness" do
|
60
|
+
MonthlyRule.new.count(5).should be_finite
|
61
|
+
MonthlyRule.new.stop(Time.utc(2020, 1, 1)).should be_finite
|
62
|
+
|
63
|
+
MonthlyRule.new.should_not be_finite
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "rule with interval" do
|
68
|
+
before do
|
69
|
+
@every_other_month_rule = MonthlyRule.new(2)
|
70
|
+
@every_ten_months_rule = MonthlyRule.new(10)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return corect step" do
|
74
|
+
@every_other_month_rule.step.should == 2.months
|
75
|
+
@every_ten_months_rule.step.should == 10.months
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should find next date" do
|
79
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
80
|
+
|
81
|
+
@every_other_month_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
82
|
+
@every_ten_months_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
83
|
+
|
84
|
+
@every_other_month_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 3, 1, 9, 30, 21)
|
85
|
+
@every_ten_months_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 11, 1, 9, 30, 21)
|
86
|
+
|
87
|
+
# align forward should include base time
|
88
|
+
@every_other_month_rule.next(base, base).should == base + 2.months
|
89
|
+
@every_ten_months_rule.next(base, base).should == base + 10.months
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should find previous date" do
|
93
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
94
|
+
|
95
|
+
@every_other_month_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 11, 1, 9, 30, 21)
|
96
|
+
@every_ten_months_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 3, 1, 9, 30, 21)
|
97
|
+
|
98
|
+
@every_other_month_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
99
|
+
@every_ten_months_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
100
|
+
|
101
|
+
# align back should exclude base time
|
102
|
+
@every_other_month_rule.previous(base, base).should == Time.local(2009, 11, 1, 9, 30, 21)
|
103
|
+
@every_ten_months_rule.previous(base, base).should == Time.local(2009, 3, 1, 9, 30, 21)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should match dates" do
|
107
|
+
# same month, same datetime
|
108
|
+
@every_ten_months_rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
109
|
+
@every_ten_months_rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
110
|
+
|
111
|
+
# right month, same dattime
|
112
|
+
@every_other_month_rule.match?(Time.local(2010, 3, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
113
|
+
@every_other_month_rule.match?(Time.local(2010, 11, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
114
|
+
|
115
|
+
# wrong month, same datetime
|
116
|
+
@every_ten_months_rule.match?(Time.local(2010, 2, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
117
|
+
@every_ten_months_rule.match?(Time.local(2010, 8, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
118
|
+
|
119
|
+
# right month, different datetime
|
120
|
+
@every_other_month_rule.match?(Time.local(2010, 3, 8, 9, 20), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
121
|
+
@every_other_month_rule.match?(Time.local(2010, 11, 21, 9, 35), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "rule with filters" do
|
126
|
+
before do
|
127
|
+
@base = Time.utc(2000, 1, 1, 9, 30, 21)
|
128
|
+
@rule = Rule.monthly.months(1, 2).weekdays(1,3)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should find next date" do
|
132
|
+
@rule.next(Time.utc(2000, 1, 1), @base).should == Time.utc(2000, 1, 3, 9, 30, 21)
|
133
|
+
@rule.next(Time.utc(2000, 1, 3, 10), @base).should == Time.utc(2000, 1, 5, 9, 30, 21)
|
134
|
+
|
135
|
+
@rule.next(Time.utc(2000, 2, 2), @base).should == Time.utc(2000, 2, 2, 9, 30, 21)
|
136
|
+
@rule.next(Time.utc(2000, 2, 2, 10), @base).should == Time.utc(2000, 2, 7, 9, 30, 21)
|
137
|
+
@rule.next(Time.utc(2000, 2, 29), @base).should == Time.utc(2001, 1, 1, 9, 30, 21)
|
138
|
+
@rule.next(Time.utc(2000, 3, 1), @base).should == Time.utc(2001, 1, 1, 9, 30, 21)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should find previous date" do
|
142
|
+
@rule.previous(Time.utc(2000, 1, 1), @base).should == Time.utc(1999, 2, 24, 9, 30, 21)
|
143
|
+
@rule.previous(Time.utc(2000, 1, 3, 10), @base).should == Time.utc(2000, 1, 3, 9, 30, 21)
|
144
|
+
|
145
|
+
@rule.previous(Time.utc(2000, 2, 2), @base).should == Time.utc(2000, 1, 31, 9, 30, 21)
|
146
|
+
@rule.previous(Time.utc(2000, 2, 2, 10), @base).should == Time.utc(2000, 2, 2, 9, 30, 21)
|
147
|
+
@rule.previous(Time.utc(2000, 3, 3), @base).should == Time.utc(2000, 2, 28, 9, 30, 21)
|
148
|
+
@rule.previous(Time.utc(2000, 3, 1), @base).should == Time.utc(2000, 2, 28, 9, 30, 21)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should match dates" do
|
152
|
+
# weekdays match
|
153
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 30, 21), @base).should be_true
|
154
|
+
@rule.match?(Time.utc(2000, 1, 5, 9, 30, 21), @base).should be_true
|
155
|
+
|
156
|
+
@rule.match?(Time.utc(2000, 1, 4, 9, 30, 21), @base).should_not be_true
|
157
|
+
@rule.match?(Time.utc(2000, 1, 6, 9, 30, 21), @base).should_not be_true
|
158
|
+
|
159
|
+
# months match
|
160
|
+
@rule.match?(Time.utc(2000, 2, 23, 9, 30, 21), @base).should be_true
|
161
|
+
@rule.match?(Time.utc(2000, 2, 2, 9, 30, 21), @base).should be_true
|
162
|
+
|
163
|
+
@rule.match?(Time.utc(2000, 3, 3, 9, 30, 21), @base).should_not be_true
|
164
|
+
@rule.match?(Time.utc(2000, 3, 4, 9, 30, 21), @base).should_not be_true
|
165
|
+
|
166
|
+
# wrong time
|
167
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 30, 28), @base).should_not be_true
|
168
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 12, 29), @base).should_not be_true
|
169
|
+
@rule.match?(Time.utc(2000, 1, 3, 10, 30, 29), @base).should_not be_true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "serialization" do
|
174
|
+
it "should do a simple hash roundtrip" do
|
175
|
+
h = Rule.monthly(5).to_hash
|
176
|
+
restore = Rule.from_hash(h)
|
177
|
+
|
178
|
+
restore.class.should == MonthlyRule
|
179
|
+
restore.interval.should == 5
|
180
|
+
restore.step.should == 5.months
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should do a hash roundtrip with count" do
|
184
|
+
h = Rule.monthly.count(10).to_hash
|
185
|
+
restore = Rule.from_hash(h)
|
186
|
+
|
187
|
+
restore.count.should == 10
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should do a hash roundtrip with stop" do
|
191
|
+
h = Rule.monthly.stop(Time.local(2020, 10, 10)).to_hash
|
192
|
+
restore = Rule.from_hash(h)
|
193
|
+
|
194
|
+
restore.stop.should == Time.local(2020, 10, 10)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should do a hash roundtrip with weekdays filter" do
|
198
|
+
h = Rule.monthly.weekdays(:mon, :tuesday).to_hash
|
199
|
+
restore = Rule.from_hash(h)
|
200
|
+
|
201
|
+
restore.filters(:weekdays).weekdays.should == [1, 2]
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should do a hash roundtrip with ordered weekdays filter" do
|
205
|
+
h = Rule.monthly.weekdays(:mon => 1, :tuesday => [2, -3]).to_hash
|
206
|
+
restore = Rule.from_hash(h)
|
207
|
+
|
208
|
+
restore.filters(:weekdays).ordered_weekdays.should == {:mo => [1], :tu => [-3, 2]}
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should do a hash roundtrip with monthdays filter" do
|
212
|
+
h = Rule.monthly.monthdays(1, 3, 7, -10).to_hash
|
213
|
+
restore = Rule.from_hash(h)
|
214
|
+
|
215
|
+
restore.filters(:monthdays).monthdays.should == [-10, 1, 3, 7]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WeeklyRule do
|
4
|
+
describe "simple rule" do
|
5
|
+
before do
|
6
|
+
@rule = WeeklyRule.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return one week as step" do
|
10
|
+
@rule.step.should == 1.week
|
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, 8, 9, 30, 21)
|
18
|
+
@rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 8, 9, 30, 21)
|
19
|
+
|
20
|
+
# align forward should include base time
|
21
|
+
@rule.next(base, base).should == base + 1.week
|
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, 25, 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, 25, 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, 8, 9, 30)).should be_true
|
36
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 15, 9, 30)).should be_true
|
37
|
+
|
38
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 8, 9, 29)).should_not be_true
|
39
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 8, 10, 30)).should_not be_true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "finite rules" do
|
44
|
+
before do
|
45
|
+
@rule = WeeklyRule.new
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should accept count and allow chaining" do
|
49
|
+
@rule.count(10).should be_a(WeeklyRule)
|
50
|
+
@rule.count.should == 10
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should accept stop time" do
|
54
|
+
@rule.stop(Time.utc(2020, 1, 1)).should be_a(WeeklyRule)
|
55
|
+
@rule.stop.should == Time.utc(2020, 1, 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should report rule finiteness" do
|
59
|
+
WeeklyRule.new.count(5).should be_finite
|
60
|
+
WeeklyRule.new.stop(Time.utc(2020, 1, 1)).should be_finite
|
61
|
+
|
62
|
+
WeeklyRule.new.should_not be_finite
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "rule with interval" do
|
67
|
+
before do
|
68
|
+
@every_other_week_rule = WeeklyRule.new(2)
|
69
|
+
@every_ten_weeks_rule = WeeklyRule.new(10)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return corect step" do
|
73
|
+
@every_other_week_rule.step.should == 2.weeks
|
74
|
+
@every_ten_weeks_rule.step.should == 10.weeks
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should find next date" do
|
78
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
79
|
+
|
80
|
+
@every_other_week_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
81
|
+
@every_ten_weeks_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
82
|
+
|
83
|
+
@every_other_week_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 15, 9, 30, 21)
|
84
|
+
@every_ten_weeks_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 3, 12, 9, 30, 21)
|
85
|
+
|
86
|
+
# align forward should include base time
|
87
|
+
@every_other_week_rule.next(base, base).should == base + 2.weeks
|
88
|
+
@every_ten_weeks_rule.next(base, base).should == base + 10.weeks
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should align dates back" do
|
92
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
93
|
+
|
94
|
+
@every_other_week_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 12, 18, 9, 30, 21)
|
95
|
+
@every_ten_weeks_rule.previous(Time.local(2010, 1, 1), base).localtime.should == Time.local(2009, 10, 23, 9, 30, 21)
|
96
|
+
|
97
|
+
@every_other_week_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
98
|
+
@every_ten_weeks_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
99
|
+
|
100
|
+
# align forward should include base time
|
101
|
+
@every_other_week_rule.previous(base, base).should == base - 2.weeks
|
102
|
+
@every_ten_weeks_rule.previous(base, base).should == base - 10.weeks
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should check date alignment" do
|
106
|
+
base = Time.local(2010, 1, 1, 9, 30)
|
107
|
+
|
108
|
+
@every_other_week_rule.match?(Time.local(2010, 1, 1, 9, 30), base).should be_true
|
109
|
+
@every_other_week_rule.match?(Time.local(2010, 1, 15, 9, 30), base).should be_true
|
110
|
+
|
111
|
+
@every_ten_weeks_rule.match?(Time.local(2010, 1, 1, 9, 30), base).should be_true
|
112
|
+
@every_ten_weeks_rule.match?(Time.local(2010, 3, 12, 9, 30), base).should be_true
|
113
|
+
|
114
|
+
@every_other_week_rule.match?(Time.local(2010, 1, 2, 9, 30), base).should_not be_true
|
115
|
+
@every_ten_weeks_rule.match?(Time.local(2010, 1, 5, 9, 30), base).should_not be_true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "serialization" do
|
120
|
+
it "should do a simple hash roundtrip" do
|
121
|
+
h = Rule.weekly(5).to_hash
|
122
|
+
restore = Rule.from_hash(h)
|
123
|
+
|
124
|
+
restore.class.should == WeeklyRule
|
125
|
+
restore.interval.should == 5
|
126
|
+
restore.step.should == 5.weeks
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should do a hash roundtrip with count" do
|
130
|
+
h = Rule.weekly.count(10).to_hash
|
131
|
+
restore = Rule.from_hash(h)
|
132
|
+
|
133
|
+
restore.count.should == 10
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should do a hash roundtrip with stop" do
|
137
|
+
h = Rule.weekly.stop(Time.local(2020, 10, 10)).to_hash
|
138
|
+
restore = Rule.from_hash(h)
|
139
|
+
|
140
|
+
restore.stop.should == Time.local(2020, 10, 10)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should do a hash roundtrip with weekdays filter" do
|
144
|
+
h = Rule.weekly.weekdays(:mon, :tuesday).to_hash
|
145
|
+
restore = Rule.from_hash(h)
|
146
|
+
|
147
|
+
restore.filters(:weekdays).weekdays.should == [1, 2]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe YearlyRule do
|
5
|
+
describe "simple rule" do
|
6
|
+
before do
|
7
|
+
@rule = YearlyRule.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return one day as step" do
|
11
|
+
@rule.step.should == 1.year
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should give next date" do
|
15
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
16
|
+
|
17
|
+
@rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
18
|
+
@rule.next(Time.local(2010, 1, 1, 9, 30, 21), base).should == Time.local(2011, 1, 1, 9, 30, 21)
|
19
|
+
@rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2011, 1, 1, 9, 30, 21)
|
20
|
+
|
21
|
+
# align forward should include base time
|
22
|
+
@rule.next(base, base).should == base + 1.year
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should give previous date" do
|
26
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
27
|
+
|
28
|
+
@rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2009, 1, 1, 9, 30, 21)
|
29
|
+
@rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
30
|
+
|
31
|
+
# align back should exclude base time
|
32
|
+
@rule.previous(base, base).should == Time.local(2009, 1, 1, 9, 30, 21)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should match dates" do
|
36
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2011, 1, 1, 9, 30)).should be_true
|
37
|
+
|
38
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2011, 1, 1, 9, 29)).should_not be_true
|
39
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2011, 1, 2, 9, 30)).should_not be_true
|
40
|
+
@rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2011, 2, 1, 9, 30)).should_not be_true
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "finite rules" do
|
45
|
+
before do
|
46
|
+
@rule = YearlyRule.new
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should accept count and allow chaining" do
|
50
|
+
@rule.count(10).should be_a(YearlyRule)
|
51
|
+
@rule.count.should == 10
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should accept stop time" do
|
55
|
+
@rule.stop(Time.utc(2020, 1, 1)).should be_a(YearlyRule)
|
56
|
+
@rule.stop.should == Time.utc(2020, 1, 1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should report rule finiteness" do
|
60
|
+
YearlyRule.new.count(5).should be_finite
|
61
|
+
YearlyRule.new.stop(Time.utc(2020, 1, 1)).should be_finite
|
62
|
+
|
63
|
+
YearlyRule.new.should_not be_finite
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "rule with interval" do
|
68
|
+
before do
|
69
|
+
@every_other_year_rule = YearlyRule.new(2)
|
70
|
+
@every_ten_years_rule = YearlyRule.new(10)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return corect step" do
|
74
|
+
@every_other_year_rule.step.should == 2.years
|
75
|
+
@every_ten_years_rule.step.should == 10.years
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should find next date" do
|
79
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
80
|
+
|
81
|
+
@every_other_year_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
82
|
+
@every_ten_years_rule.next(Time.local(2010, 1, 1), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
83
|
+
|
84
|
+
@every_other_year_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2012, 1, 1, 9, 30, 21)
|
85
|
+
@every_ten_years_rule.next(Time.local(2010, 1, 1, 10), base).should == Time.local(2020, 1, 1, 9, 30, 21)
|
86
|
+
|
87
|
+
# align forward should include base time
|
88
|
+
@every_other_year_rule.next(base, base).should == base + 2.years
|
89
|
+
@every_ten_years_rule.next(base, base).should == base + 10.years
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should find previous date" do
|
93
|
+
base = Time.local(2010, 1, 1, 9, 30, 21)
|
94
|
+
|
95
|
+
@every_other_year_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2008, 1, 1, 9, 30, 21)
|
96
|
+
@every_ten_years_rule.previous(Time.local(2010, 1, 1), base).should == Time.local(2000, 1, 1, 9, 30, 21)
|
97
|
+
|
98
|
+
@every_other_year_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
99
|
+
@every_ten_years_rule.previous(Time.local(2010, 1, 1, 10), base).should == Time.local(2010, 1, 1, 9, 30, 21)
|
100
|
+
|
101
|
+
# align back should exclude base time
|
102
|
+
@every_other_year_rule.previous(base, base).should == Time.local(2008, 1, 1, 9, 30, 21)
|
103
|
+
@every_ten_years_rule.previous(base, base).should == Time.local(2000, 1, 1, 9, 30, 21)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should match dates" do
|
107
|
+
# same year, same datetime
|
108
|
+
@every_ten_years_rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
109
|
+
@every_ten_years_rule.match?(Time.local(2010, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should be_true
|
110
|
+
|
111
|
+
# right year, same dattime
|
112
|
+
@every_other_year_rule.match?(Time.local(2012, 1, 12, 9, 30), Time.local(2010, 1, 12, 9, 30)).should be_true
|
113
|
+
@every_other_year_rule.match?(Time.local(2020, 1, 12, 9, 30), Time.local(2010, 1, 12, 9, 30)).should be_true
|
114
|
+
|
115
|
+
# wrong year, same datetime
|
116
|
+
@every_ten_years_rule.match?(Time.local(2011, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
117
|
+
@every_ten_years_rule.match?(Time.local(2015, 1, 1, 9, 30), Time.local(2010, 1, 1, 9, 30)).should_not be_true
|
118
|
+
|
119
|
+
# right year, different datetime
|
120
|
+
@every_other_year_rule.match?(Time.local(2012, 3, 8, 9, 20), Time.local(2010, 1, 12, 9, 30)).should_not be_true
|
121
|
+
@every_other_year_rule.match?(Time.local(2020, 6, 21, 9, 35), Time.local(2010, 1, 12, 9, 30)).should_not be_true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "rule with filters" do
|
126
|
+
before do
|
127
|
+
@base = Time.utc(2000, 1, 1, 9, 30, 21)
|
128
|
+
@rule = Rule.yearly.months(1, 2).weekdays(1,3)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should find next date" do
|
132
|
+
@rule.next(Time.utc(2000, 1, 1), @base).should == Time.utc(2000, 1, 3, 9, 30, 21)
|
133
|
+
@rule.next(Time.utc(2000, 1, 3, 10), @base).should == Time.utc(2000, 1, 5, 9, 30, 21)
|
134
|
+
|
135
|
+
@rule.next(Time.utc(2000, 2, 2), @base).should == Time.utc(2000, 2, 2, 9, 30, 21)
|
136
|
+
@rule.next(Time.utc(2000, 2, 2, 10), @base).should == Time.utc(2000, 2, 7, 9, 30, 21)
|
137
|
+
@rule.next(Time.utc(2000, 2, 29), @base).should == Time.utc(2001, 1, 1, 9, 30, 21)
|
138
|
+
@rule.next(Time.utc(2000, 3, 1), @base).should == Time.utc(2001, 1, 1, 9, 30, 21)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should find previous date" do
|
142
|
+
@rule.previous(Time.utc(2000, 1, 1), @base).should == Time.utc(1999, 2, 24, 9, 30, 21)
|
143
|
+
@rule.previous(Time.utc(2000, 1, 3, 10), @base).should == Time.utc(2000, 1, 3, 9, 30, 21)
|
144
|
+
|
145
|
+
@rule.previous(Time.utc(2000, 2, 2), @base).should == Time.utc(2000, 1, 31, 9, 30, 21)
|
146
|
+
@rule.previous(Time.utc(2000, 2, 2, 10), @base).should == Time.utc(2000, 2, 2, 9, 30, 21)
|
147
|
+
@rule.previous(Time.utc(2000, 3, 3), @base).should == Time.utc(2000, 2, 28, 9, 30, 21)
|
148
|
+
@rule.previous(Time.utc(2000, 3, 1), @base).should == Time.utc(2000, 2, 28, 9, 30, 21)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should match dates" do
|
152
|
+
# weekdays match
|
153
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 30, 21), @base).should be_true
|
154
|
+
@rule.match?(Time.utc(2000, 1, 5, 9, 30, 21), @base).should be_true
|
155
|
+
|
156
|
+
@rule.match?(Time.utc(2000, 1, 4, 9, 30, 21), @base).should_not be_true
|
157
|
+
@rule.match?(Time.utc(2000, 1, 6, 9, 30, 21), @base).should_not be_true
|
158
|
+
|
159
|
+
# months match
|
160
|
+
@rule.match?(Time.utc(2000, 2, 23, 9, 30, 21), @base).should be_true
|
161
|
+
@rule.match?(Time.utc(2000, 2, 2, 9, 30, 21), @base).should be_true
|
162
|
+
|
163
|
+
@rule.match?(Time.utc(2000, 3, 3, 9, 30, 21), @base).should_not be_true
|
164
|
+
@rule.match?(Time.utc(2000, 3, 4, 9, 30, 21), @base).should_not be_true
|
165
|
+
|
166
|
+
# wrong time
|
167
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 30, 28), @base).should_not be_true
|
168
|
+
@rule.match?(Time.utc(2000, 1, 3, 9, 12, 29), @base).should_not be_true
|
169
|
+
@rule.match?(Time.utc(2000, 1, 3, 10, 30, 29), @base).should_not be_true
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "serialization" do
|
174
|
+
it "should do a hash roundtrip" do
|
175
|
+
h = Rule.yearly(5).to_hash
|
176
|
+
restore = Rule.from_hash(h)
|
177
|
+
|
178
|
+
restore.class.should == YearlyRule
|
179
|
+
restore.interval.should == 5
|
180
|
+
restore.step.should == 5.years
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should do a hash roundtrip with count" do
|
184
|
+
h = Rule.yearly.count(10).to_hash
|
185
|
+
restore = Rule.from_hash(h)
|
186
|
+
|
187
|
+
restore.count.should == 10
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should do a hash roundtrip with stop" do
|
191
|
+
h = Rule.yearly.stop(Time.local(2020, 10, 10)).to_hash
|
192
|
+
restore = Rule.from_hash(h)
|
193
|
+
|
194
|
+
restore.stop.should == Time.local(2020, 10, 10)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should do a hash roundtrip with weekdays filter" do
|
198
|
+
h = Rule.yearly.weekdays(:mon, :tuesday).to_hash
|
199
|
+
restore = Rule.from_hash(h)
|
200
|
+
|
201
|
+
restore.filters(:weekdays).weekdays.should == [1, 2]
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should do a hash roundtrip with ordered weekdays filter" do
|
205
|
+
h = Rule.yearly.weekdays(:mon => 1, :tuesday => [2, -3]).to_hash
|
206
|
+
restore = Rule.from_hash(h)
|
207
|
+
|
208
|
+
restore.filters(:weekdays).ordered_weekdays.should == {:mo => [1], :tu => [-3, 2]}
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should do a hash roundtrip with monthdays filter" do
|
212
|
+
h = Rule.yearly.monthdays(1, 3, 7, -10).to_hash
|
213
|
+
restore = Rule.from_hash(h)
|
214
|
+
|
215
|
+
restore.filters(:monthdays).monthdays.should == [-10, 1, 3, 7]
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should do a hash roundtrip with months filter" do
|
219
|
+
h = Rule.yearly.months(1, 3, 12, -5).to_hash
|
220
|
+
restore = Rule.from_hash(h)
|
221
|
+
|
222
|
+
restore.filters(:months).months.should == [-5, 1, 3, 12]
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should do a hash roundtrip with yeardays filter" do
|
226
|
+
h = Rule.yearly.yeardays(1, 30, 120, -50).to_hash
|
227
|
+
restore = Rule.from_hash(h)
|
228
|
+
|
229
|
+
restore.filters(:yeardays).yeardays.should == [-50, 1, 30, 120]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|