symphony 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +40 -2
- data/History.rdoc +7 -0
- data/Manifest.txt +2 -1
- data/Rakefile +26 -2
- data/TODO.md +0 -2
- data/lib/symphony.rb +2 -2
- data/lib/symphony/intervalexpression.rb +1216 -0
- data/lib/symphony/mixins.rb +147 -1
- data/lib/symphony/task.rb +1 -18
- data/spec/symphony/intervalexpression_spec.rb +481 -0
- data/spec/symphony/mixins_spec.rb +51 -0
- metadata +37 -8
- metadata.gz.sig +0 -0
- data/lib/symphony/tasks/pinger.rb +0 -64
data/lib/symphony/mixins.rb
CHANGED
@@ -71,5 +71,151 @@ module Symphony
|
|
71
71
|
end # module MethodUtilities
|
72
72
|
|
73
73
|
|
74
|
+
# Functions for time calculations
|
75
|
+
module TimeFunctions
|
76
|
+
|
77
|
+
###############
|
78
|
+
module_function
|
79
|
+
###############
|
80
|
+
|
81
|
+
### Calculate the (approximate) number of seconds that are in +count+ of the
|
82
|
+
### given +unit+ of time.
|
83
|
+
def calculate_seconds( count, unit )
|
84
|
+
return case unit
|
85
|
+
when :seconds, :second
|
86
|
+
count
|
87
|
+
when :minutes, :minute
|
88
|
+
count * 60
|
89
|
+
when :hours, :hour
|
90
|
+
count * 3600
|
91
|
+
when :days, :day
|
92
|
+
count * 86400
|
93
|
+
when :weeks, :week
|
94
|
+
count * 604800
|
95
|
+
when :fortnights, :fortnight
|
96
|
+
count * 1209600
|
97
|
+
when :months, :month
|
98
|
+
count * 2592000
|
99
|
+
when :years, :year
|
100
|
+
count * 31557600
|
101
|
+
else
|
102
|
+
raise ArgumentError, "don't know how to calculate seconds in a %p" % [ unit ]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end # module TimeFunctions
|
107
|
+
|
108
|
+
|
109
|
+
# Refinements to Numeric to add time-related convenience methods
|
110
|
+
module TimeRefinements
|
111
|
+
refine Numeric do
|
112
|
+
|
113
|
+
### Number of seconds (returns receiver unmodified)
|
114
|
+
def seconds
|
115
|
+
return self
|
116
|
+
end
|
117
|
+
alias_method :second, :seconds
|
118
|
+
|
119
|
+
### Returns number of seconds in <receiver> minutes
|
120
|
+
def minutes
|
121
|
+
return TimeFunctions.calculate_seconds( self, :minutes )
|
122
|
+
end
|
123
|
+
alias_method :minute, :minutes
|
124
|
+
|
125
|
+
### Returns the number of seconds in <receiver> hours
|
126
|
+
def hours
|
127
|
+
return TimeFunctions.calculate_seconds( self, :hours )
|
128
|
+
end
|
129
|
+
alias_method :hour, :hours
|
130
|
+
|
131
|
+
### Returns the number of seconds in <receiver> days
|
132
|
+
def days
|
133
|
+
return TimeFunctions.calculate_seconds( self, :day )
|
134
|
+
end
|
135
|
+
alias_method :day, :days
|
136
|
+
|
137
|
+
### Return the number of seconds in <receiver> weeks
|
138
|
+
def weeks
|
139
|
+
return TimeFunctions.calculate_seconds( self, :weeks )
|
140
|
+
end
|
141
|
+
alias_method :week, :weeks
|
142
|
+
|
143
|
+
### Returns the number of seconds in <receiver> fortnights
|
144
|
+
def fortnights
|
145
|
+
return TimeFunctions.calculate_seconds( self, :fortnights )
|
146
|
+
end
|
147
|
+
alias_method :fortnight, :fortnights
|
148
|
+
|
149
|
+
### Returns the number of seconds in <receiver> months (approximate)
|
150
|
+
def months
|
151
|
+
return TimeFunctions.calculate_seconds( self, :months )
|
152
|
+
end
|
153
|
+
alias_method :month, :months
|
154
|
+
|
155
|
+
### Returns the number of seconds in <receiver> years (approximate)
|
156
|
+
def years
|
157
|
+
return TimeFunctions.calculate_seconds( self, :years )
|
158
|
+
end
|
159
|
+
alias_method :year, :years
|
160
|
+
|
161
|
+
|
162
|
+
### Returns the Time <receiver> number of seconds before the
|
163
|
+
### specified +time+. E.g., 2.hours.before( header.expiration )
|
164
|
+
def before( time )
|
165
|
+
return time - self
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
### Returns the Time <receiver> number of seconds ago. (e.g.,
|
170
|
+
### expiration > 2.hours.ago )
|
171
|
+
def ago
|
172
|
+
return self.before( ::Time.now )
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
### Returns the Time <receiver> number of seconds after the given +time+.
|
177
|
+
### E.g., 10.minutes.after( header.expiration )
|
178
|
+
def after( time )
|
179
|
+
return time + self
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
### Return a new Time <receiver> number of seconds from now.
|
184
|
+
def from_now
|
185
|
+
return self.after( ::Time.now )
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
### Return a string describing approximately the amount of time in
|
190
|
+
### <receiver> number of seconds.
|
191
|
+
def timeperiod
|
192
|
+
return case
|
193
|
+
when self < 1.minute
|
194
|
+
'less than a minute'
|
195
|
+
when self < 50.minutes
|
196
|
+
'%d minutes' % [ (self.to_f / 1.minute).ceil ]
|
197
|
+
when self < 90.minutes
|
198
|
+
'about an hour'
|
199
|
+
when self < 18.hours
|
200
|
+
"%d hours" % [ (self.to_f / 1.hour).ceil ]
|
201
|
+
when self < 30.hours
|
202
|
+
'about a day'
|
203
|
+
when self < 1.week
|
204
|
+
"%d days" % [ (self.to_f / 1.day).ceil ]
|
205
|
+
when self < 2.weeks
|
206
|
+
'about one week'
|
207
|
+
when self < 3.months
|
208
|
+
"%d weeks" % [ (self.to_f / 1.week).ceil ]
|
209
|
+
when self < 18.months
|
210
|
+
"%d months" % [ (self.to_f / 1.month).ceil ]
|
211
|
+
else
|
212
|
+
"%d years" % [ (self.to_f / 1.year).ceil ]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end # refine Numeric
|
217
|
+
end # module TimeRefinements
|
218
|
+
|
219
|
+
end # module Symphony
|
220
|
+
|
74
221
|
|
75
|
-
end # module Symphony
|
data/lib/symphony/task.rb
CHANGED
@@ -25,7 +25,7 @@ class Symphony::Task
|
|
25
25
|
|
26
26
|
|
27
27
|
# Signal to reset to defaults for the child
|
28
|
-
SIGNALS = %i[ INT TERM HUP
|
28
|
+
SIGNALS = %i[ INT TERM HUP ]
|
29
29
|
|
30
30
|
# Valid work model types
|
31
31
|
WORK_MODELS = %i[ longlived oneshot ]
|
@@ -319,10 +319,6 @@ class Symphony::Task
|
|
319
319
|
self.on_interrupt
|
320
320
|
when :HUP
|
321
321
|
self.on_hangup
|
322
|
-
when :CHLD
|
323
|
-
self.on_child_exit
|
324
|
-
when :WINCH
|
325
|
-
self.on_window_size_change
|
326
322
|
else
|
327
323
|
self.log.warn "Unhandled signal %s" % [ sig ]
|
328
324
|
end
|
@@ -379,19 +375,6 @@ class Symphony::Task
|
|
379
375
|
end
|
380
376
|
|
381
377
|
|
382
|
-
### Handle a child process exiting.
|
383
|
-
def on_child_exit
|
384
|
-
self.log.info "Child exited."
|
385
|
-
Process.waitpid( 0, Process::WNOHANG )
|
386
|
-
end
|
387
|
-
|
388
|
-
|
389
|
-
### Handle a window size change event. No-op by default.
|
390
|
-
def on_window_size_change
|
391
|
-
self.log.info "Window size changed."
|
392
|
-
end
|
393
|
-
|
394
|
-
|
395
378
|
### Handle a hangup signal by re-reading the config and restarting.
|
396
379
|
def on_hangup
|
397
380
|
self.log.info "Hangup signal."
|
@@ -0,0 +1,481 @@
|
|
1
|
+
# vim: set nosta noet ts=4 sw=4 ft=rspec:
|
2
|
+
|
3
|
+
require_relative '../helpers'
|
4
|
+
require 'symphony/intervalexpression'
|
5
|
+
require 'symphony/mixins'
|
6
|
+
|
7
|
+
using Symphony::TimeRefinements
|
8
|
+
|
9
|
+
|
10
|
+
#####################################################################
|
11
|
+
### C O N T E X T S
|
12
|
+
#####################################################################
|
13
|
+
|
14
|
+
describe Symphony::IntervalExpression do
|
15
|
+
|
16
|
+
let( :past ) { Time.at(1262376000) }
|
17
|
+
|
18
|
+
it "can't be instantiated directly" do
|
19
|
+
expect { described_class.new }.to raise_error( NoMethodError )
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises an exception if unable to parse the expression" do
|
23
|
+
expect {
|
24
|
+
described_class.parse( 'wut!' )
|
25
|
+
}.to raise_error( Symphony::TimeParseError, /unable to parse/ )
|
26
|
+
end
|
27
|
+
|
28
|
+
it "normalizes the expression before attempting to parse it" do
|
29
|
+
allow( Time ).to receive( :now ).and_return( past )
|
30
|
+
parsed = described_class.parse( '\'";At 2014---01-01 14::00(' )
|
31
|
+
expect( parsed.to_s ).to eq( 'at 2014-01-01 14:00' )
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can parse the expression, offset from a different time" do
|
35
|
+
parsed = described_class.parse( 'every 5 seconds ending in an hour', past )
|
36
|
+
expect( parsed.starting ).to eq( past )
|
37
|
+
expect( parsed.ending ).to eq( past + 3600 )
|
38
|
+
end
|
39
|
+
|
40
|
+
it "is comparable" do
|
41
|
+
p1 = described_class.parse( 'at 2pm', past )
|
42
|
+
p2 = described_class.parse( 'at 3pm', past )
|
43
|
+
p3 = described_class.parse( 'at 2:00pm', past )
|
44
|
+
|
45
|
+
expect( p1 ).to be < p2
|
46
|
+
expect( p2 ).to be > p1
|
47
|
+
expect( p1 ).to eq( p3 )
|
48
|
+
end
|
49
|
+
|
50
|
+
it "won't allow scheduling dates in the past" do
|
51
|
+
expect {
|
52
|
+
described_class.parse( 'on 1999-01-01' )
|
53
|
+
}.to raise_error( Symphony::TimeParseError, /schedule in the past/ )
|
54
|
+
end
|
55
|
+
|
56
|
+
it "doesn't allow intervals of 0" do
|
57
|
+
expect {
|
58
|
+
described_class.parse( 'every 0 seconds' )
|
59
|
+
}.to raise_error( Symphony::TimeParseError, /unable to parse/ )
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
context 'exact times and dates' do
|
64
|
+
|
65
|
+
# stub for Time, tests are from this 'stuck' point:
|
66
|
+
# 2010-01-01 12:00
|
67
|
+
#
|
68
|
+
before( :each ) do
|
69
|
+
allow( Time ).to receive( :now ).and_return( past )
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'at 2pm' do |example|
|
73
|
+
parsed = described_class.parse( example.description )
|
74
|
+
expect( parsed.valid ).to be
|
75
|
+
expect( parsed.recurring ).to be_falsey
|
76
|
+
expect( parsed.interval ).to be( 7200.0 )
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'at 2:30pm' do |example|
|
80
|
+
parsed = described_class.parse( example.description )
|
81
|
+
expect( parsed.valid ).to be_truthy
|
82
|
+
expect( parsed.interval ).to be( 9000.0 )
|
83
|
+
end
|
84
|
+
|
85
|
+
it "pushes ambiguous times in today's past into tomorrow (at 11am)" do
|
86
|
+
parsed = described_class.parse( 'at 11am' )
|
87
|
+
expect( parsed.valid ).to be_truthy
|
88
|
+
expect( parsed.interval ).to be( 82800.0 )
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'on 2010-01-02' do |example|
|
92
|
+
parsed = described_class.parse( example.description )
|
93
|
+
expect( parsed.valid ).to be_truthy
|
94
|
+
expect( parsed.interval ).to be( 43200.0 )
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'on 2010-01-02 12:00' do |example|
|
98
|
+
parsed = described_class.parse( example.description )
|
99
|
+
expect( parsed.valid ).to be_truthy
|
100
|
+
expect( parsed.interval ).to be( 86400.0 )
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'on 2010-01-02 12:00:01' do |example|
|
104
|
+
parsed = described_class.parse( example.description )
|
105
|
+
expect( parsed.valid ).to be_truthy
|
106
|
+
expect( parsed.interval ).to be( 86401.0 )
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'correctly timeboxes the expression' do
|
110
|
+
parsed = described_class.parse( 'at 2pm' )
|
111
|
+
expect( parsed.valid ).to be_truthy
|
112
|
+
expect( parsed.interval ).to be( 7200.0 )
|
113
|
+
expect( parsed.ending ).to be_nil
|
114
|
+
expect( parsed.recurring ).to be_falsey
|
115
|
+
expect( parsed.starting ).to eq( past )
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'always sets a start time if one is not specified' do
|
119
|
+
parsed = described_class.parse( 'at 2pm' )
|
120
|
+
expect( parsed.valid ).to be_truthy
|
121
|
+
expect( parsed.recurring ).to be_falsey
|
122
|
+
expect( parsed.starting ).to eq( past )
|
123
|
+
expect( parsed.interval ).to be( 7200.0 )
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'one-shot intervals' do
|
128
|
+
|
129
|
+
# stub for Time, tests are from this 'stuck' point:
|
130
|
+
# 2010-01-01 12:00
|
131
|
+
#
|
132
|
+
before( :each ) do
|
133
|
+
allow( Time ).to receive( :now ).and_return( past )
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'in 30 seconds' do |example|
|
137
|
+
parsed = described_class.parse( example.description )
|
138
|
+
expect( parsed.valid ).to be_truthy
|
139
|
+
expect( parsed.recurring ).to be_falsey
|
140
|
+
expect( parsed.interval ).to be( 30.0 )
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'in 30 seconds from now' do |example|
|
144
|
+
parsed = described_class.parse( example.description )
|
145
|
+
expect( parsed.valid ).to be_truthy
|
146
|
+
expect( parsed.interval ).to be( 30.0 )
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'in an hour from now' do |example|
|
150
|
+
parsed = described_class.parse( example.description )
|
151
|
+
expect( parsed.valid ).to be_truthy
|
152
|
+
expect( parsed.interval ).to be( 3600.0 )
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'in a minute' do |example|
|
156
|
+
parsed = described_class.parse( example.description )
|
157
|
+
expect( parsed.valid ).to be_truthy
|
158
|
+
expect( parsed.interval ).to be( 60.0 )
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'correctly timeboxes the expression' do
|
162
|
+
parsed = described_class.parse( 'in 30 seconds' )
|
163
|
+
expect( parsed.valid ).to be_truthy
|
164
|
+
expect( parsed.interval ).to be( 30.0 )
|
165
|
+
expect( parsed.ending ).to be_nil
|
166
|
+
expect( parsed.recurring ).to be_falsey
|
167
|
+
expect( parsed.starting ).to eq( past )
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'always sets a start time if one is not specified' do
|
171
|
+
parsed = described_class.parse( 'in 5 seconds' )
|
172
|
+
expect( parsed.valid ).to be_truthy
|
173
|
+
expect( parsed.recurring ).to be_falsey
|
174
|
+
expect( parsed.starting ).to eq( past )
|
175
|
+
expect( parsed.interval ).to be( 5.0 )
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'ignores end specifications for non-recurring run times' do
|
179
|
+
parsed = described_class.parse( 'run at 2010-01-02 end at 2010-03-01' )
|
180
|
+
expect( parsed.valid ).to be_truthy
|
181
|
+
expect( parsed.recurring ).to be_falsey
|
182
|
+
expect( parsed.starting ).to eq( past )
|
183
|
+
expect( parsed.ending ).to be_falsey
|
184
|
+
expect( parsed.interval ).to be( 43200.0 )
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'repeating intervals' do
|
189
|
+
|
190
|
+
# stub for Time, tests are from this 'stuck' point:
|
191
|
+
# 2010-01-01 12:00
|
192
|
+
#
|
193
|
+
before( :each ) do
|
194
|
+
allow( Time ).to receive( :now ).and_return( past )
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'every 500 milliseconds' do |example|
|
198
|
+
parsed = described_class.parse( example.description )
|
199
|
+
expect( parsed.valid ).to be_truthy
|
200
|
+
expect( parsed.recurring ).to be_truthy
|
201
|
+
expect( parsed.interval ).to be( 0.5 )
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'every 30 seconds' do |example|
|
205
|
+
parsed = described_class.parse( example.description )
|
206
|
+
expect( parsed.valid ).to be_truthy
|
207
|
+
expect( parsed.recurring ).to be_truthy
|
208
|
+
expect( parsed.interval ).to be( 30.0 )
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'once an hour' do |example|
|
212
|
+
parsed = described_class.parse( example.description )
|
213
|
+
expect( parsed.valid ).to be_truthy
|
214
|
+
expect( parsed.recurring ).to be_truthy
|
215
|
+
expect( parsed.interval ).to be( 3600.0 )
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'once a minute' do |example|
|
219
|
+
parsed = described_class.parse( example.description )
|
220
|
+
expect( parsed.valid ).to be_truthy
|
221
|
+
expect( parsed.recurring ).to be_truthy
|
222
|
+
expect( parsed.interval ).to be( 60.0 )
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'once per week' do |example|
|
226
|
+
parsed = described_class.parse( example.description )
|
227
|
+
expect( parsed.valid ).to be_truthy
|
228
|
+
expect( parsed.recurring ).to be_truthy
|
229
|
+
expect( parsed.interval ).to be( 604800.0 )
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'every day' do |example|
|
233
|
+
parsed = described_class.parse( example.description )
|
234
|
+
expect( parsed.valid ).to be_truthy
|
235
|
+
expect( parsed.recurring ).to be_truthy
|
236
|
+
expect( parsed.interval ).to be( 86400.0 )
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'every other day' do |example|
|
240
|
+
parsed = described_class.parse( example.description )
|
241
|
+
expect( parsed.valid ).to be_truthy
|
242
|
+
expect( parsed.recurring ).to be_truthy
|
243
|
+
expect( parsed.interval ).to be( 172800.0 )
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'always sets a start time if one is not specified' do
|
247
|
+
parsed = described_class.parse( 'every 5 seconds' )
|
248
|
+
expect( parsed.valid ).to be_truthy
|
249
|
+
expect( parsed.recurring ).to be_truthy
|
250
|
+
expect( parsed.starting ).to eq( past )
|
251
|
+
expect( parsed.interval ).to be( 5.0 )
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'repeating intervals with only an expiration date' do
|
256
|
+
|
257
|
+
# stub for Time, tests are from this 'stuck' point:
|
258
|
+
# 2010-01-01 12:00
|
259
|
+
#
|
260
|
+
before( :each ) do
|
261
|
+
allow( Time ).to receive( :now ).and_return( past )
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'every day ending in 1 week' do |example|
|
265
|
+
parsed = described_class.parse( example.description )
|
266
|
+
expect( parsed.valid ).to be_truthy
|
267
|
+
expect( parsed.recurring ).to be_truthy
|
268
|
+
expect( parsed.interval ).to be( 86400.0 )
|
269
|
+
expect( parsed.ending ).to eq( past + 604800 )
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'once a day finishing in a week from now' do |example|
|
273
|
+
parsed = described_class.parse( example.description )
|
274
|
+
expect( parsed.valid ).to be_truthy
|
275
|
+
expect( parsed.recurring ).to be_truthy
|
276
|
+
expect( parsed.interval ).to be( 86400.0 )
|
277
|
+
expect( parsed.ending ).to eq( past + 604800 )
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'once a day until 2010-02-01' do |example|
|
281
|
+
parsed = described_class.parse( example.description )
|
282
|
+
expect( parsed.valid ).to be_truthy
|
283
|
+
expect( parsed.recurring ).to be_truthy
|
284
|
+
expect( parsed.interval ).to be( 86400.0 )
|
285
|
+
expect( parsed.ending ).to eq( past + 2635200 )
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'once a day end on 2010-02-01 00:00:10' do |example|
|
289
|
+
parsed = described_class.parse( example.description )
|
290
|
+
expect( parsed.valid ).to be_truthy
|
291
|
+
expect( parsed.recurring ).to be_truthy
|
292
|
+
expect( parsed.interval ).to be( 86400.0 )
|
293
|
+
expect( parsed.ending ).to eq( past + 2635210 )
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'always sets a start time if one is not specified' do
|
297
|
+
parsed = described_class.parse( 'every 5 seconds ending in 1 week' )
|
298
|
+
expect( parsed.valid ).to be_truthy
|
299
|
+
expect( parsed.recurring ).to be_truthy
|
300
|
+
expect( parsed.starting ).to eq( past )
|
301
|
+
expect( parsed.interval ).to be( 5.0 )
|
302
|
+
expect( parsed.ending ).to eq( past + 604800 )
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context 'repeating intervals with only a start time' do
|
307
|
+
|
308
|
+
# stub for Time, tests are from this 'stuck' point:
|
309
|
+
# 2010-01-01 12:00
|
310
|
+
#
|
311
|
+
before( :each ) do
|
312
|
+
allow( Time ).to receive( :now ).and_return( past )
|
313
|
+
end
|
314
|
+
|
315
|
+
it "won't allow explicit start times with non-recurring run times" do
|
316
|
+
expect {
|
317
|
+
described_class.parse( 'start at 2010-02-01 run at 2010-02-01' )
|
318
|
+
}.to raise_error( Symphony::TimeParseError, /use 'at \[datetime\]' instead/ )
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'starting in 5 minutes, run once a second' do |example|
|
322
|
+
parsed = described_class.parse( example.description )
|
323
|
+
expect( parsed.valid ).to be_truthy
|
324
|
+
expect( parsed.recurring ).to be_truthy
|
325
|
+
expect( parsed.starting ).to eq( past + 300 )
|
326
|
+
expect( parsed.interval ).to be( 1.0 )
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'starting in a day execute every 3 minutes' do |example|
|
330
|
+
parsed = described_class.parse( example.description )
|
331
|
+
expect( parsed.valid ).to be_truthy
|
332
|
+
expect( parsed.recurring ).to be_truthy
|
333
|
+
expect( parsed.starting ).to eq( past + 86400 )
|
334
|
+
expect( parsed.interval ).to be( 180.0 )
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'start at 2010-01-02 execute every 1 minute' do |example|
|
338
|
+
parsed = described_class.parse( example.description )
|
339
|
+
expect( parsed.valid ).to be_truthy
|
340
|
+
expect( parsed.recurring ).to be_truthy
|
341
|
+
expect( parsed.starting ).to eq( past + 43200 )
|
342
|
+
expect( parsed.interval ).to be( 60.0 )
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'always sets a start time if one is not specified' do
|
346
|
+
parsed = described_class.parse( 'every 5 seconds' )
|
347
|
+
expect( parsed.valid ).to be_truthy
|
348
|
+
expect( parsed.recurring ).to be_truthy
|
349
|
+
expect( parsed.starting ).to eq( past )
|
350
|
+
expect( parsed.interval ).to be( 5.0 )
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context 'intervals with start and end times' do
|
355
|
+
|
356
|
+
# stub for Time, tests are from this 'stuck' point:
|
357
|
+
# 2010-01-01 12:00
|
358
|
+
#
|
359
|
+
before( :each ) do
|
360
|
+
allow( Time ).to receive( :now ).and_return( past )
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'beginning in 1 hour from now run every 5 seconds ending on 2010-01-02' do |example|
|
364
|
+
parsed = described_class.parse( example.description )
|
365
|
+
expect( parsed.valid ).to be_truthy
|
366
|
+
expect( parsed.recurring ).to be_truthy
|
367
|
+
expect( parsed.starting ).to eq( past + 3600 )
|
368
|
+
expect( parsed.interval ).to be( 5.0 )
|
369
|
+
expect( parsed.ending ).to eq( past + 43200 )
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'starting in 1 hour, run every 5 seconds and finish at 3pm' do |example|
|
373
|
+
parsed = described_class.parse( example.description )
|
374
|
+
expect( parsed.valid ).to be_truthy
|
375
|
+
expect( parsed.recurring ).to be_truthy
|
376
|
+
expect( parsed.starting ).to eq( past + 3600 )
|
377
|
+
expect( parsed.interval ).to be( 5.0 )
|
378
|
+
expect( parsed.ending ).to eq( past + 3600 * 3 )
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'begin in an hour run every 5 seconds and then stop at 3pm' do |example|
|
382
|
+
parsed = described_class.parse( example.description )
|
383
|
+
expect( parsed.valid ).to be_truthy
|
384
|
+
expect( parsed.recurring ).to be_truthy
|
385
|
+
expect( parsed.starting ).to eq( past + 3600 )
|
386
|
+
expect( parsed.interval ).to be( 5.0 )
|
387
|
+
expect( parsed.ending ).to eq( past + 3600 * 3 )
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'start at 2010-01-02 10:00 and then run each minute for the next 6 days' do |example|
|
391
|
+
parsed = described_class.parse( example.description )
|
392
|
+
expect( parsed.valid ).to be_truthy
|
393
|
+
expect( parsed.recurring ).to be_truthy
|
394
|
+
expect( parsed.starting ).to eq( past + 43200 + 36000 )
|
395
|
+
expect( parsed.interval ).to be( 60.0 )
|
396
|
+
expect( parsed.ending ).to eq( Time.parse('2010-01-02 10:00') + 86400 * 6 )
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context 'intervals with a count' do
|
401
|
+
|
402
|
+
# stub for Time, tests are from this 'stuck' point:
|
403
|
+
# 2010-01-01 12:00
|
404
|
+
#
|
405
|
+
before( :each ) do
|
406
|
+
allow( Time ).to receive( :now ).and_return( past )
|
407
|
+
end
|
408
|
+
|
409
|
+
it "won't allow count multipliers without an interval nor an end date" do
|
410
|
+
expect {
|
411
|
+
described_class.parse( 'run 10 times' )
|
412
|
+
}.to raise_error( Symphony::TimeParseError, /end date or interval is required/ )
|
413
|
+
end
|
414
|
+
|
415
|
+
it '10 times a minute for 2 days' do |example|
|
416
|
+
parsed = described_class.parse( example.description )
|
417
|
+
expect( parsed.multiplier ).to be( 10 )
|
418
|
+
expect( parsed.recurring ).to be_truthy
|
419
|
+
expect( parsed.starting ).to eq( past )
|
420
|
+
expect( parsed.interval ).to be( 6.0 )
|
421
|
+
expect( parsed.ending ).to eq( past + 86400 * 2 )
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'run 45 times every hour' do |example|
|
425
|
+
parsed = described_class.parse( example.description )
|
426
|
+
expect( parsed.multiplier ).to be( 45 )
|
427
|
+
expect( parsed.recurring ).to be_truthy
|
428
|
+
expect( parsed.starting ).to eq( past )
|
429
|
+
expect( parsed.interval ).to be( 80.0 )
|
430
|
+
expect( parsed.ending ).to be_nil
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'start at 2010-01-02 run 12 times and end on 2010-01-03' do |example|
|
434
|
+
parsed = described_class.parse( example.description )
|
435
|
+
expect( parsed.multiplier ).to be( 12 )
|
436
|
+
expect( parsed.recurring ).to be_truthy
|
437
|
+
expect( parsed.starting ).to eq( past + 43200 )
|
438
|
+
expect( parsed.interval ).to be( 7200.0 )
|
439
|
+
expect( parsed.ending ).to eq( past + 86400 + 43200 )
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'starting in an hour from now run 6 times a minute for 2 hours' do |example|
|
443
|
+
parsed = described_class.parse( example.description )
|
444
|
+
expect( parsed.multiplier ).to be( 6 )
|
445
|
+
expect( parsed.recurring ).to be_truthy
|
446
|
+
expect( parsed.starting ).to eq( past + 3600 )
|
447
|
+
expect( parsed.interval ).to be( 10.0 )
|
448
|
+
expect( parsed.ending ).to eq( past + 3600 * 3 )
|
449
|
+
end
|
450
|
+
|
451
|
+
it 'beginning a day from now, run 30 times per minute and finish in 2 weeks' do |example|
|
452
|
+
parsed = described_class.parse( example.description )
|
453
|
+
expect( parsed.multiplier ).to be( 30 )
|
454
|
+
expect( parsed.recurring ).to be_truthy
|
455
|
+
expect( parsed.starting ).to eq( past + 86400 )
|
456
|
+
expect( parsed.interval ).to be( 2.0 )
|
457
|
+
expect( parsed.ending ).to eq( past + 1209600 + 86400 )
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
context "when checking if it's okay to run" do
|
462
|
+
|
463
|
+
it 'returns true if the interval is within bounds' do
|
464
|
+
parsed = described_class.parse( 'at 2pm' )
|
465
|
+
expect( parsed.fire? ).to be_truthy
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'returns nil if the ending (expiration) date has passed' do
|
469
|
+
allow( Time ).to receive( :now ).and_return( past )
|
470
|
+
parsed = described_class.parse( 'every minute' )
|
471
|
+
parsed.instance_variable_set( :@ending, past - 30 )
|
472
|
+
expect( parsed.fire? ).to be_nil
|
473
|
+
end
|
474
|
+
|
475
|
+
it 'returns false if the starting window has yet to occur' do
|
476
|
+
parsed = described_class.parse( 'starting in 2 hours run each minute' )
|
477
|
+
expect( parsed.fire? ).to be_falsey
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|