cron.before and cron.after now return a clock

This commit is contained in:
kikito 2013-09-25 18:40:12 +02:00
parent d7b075a076
commit c4f205460f
2 changed files with 92 additions and 115 deletions

View File

@ -28,8 +28,6 @@ local cron = {
-- Private functions -- Private functions
local entries -- initialized in cron.reset
local function isCallable(callback) local function isCallable(callback)
local tc = type(callback) local tc = type(callback)
if tc == 'function' then return true end if tc == 'function' then return true end
@ -40,67 +38,61 @@ local function isCallable(callback)
return false return false
end end
local function checkTimeAndCallback(time, callback) local function checkPositiveInteger(name, value)
assert(type(time) == "number" and time > 0, "time must be a positive number") if type(value) ~= "number" or value < 0 then
assert(isCallable(callback), "callback must be a function") error(name .. " must be a positive number")
end
local function newEntry(time, callback, update, ...)
local entry = {
time = time,
callback = callback,
args = {...},
running = 0,
update = update
}
entries[entry] = entry
return entry
end
local function updateTimedEntry(self, dt) -- returns true if expired
self.running = self.running + dt
if self.running >= self.time then
self.callback(unpack(self.args))
cron.cancel(self)
end end
end end
local function updatePeriodicEntry(self, dt) local Clock = {}
local Clock_mt = {__index = Clock}
local function newClock(time, callback, update, ...)
checkPositiveInteger('time', time)
assert(isCallable(callback), "callback must be a function")
return setmetatable({
time = time,
callback = callback,
args = {...},
running = 0,
update = update
}, Clock_mt)
end
local function updateAfterClock(self, dt) -- returns true if expired
checkPositiveInteger('dt', dt)
if self.running >= self.time then return true end
self.running = self.running + dt
if self.running >= self.time then
self.callback(unpack(self.args))
return true
end
return false
end
local function updateEveryClock(self, dt)
checkPositiveInteger('dt', dt)
self.running = self.running + dt self.running = self.running + dt
while self.running >= self.time do while self.running >= self.time do
self.callback(unpack(self.args)) self.callback(unpack(self.args))
self.running = self.running - self.time self.running = self.running - self.time
end end
end return false
-- Public functions
function cron.cancel(id)
entries[id] = nil
end end
function cron.after(time, callback, ...) function cron.after(time, callback, ...)
checkTimeAndCallback(time, callback) return newClock(time, callback, updateAfterClock, ...)
return newEntry(time, callback, updateTimedEntry, ...)
end end
function cron.every(time, callback, ...) function cron.every(time, callback, ...)
checkTimeAndCallback(time, callback) return newClock(time, callback, updateEveryClock, ...)
return newEntry(time, callback, updatePeriodicEntry, ...)
end end
function cron.update(dt)
assert(type(dt) == "number" and dt >= 0, "dt must be a non-negative number")
for _, entry in pairs(entries) do entry:update(dt) end
end
function cron.reset()
entries = {}
end
cron.reset()
return cron return cron

View File

@ -2,7 +2,7 @@ local cron = require 'cron'
describe( 'cron', function() describe( 'cron', function()
local counter = 0 local counter
local function count(amount) local function count(amount)
amount = amount or 1 amount = amount or 1
counter = counter + amount counter = counter + amount
@ -11,41 +11,25 @@ describe( 'cron', function()
before_each(function() before_each(function()
counter = 0 counter = 0
cron.reset()
end) end)
describe('.update', function()
it('throws an error if dt is a negative number', function()
assert.error(function() cron.update() end)
assert.error(function() cron.update(-1) end)
assert.not_error(function() cron.update(1) end)
end)
end)
describe('.reset', function() describe('clock', function()
it('Cancels all timed actions', function()
cron.after(1, count)
cron.after(2, count)
cron.update(1)
assert.equal(counter, 1)
cron.reset()
cron.update(1)
assert.equal(counter, 1)
end)
it('Cancels all periodical actions', function() describe(':update', function()
cron.every(1, count) it('throws an error if dt is a negative number', function()
cron.update(1) local clock = cron.every(1, count)
assert.equal(counter, 1) assert.error(function() clock:update() end)
cron.reset() assert.error(function() clock:update(-1) end)
cron.update(1) assert.not_error(function() clock:update(1) end)
assert.equal(counter, 1) end)
end) end)
end) end)
describe('.after', function() describe('.after', function()
it('Throws error if time is not a positive number, or callback is not callable', function() it('checks parameters', function()
assert.error(function() cron.after('error', count) end) assert.error(function() cron.after('error', count) end)
assert.error(function() cron.after(2, 'error') end) assert.error(function() cron.after(2, 'error') end)
assert.error(function() cron.after(-2, count) end) assert.error(function() cron.after(-2, count) end)
@ -54,28 +38,45 @@ describe( 'cron', function()
assert.not_error(function() cron.after(2, countable) end) assert.not_error(function() cron.after(2, countable) end)
end) end)
it('Executes timed actions only once, at the right time', function() it('produces a clock that executes actions only once, at the right time', function()
cron.after(2, count) local c1 = cron.after(2, count)
cron.after(4, count) local c2 = cron.after(4, count)
cron.update(1)
-- 1
c1:update(1)
assert.equal(counter, 0) assert.equal(counter, 0)
cron.update(1) c2:update(1)
assert.equal(counter, 0)
-- 2
c1:update(1)
assert.equal(counter, 1) assert.equal(counter, 1)
cron.update(1) c2:update(1)
assert.equal(counter, 1) assert.equal(counter, 1)
cron.update(1)
-- 3
c1:update(1)
assert.equal(counter, 1)
c2:update(1)
assert.equal(counter, 1)
-- 4
c1:update(1)
assert.equal(counter, 1)
c2:update(1)
assert.equal(counter, 2) assert.equal(counter, 2)
end) end)
it('Passes on parameters to the function, if specified', function() it('Passes on parameters to the callback', function()
cron.after(1, count, 2) local c1 = cron.after(1, count, 2)
cron.update(1) c1:update(1)
assert.equal(counter, 2) assert.equal(counter, 2)
end) end)
end) end)
describe('.every', function() describe('.every', function()
it('Throws errors if time is not a positive number, or callback is not function', function() it('checks parameters', function()
assert.error(function() cron.every('error', count) end) assert.error(function() cron.every('error', count) end)
assert.error(function() cron.every(2, 'error') end) assert.error(function() cron.every(2, 'error') end)
assert.error(function() cron.every(-2, count) end) assert.error(function() cron.every(-2, count) end)
@ -84,49 +85,33 @@ describe( 'cron', function()
assert.not_error(function() cron.every(2, countable) end) assert.not_error(function() cron.every(2, countable) end)
end) end)
it('Executes periodical actions periodically', function() it('Invokes callback periodically', function()
cron.every(3, count) local c = cron.every(3, count)
cron.update(1)
c:update(1)
assert.equal(counter, 0) assert.equal(counter, 0)
cron.update(2)
c:update(2)
assert.equal(counter, 1) assert.equal(counter, 1)
cron.update(2)
c:update(2)
assert.equal(counter, 1) assert.equal(counter, 1)
cron.update(1)
c:update(1)
assert.equal(counter, 2) assert.equal(counter, 2)
end) end)
it('Executes the same action multiple times on a single update if appropiate', function() it('Executes the same action multiple times on a single update if appropiate', function()
cron.every(1, count) local c = cron.every(1, count)
cron.update(2) c:update(2)
assert.equal(counter, 2) assert.equal(counter, 2)
end) end)
it('Respects parameters', function() it('Respects parameters', function()
cron.every(1, count, 2) local c = cron.every(1, count, 2)
cron.update(2) c:update(2)
assert.equal(counter, 4) assert.equal(counter, 4)
end) end)
end) end)
describe('.cancel', function()
it('Cancels timed entries', function()
local id = cron.after(1, count)
cron.update(1)
assert.equal(counter, 1)
cron.cancel(id)
cron.update(1)
assert.equal(counter, 1)
end)
it('Cancels periodical entries', function()
local id = cron.every(1, count)
cron.update(1)
assert.equal(counter, 1)
cron.cancel(id)
cron.update(1)
assert.equal(counter, 1)
end)
end)
end) end)