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