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
end end
local function newEntry(time, callback, update, ...) local Clock = {}
local entry = { 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, time = time,
callback = callback, callback = callback,
args = {...}, args = {...},
running = 0, running = 0,
update = update update = update
} }, Clock_mt)
entries[entry] = entry
return entry
end end
local function updateTimedEntry(self, dt) -- returns true if expired 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 self.running = self.running + dt
if self.running >= self.time then if self.running >= self.time then
self.callback(unpack(self.args)) self.callback(unpack(self.args))
cron.cancel(self) return true
end end
return false
end end
local function updatePeriodicEntry(self, dt) 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()
describe('clock', function()
describe(':update', function()
it('throws an error if dt is a negative number', function() it('throws an error if dt is a negative number', function()
assert.error(function() cron.update() end) local clock = cron.every(1, count)
assert.error(function() cron.update(-1) end) assert.error(function() clock:update() end)
assert.not_error(function() cron.update(1) end) assert.error(function() clock:update(-1) end)
assert.not_error(function() clock:update(1) end)
end) end)
end) end)
describe('.reset', 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) end)
it('Cancels all periodical actions', function()
cron.every(1, count)
cron.update(1)
assert.equal(counter, 1)
cron.reset()
cron.update(1)
assert.equal(counter, 1)
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)