tickwork 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,576 @@
1
+ require File.expand_path('../../lib/tickwork', __FILE__)
2
+ require File.expand_path('../data_stores/fake_store.rb', __FILE__)
3
+ require File.expand_path('../null_logger.rb', __FILE__)
4
+ require "minitest/autorun"
5
+ require 'mocha/mini_test'
6
+ require 'time'
7
+ require 'active_support/time'
8
+
9
+ describe Tickwork::Manager do
10
+ def self.test_order
11
+ :alpha
12
+ end
13
+ before do
14
+ @manager = Tickwork::Manager.new
15
+ @manager.configure do |config|
16
+ config[:data_store] = Tickwork::FakeStore.new
17
+ config[:logger] = NullLogger.new
18
+ end
19
+ class << @manager
20
+ def log(msg); end
21
+ end
22
+ @manager.handler { }
23
+ end
24
+
25
+ def assert_will_run(t)
26
+ if t.is_a? String
27
+ t = Time.parse(t)
28
+ end
29
+ assert_equal 1, @manager.tick(t).size
30
+ end
31
+
32
+ def assert_wont_run(t)
33
+ if t.is_a? String
34
+ t = Time.parse(t)
35
+ end
36
+ assert_equal 0, @manager.tick(t).size
37
+ end
38
+
39
+ it "once a minute" do
40
+ @manager.every(1.minute, 'myjob')
41
+
42
+ assert_will_run(t=Time.now)
43
+ assert_wont_run(t+30)
44
+ assert_will_run(t+60)
45
+ end
46
+
47
+ it "every three minutes" do
48
+ @manager.every(3.minutes, 'myjob')
49
+
50
+ assert_will_run(t=Time.now)
51
+ assert_wont_run(t+2*60)
52
+ assert_will_run(t+3*60)
53
+ end
54
+
55
+ it "once an hour" do
56
+ @manager.every(1.hour, 'myjob')
57
+
58
+ assert_will_run(t=Time.now)
59
+ assert_wont_run(t+30*60)
60
+ assert_will_run(t+60*60)
61
+ end
62
+
63
+ it "once a week" do
64
+ @manager.every(1.week, 'myjob')
65
+
66
+ assert_will_run(t=Time.now)
67
+ assert_wont_run(t+60*60*24*6)
68
+ assert_will_run(t+60*60*24*7)
69
+ end
70
+
71
+ it "won't drift later and later" do
72
+ @manager.every(1.hour, 'myjob')
73
+
74
+ assert_will_run(Time.parse("10:00:00.5"))
75
+ assert_wont_run(Time.parse("10:59:59.999"))
76
+ assert_will_run(Time.parse("11:00:00.0"))
77
+ end
78
+
79
+ it "aborts when no handler defined" do
80
+ manager = Tickwork::Manager.new
81
+ assert_raises(Tickwork::Manager::NoHandlerDefined) do
82
+ manager.every(1.minute, 'myjob')
83
+ end
84
+ end
85
+
86
+ it "aborts when fails to parse" do
87
+ assert_raises(Tickwork::At::FailedToParse) do
88
+ @manager.every(1.day, "myjob", :at => "a:bc")
89
+ end
90
+ end
91
+
92
+ it "general handler" do
93
+ $set_me = 0
94
+ @manager.handler { $set_me = 1 }
95
+ @manager.every(1.minute, 'myjob')
96
+ @manager.tick(Time.now)
97
+ assert_equal 1, $set_me
98
+ end
99
+
100
+ it "event-specific handler" do
101
+ $set_me = 0
102
+ @manager.every(1.minute, 'myjob') { $set_me = 2 }
103
+ @manager.tick(Time.now)
104
+
105
+ assert_equal 2, $set_me
106
+ end
107
+
108
+ it "should pass time to the general handler" do
109
+ received = nil
110
+ now = Time.now
111
+ @manager.handler { |job, time| received = time }
112
+ @manager.every(1.minute, 'myjob')
113
+ @manager.tick(now)
114
+ assert_equal now, received
115
+ end
116
+
117
+ it "should pass time to the event-specific handler" do
118
+ received = nil
119
+ now = Time.now
120
+ @manager.every(1.minute, 'myjob') { |job, time| received = time }
121
+ @manager.tick(now)
122
+ assert_equal now, received
123
+ end
124
+
125
+ it "exceptions are trapped and logged" do
126
+ @manager.handler { raise 'boom' }
127
+ @manager.every(1.minute, 'myjob')
128
+
129
+ mocked_logger = MiniTest::Mock.new
130
+ mocked_logger.expect :error, true, [RuntimeError]
131
+ @manager.configure { |c| c[:logger] = mocked_logger }
132
+ @manager.tick(Time.now)
133
+ mocked_logger.verify
134
+ end
135
+
136
+ it "exceptions still set the last timestamp to avoid spastic error loops" do
137
+ @manager.handler { raise 'boom' }
138
+ event = @manager.every(1.minute, 'myjob')
139
+ @manager.stubs(:log_error)
140
+ @manager.tick(t = Time.now)
141
+ assert_equal t, event.last
142
+ end
143
+
144
+ it "should be configurable" do
145
+ logger = NullLogger.new
146
+ @manager.configure do |config|
147
+ config[:logger] = logger
148
+ config[:max_threads] = 20
149
+ config[:max_ticks] = 21
150
+ config[:tick_size] = 59
151
+ config[:max_catchup] = 3000
152
+ config[:thread] = true
153
+ config[:namespace] = 'superhero'
154
+ end
155
+
156
+ assert_equal logger, @manager.config[:logger]
157
+ assert_equal 20, @manager.config[:max_threads]
158
+ assert_equal 21, @manager.config[:max_ticks]
159
+ assert_equal 59, @manager.config[:tick_size]
160
+ assert_equal 3000, @manager.config[:max_catchup]
161
+ assert_equal true, @manager.config[:thread]
162
+ assert_equal 'superhero', @manager.config[:namespace]
163
+ end
164
+
165
+ it "configuration should have reasonable defaults" do
166
+ @manager = Tickwork::Manager.new
167
+ assert @manager.config[:logger].is_a?(Logger)
168
+ assert_equal 10, @manager.config[:max_threads]
169
+ assert_equal 10, @manager.config[:max_ticks]
170
+ assert_equal 60, @manager.config[:tick_size]
171
+ assert_equal 3600, @manager.config[:max_catchup]
172
+ assert_equal false, @manager.config[:thread]
173
+ assert_equal '_tickwork_', @manager.config[:namespace]
174
+ end
175
+
176
+ it "config raises exception without a datastore" do
177
+ @my_manager = Tickwork::Manager.new
178
+ assert_raises Tickwork::Manager::NoDataStoreDefined do
179
+ @my_manager.configure do |config|
180
+ config[:tick_size] = 10
181
+ end
182
+ end
183
+ end
184
+
185
+ it "run raises exception without a datastore" do
186
+ @my_manager = Tickwork::Manager.new
187
+ assert_raises Tickwork::Manager::NoDataStoreDefined do
188
+ @my_manager.run
189
+ end
190
+ end
191
+
192
+ describe ':at option' do
193
+ it "once a day at 16:20" do
194
+ @manager.every(1.day, 'myjob', :at => '16:20')
195
+
196
+ assert_wont_run 'jan 1 2010 16:19:59'
197
+ assert_will_run 'jan 1 2010 16:20:00'
198
+ assert_wont_run 'jan 1 2010 16:20:01'
199
+ assert_wont_run 'jan 2 2010 16:19:59'
200
+ assert_will_run 'jan 2 2010 16:20:00'
201
+ end
202
+
203
+ it "twice a day at 16:20 and 18:10" do
204
+ @manager.every(1.day, 'myjob', :at => ['16:20', '18:10'])
205
+
206
+ assert_wont_run 'jan 1 2010 16:19:59'
207
+ assert_will_run 'jan 1 2010 16:20:00'
208
+ assert_wont_run 'jan 1 2010 16:20:01'
209
+
210
+ assert_wont_run 'jan 1 2010 18:09:59'
211
+ assert_will_run 'jan 1 2010 18:10:00'
212
+ assert_wont_run 'jan 1 2010 18:10:01'
213
+ end
214
+ end
215
+
216
+ describe ':tz option' do
217
+ it "time zone is not set by default" do
218
+ assert @manager.config[:tz].nil?
219
+ end
220
+
221
+ it "should be able to specify a different timezone than local" do
222
+ @manager.every(1.day, 'myjob', :at => '10:00', :tz => 'UTC')
223
+
224
+ assert_wont_run 'jan 1 2010 10:00:00 EST'
225
+ assert_will_run 'jan 1 2010 10:00:00 UTC'
226
+ end
227
+
228
+ it "should be able to specify a different timezone than local for multiple times" do
229
+ @manager.every(1.day, 'myjob', :at => ['10:00', '8:00'], :tz => 'UTC')
230
+
231
+ assert_wont_run 'jan 1 2010 08:00:00 EST'
232
+ assert_will_run 'jan 1 2010 08:00:00 UTC'
233
+ assert_wont_run 'jan 1 2010 10:00:00 EST'
234
+ assert_will_run 'jan 1 2010 10:00:00 UTC'
235
+ end
236
+
237
+ it "should be able to configure a default timezone to use for all events" do
238
+ @manager.configure { |config| config[:tz] = 'UTC' }
239
+ @manager.every(1.day, 'myjob', :at => '10:00')
240
+
241
+ assert_wont_run 'jan 1 2010 10:00:00 EST'
242
+ assert_will_run 'jan 1 2010 10:00:00 UTC'
243
+ end
244
+
245
+ it "should be able to override a default timezone in an event" do
246
+ @manager.configure { |config| config[:tz] = 'UTC' }
247
+ @manager.every(1.day, 'myjob', :at => '10:00', :tz => 'EST')
248
+
249
+ assert_will_run 'jan 1 2010 10:00:00 EST'
250
+ assert_wont_run 'jan 1 2010 10:00:00 UTC'
251
+ end
252
+ end
253
+
254
+ describe ':if option' do
255
+ it ":if true then always run" do
256
+ @manager.every(1.second, 'myjob', :if => lambda { |_| true })
257
+
258
+ assert_will_run 'jan 1 2010 16:20:00'
259
+ end
260
+
261
+ it ":if false then never run" do
262
+ @manager.every(1.second, 'myjob', :if => lambda { |_| false })
263
+
264
+ assert_wont_run 'jan 1 2010 16:20:00'
265
+ end
266
+
267
+ it ":if the first day of month" do
268
+ @manager.every(1.second, 'myjob', :if => lambda { |t| t.day == 1 })
269
+
270
+ assert_will_run 'jan 1 2010 16:20:00'
271
+ assert_wont_run 'jan 2 2010 16:20:00'
272
+ assert_will_run 'feb 1 2010 16:20:00'
273
+ end
274
+
275
+ it ":if it is compared to a time with zone" do
276
+ tz = 'America/Chicago'
277
+ time = Time.utc(2012,5,25,10,00)
278
+ @manager.every(1.second, 'myjob', tz: tz, :if => lambda { |t|
279
+ ((time - 1.hour)..(time + 1.hour)).cover? t
280
+ })
281
+ assert_will_run time
282
+ end
283
+
284
+ it ":if is not callable then raise ArgumentError" do
285
+ assert_raises(ArgumentError) do
286
+ @manager.every(1.second, 'myjob', :if => true)
287
+ end
288
+ end
289
+ end
290
+
291
+ describe "max_threads" do
292
+ it "should warn when an event tries to generate threads more than max_threads" do
293
+ logger = NullLogger.new
294
+ @manager.configure do |config|
295
+ config[:max_threads] = 1
296
+ config[:logger] = logger
297
+ end
298
+
299
+ @manager.every(1.minute, 'myjob1', :thread => true) { sleep 2 }
300
+ @manager.every(1.minute, 'myjob2', :thread => true) { sleep 2 }
301
+ logger.expects(:error).with("Threads exhausted; skipping myjob2")
302
+
303
+ @manager.tick(Time.now)
304
+ end
305
+
306
+ it "should not warn when thread is managed by others" do
307
+ begin
308
+ t = Thread.new { sleep 5 }
309
+ logger = Logger.new(StringIO.new)
310
+ @manager.configure do |config|
311
+ config[:max_threads] = 1
312
+ config[:logger] = logger
313
+ end
314
+
315
+ @manager.every(1.minute, 'myjob', :thread => true)
316
+ logger.expects(:error).never
317
+
318
+ @manager.tick(Time.now)
319
+ ensure
320
+ t.kill
321
+ end
322
+ end
323
+ end
324
+
325
+ describe "callbacks" do
326
+ it "should not accept unknown callback name" do
327
+ assert_raises(RuntimeError, "Unsupported callback unknown_callback") do
328
+ @manager.on(:unknown_callback) do
329
+ true
330
+ end
331
+ end
332
+ end
333
+
334
+ it "should run before_tick callback once on tick" do
335
+ counter = 0
336
+ @manager.on(:before_tick) do
337
+ counter += 1
338
+ end
339
+ @manager.tick
340
+ assert_equal 1, counter
341
+ end
342
+
343
+ it "should not run events if before_tick returns false" do
344
+ @manager.on(:before_tick) do
345
+ false
346
+ end
347
+ @manager.every(1.second, 'myjob') { raise "should not run" }
348
+ @manager.tick
349
+ end
350
+
351
+ it "should run before_run twice if two events are registered" do
352
+ counter = 0
353
+ @manager.on(:before_run) do
354
+ counter += 1
355
+ end
356
+ @manager.every(1.second, 'myjob')
357
+ @manager.every(1.second, 'myjob2')
358
+ @manager.tick
359
+ assert_equal 2, counter
360
+ end
361
+
362
+ it "should run even jobs only" do
363
+ counter = 0
364
+ ran = false
365
+ @manager.on(:before_run) do
366
+ counter += 1
367
+ counter % 2 == 0
368
+ end
369
+ @manager.every(1.second, 'myjob') { raise "should not ran" }
370
+ @manager.every(1.second, 'myjob2') { ran = true }
371
+ @manager.tick
372
+ assert ran
373
+ end
374
+
375
+ it "should run after_run callback for each event" do
376
+ counter = 0
377
+ @manager.on(:after_run) do
378
+ counter += 1
379
+ end
380
+ @manager.every(1.second, 'myjob')
381
+ @manager.every(1.second, 'myjob2')
382
+ @manager.tick
383
+ assert_equal 2, counter
384
+ end
385
+
386
+ it "should run after_tick callback once" do
387
+ counter = 0
388
+ @manager.on(:after_tick) do
389
+ counter += 1
390
+ end
391
+ @manager.tick
392
+ assert_equal 1, counter
393
+ end
394
+ end
395
+
396
+ it "should start from last tick" do
397
+ @manager.configure do |config|
398
+ config[:tick_size] = 1
399
+ config[:max_ticks] = 1
400
+ end
401
+ last = Time.now.to_i - 1000
402
+ @manager.data_store.set(@manager.data_store_key, last)
403
+ @manager.expects(:tick).with(last + 1).then.returns
404
+ @manager.run
405
+ end
406
+
407
+ it "should tick to max_ticks" do
408
+ @manager.configure do |config|
409
+ config[:tick_size] = 1
410
+ config[:max_ticks] = 3
411
+ end
412
+ last = Time.now.to_i - 1000
413
+ @manager.data_store.set(@manager.data_store_key, last)
414
+ @manager.expects(:tick).with(last + 1).then.returns
415
+ @manager.expects(:tick).with(last + 2).then.returns
416
+ @manager.expects(:tick).with(last + 3).then.returns
417
+ @manager.run
418
+ end
419
+
420
+ it "should tick by tick size" do
421
+ @manager.configure do |config|
422
+ config[:tick_size] = 2
423
+ config[:max_ticks] = 3
424
+ end
425
+ last = Time.now.to_i - 1000
426
+ @manager.data_store.set(@manager.data_store_key, last)
427
+ @manager.expects(:tick).with(last + 2).then.returns
428
+ @manager.expects(:tick).with(last + 4).then.returns
429
+ @manager.expects(:tick).with(last + 6).then.returns
430
+ @manager.run
431
+ end
432
+
433
+ it "should not tick into the future" do
434
+ @manager.configure do |config|
435
+ config[:tick_size] = 10
436
+ config[:max_ticks] = 3
437
+ end
438
+ last = Time.now.to_i - 1
439
+ @manager.data_store.set(@manager.data_store_key, last)
440
+ module Failure
441
+ def tick
442
+ raise "don't call me"
443
+ end
444
+ end
445
+ @manager.extend Failure
446
+ @manager.run
447
+ end
448
+
449
+ it "should save the last tick time" do
450
+ @manager.configure do |config|
451
+ config[:tick_size] = 10
452
+ config[:max_ticks] = 1
453
+ end
454
+ last = Time.now.to_i - 1000
455
+ @manager.data_store.set(@manager.data_store_key, last)
456
+ @manager.expects(:tick).with(last + 10).then.returns
457
+ @manager.run
458
+ assert_equal (last + 10), @manager.data_store.get(@manager.data_store_key)
459
+ end
460
+
461
+ it "should tick from now if no last time" do
462
+ @manager.configure do |config|
463
+ config[:tick_size] = 10
464
+ config[:max_ticks] = 1
465
+ end
466
+ @manager.expects(:tick).with(Time.now.to_i).then.returns
467
+ @manager.run
468
+ end
469
+
470
+ it "should be saving event last run times" do
471
+ @manager.configure do |config|
472
+ config[:tick_size] = 10
473
+ config[:max_ticks] = 1
474
+ end
475
+ @manager.every(1.minute, 'myjob')
476
+ assert_equal 0, @manager.config[:data_store].size
477
+ @manager.run
478
+ assert_equal 2, @manager.config[:data_store].size
479
+ assert_equal false, @manager.config[:data_store].get('_tickwork_myjob').nil?
480
+ end
481
+
482
+ it "should start from max catchup when last is further in the past" do
483
+ @manager.configure do |config|
484
+ config[:tick_size] = 1
485
+ config[:max_ticks] = 1
486
+ config[:max_catchup] = 1800
487
+ end
488
+
489
+ @manager.every(1.minute, 'myjob')
490
+ last = Time.now.to_i - 3600
491
+ @manager.data_store.set(@manager.data_store_key, last)
492
+ @manager.expects(:tick).with(Time.now.to_i - 1800).then.returns
493
+ @manager.run
494
+ end
495
+
496
+ it "0 should disable max catchup" do
497
+ @manager.configure do |config|
498
+ config[:tick_size] = 1
499
+ config[:max_ticks] = 1
500
+ config[:max_catchup] = 0
501
+ end
502
+
503
+ @manager.every(1.minute, 'myjob')
504
+ last = Time.now.to_i - 36000
505
+ @manager.data_store.set(@manager.data_store_key, last)
506
+ @manager.expects(:tick).with(Time.now.to_i - 36000 + 1).then.returns
507
+ @manager.run
508
+ end
509
+
510
+ it "0 should disable max catchup" do
511
+ @manager.configure do |config|
512
+ config[:tick_size] = 1
513
+ config[:max_ticks] = 1
514
+ config[:max_catchup] = nil
515
+ end
516
+
517
+ @manager.every(1.minute, 'myjob')
518
+ last = Time.now.to_i - 36000
519
+ @manager.data_store.set(@manager.data_store_key, last)
520
+ @manager.expects(:tick).with(Time.now.to_i - 36000 + 1).then.returns
521
+ @manager.run
522
+ end
523
+
524
+ it "should return the last time it ran" do
525
+ @manager.configure do |config|
526
+ config[:tick_size] = 10
527
+ config[:max_ticks] = 2
528
+ end
529
+ last = Time.now.to_i - 1000
530
+ @manager.data_store.set(@manager.data_store_key, last)
531
+ @manager.expects(:tick).with(last + 10).then.returns
532
+ @manager.expects(:tick).with(last + 20).then.returns
533
+ assert_equal last + 20, @manager.run
534
+ end
535
+
536
+ it "should clear it's datastore on #clear!" do
537
+ @manager.data_store.set(@manager.data_store_key, "10")
538
+ @manager.clear!
539
+ assert_equal nil, @manager.data_store.get(@manager.data_store_key)
540
+ end
541
+
542
+ describe 'error_handler' do
543
+ before do
544
+ @errors = []
545
+ @manager.error_handler do |e|
546
+ @errors << e
547
+ end
548
+
549
+ # block error log
550
+ @string_io = StringIO.new
551
+ @manager.configure do |config|
552
+ config[:logger] = Logger.new(@string_io)
553
+ end
554
+ @manager.every(1.second, 'myjob') { raise 'it error' }
555
+ end
556
+
557
+ it 'registered error_handler handles error from event' do
558
+ @manager.tick
559
+ assert_equal ['it error'], @errors.map(&:message)
560
+ end
561
+
562
+ it 'error is notified to logger and handler' do
563
+ @manager.tick
564
+ assert @string_io.string.include?('it error')
565
+ end
566
+
567
+ it 'error in handler will NOT be suppressed' do
568
+ @manager.error_handler do |e|
569
+ raise e.message + ' re-raised'
570
+ end
571
+ assert_raises(RuntimeError, 'it error re-raised') do
572
+ @manager.tick
573
+ end
574
+ end
575
+ end
576
+ end
@@ -0,0 +1,19 @@
1
+ # https://github.com/karafka/null-logger/blob/master/lib/null_logger.rb
2
+ # under MIT license as of 5/9/2016
3
+
4
+ # Null logger class
5
+ # Is used when logger is not defined
6
+ class NullLogger
7
+ # Possible log levels from ruby Logger::Severity class
8
+ LOG_LEVELS = %w( unknown fatal error warn info debug ).freeze
9
+
10
+ # Returns nil for any method call from LOG_LEVELS array
11
+ # Instead raise NoMethodError
12
+ # @example:
13
+ # NullLogger.new.fatal -> return nil
14
+ # NullLogger.new.wrong_method -> raise NoMethodError
15
+ def method_missing(method_name, *args, &block)
16
+ return nil if LOG_LEVELS.include?(method_name.to_s)
17
+ super(method_name, *args, &block)
18
+ end
19
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path('../../lib/tickwork', __FILE__)
2
+ require File.expand_path('../data_stores/fake_store.rb', __FILE__)
3
+ require 'minitest/autorun'
4
+ require 'mocha/mini_test'
5
+
6
+ describe Tickwork do
7
+ def self.test_order
8
+ :alpha
9
+ end
10
+ before do
11
+ @log_output = StringIO.new
12
+ Tickwork.configure do |config|
13
+ config[:logger] = Logger.new(@log_output)
14
+ #config[:logger] = Logger.new(STDOUT)
15
+ config[:data_store] = Tickwork::FakeStore.new
16
+ end
17
+ end
18
+
19
+ after do
20
+ Tickwork.clear!
21
+ end
22
+
23
+ it 'should run events with configured logger' do
24
+ run = false
25
+ Tickwork.handler do |job|
26
+ run = job == 'myjob'
27
+ end
28
+ Tickwork.every(1.minute, 'myjob')
29
+ # don't know why this doesn't work with while but did with loop
30
+ #Tickwork.manager.expects(:while).yields.then.returns
31
+ Tickwork.run
32
+
33
+ assert run
34
+ assert @log_output.string.include?('Triggering')
35
+ end
36
+
37
+ it 'should log event correctly' do
38
+ run = false
39
+ Tickwork.handler do |job|
40
+ run = job == 'an event'
41
+ end
42
+ Tickwork.every(1.minute, 'an event')
43
+ # Tickwork.manager.expects(:while).yields.then.returns
44
+ Tickwork.run
45
+ assert run
46
+ assert @log_output.string.include?("Triggering 'an event'")
47
+ end
48
+
49
+ it 'should pass event without modification to handler' do
50
+ event_object = 'myEvent'
51
+ run = false
52
+ Tickwork.handler do |job|
53
+ run = job == event_object
54
+ end
55
+ Tickwork.every(1.minute, event_object)
56
+ # Tickwork.manager.expects(:while).yields.then.returns
57
+ Tickwork.run
58
+ assert run
59
+ end
60
+
61
+ it 'should not run anything after reset' do
62
+ Tickwork.every(1.minute, 'myjob') { }
63
+ Tickwork.clear!
64
+ Tickwork.configure do |config|
65
+ config[:sleep_timeout] = 0
66
+ config[:logger] = Logger.new(@log_output)
67
+ config[:data_store] = Tickwork::FakeStore.new
68
+ end
69
+ # Tickwork.manager.expects(:while).yields.then.returns
70
+ Tickwork.run
71
+ assert @log_output.string.include?('0 events')
72
+ end
73
+
74
+ it 'should pass all arguments to every' do
75
+ Tickwork.every(1.second, 'myjob', if: lambda { |_| false }) { }
76
+ # Tickwork.manager.expects(:while).yields.then.returns
77
+ Tickwork.run
78
+ assert @log_output.string.include?('1 events')
79
+ assert !@log_output.string.include?('Triggering')
80
+ end
81
+
82
+ it 'support module re-open style' do
83
+ $called = false
84
+ module ::Tickwork
85
+ every(1.second, 'myjob') { $called = true }
86
+ end
87
+ # Tickwork.manager.expects(:while).yields.then.returns
88
+ Tickwork.run
89
+ assert $called
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "tickwork"
3
+ s.version = "0.0.1"
4
+
5
+ s.authors = ["John Hinnegan"]
6
+ s.license = 'MIT'
7
+ s.description = "A fork of clockwork. Under development."
8
+ s.email = ["tickwork@johnhinnegan.com"]
9
+ s.extra_rdoc_files = [
10
+ "README.md"
11
+ ]
12
+ s.homepage = "http://github.com/softwaregravy/clockwork"
13
+ s.summary = "A scheduling library"
14
+
15
+ s.files = `git ls-files`.split($/)
16
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency(%q<tzinfo>)
21
+ s.add_dependency(%q<activesupport>)
22
+
23
+ s.add_development_dependency "bundler", "~> 1.3"
24
+ s.add_development_dependency "rake"
25
+ s.add_development_dependency "daemons"
26
+ s.add_development_dependency "minitest", "~> 5.8"
27
+ s.add_development_dependency "mocha"
28
+ end