Skip to content

Instantly share code, notes, and snippets.

@SamMousa
Created July 6, 2021 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SamMousa/ece11e30aaaf9088d65aa5ceb9a16d01 to your computer and use it in GitHub Desktop.
Save SamMousa/ece11e30aaaf9088d65aa5ceb9a16d01 to your computer and use it in GitHub Desktop.
AMD Style module loader for LUA
--[[ BEGIN LIBRARY CODE ]]--
local libraries = {}
local queue = {}
local function assertString(param)
if type(param) ~= 'string' then
error(string.format("Expected string got %s", type(param)))
end
end
local function coalesce(...)
for i = 1, select('#', ...) do
local item = select(i, ...)
if (item ~= nil) then
return item
end
end
end
local function assertBoolean(param)
if type(param) ~= 'boolean' then
error(string.format("Expected boolean got %s", type(param)))
end
end
-- @return bool
local function allLoaded(dependencies)
for _, dep in ipairs(dependencies) do
if not libraries[dep] then
return false
end
end
return true
end
-- @return list
local function retrieve(dependencies, requester)
local result = {}
for _, dep in ipairs(dependencies) do
if not libraries[dep] then
error(string.format("Library %s not loaded", dep));
end
local d = libraries[dep]
if type(d) == 'function' then
print("calling constructor")
table.insert(result, libraries[dep](requester))
else
table.insert(result, libraries[dep])
end
end
return result
end
local function loadLibrary(name, dependencies, callback)
print(string.format("Loading %s", name))
libraries[name] = callback(unpack(retrieve(dependencies, name)))
end
local function checkQueue()
local loadedAny = false
local newQueue = {}
local unloaded = 1
for i, definition in ipairs(queue) do
if allLoaded(definition.dependencies) then
loadLibrary(definition.name, definition.dependencies, definition.callback)
loadedAny = true
else
newQueue[unloaded] = definition
unloaded = unloaded + 1
end
end
if loadedAny then
queue = newQueue
-- use return for efficient tail recursion
return checkQueue()
end
end
function define(name, dependencies, callback)
print(string.format("Defining %s with %d dependencies", name, #dependencies))
-- Shortcut check if loaded
if allLoaded(dependencies) then
loadLibrary(name, dependencies, callback)
checkQueue()
else
table.insert(queue, {name = name, dependencies = dependencies, callback = callback})
end
end
--[[ END LIBRARY CODE ]]--
--[[ MODULE myModule ]]--
define('myModule', {'dep1', 'dep2'}, function(dep1, dep2)
return {}
end)
--[[ MODULE database ]]--
define('database', {'storage'}, function(storage)
storage.initialize();
-- must return a constructor function
return function(module)
assertString(module)
local prefix = module .. '.'
return {
setBoolean = function(name, value)
assertBoolean(value)
storage.set(prefix .. name, value)
end,
getBoolean = function(name, default)
local result = storage.get(prefix .. name)
return coalesce(result, default)
end
}
end
end)
--[[ MODULE storage ]]--
define('storage', {}, function()
local data = {}
local api = {
initialize = function() print("Initializing storage") end,
set = function(key, value)
print (string.format("Storing key %s, value %s", key, tostring(value)))
data[key] = value
end,
get = function(key)
print (string.format("Retrieving key %s", key))
return data[key]
end,
}
return api
end)
--[[ MODULE dep1 ]]--
define('dep1', {'dep2'}, function(dep2)
return {}
end)
--[[ MODULE dep2 ]]--
define('dep2', {'database'}, function(database)
database.setBoolean('isEnabled', false)
print(database.getBoolean('isEnabled', true))
print(database.getBoolean('unknownSetting', true))
return {}
end)
--[[ TEST CODE
Since modules are loaded out of order, we never know when we are done.
In real world scenario we could set a timeout and throw an exception if not all dependencies are available after it has expired.
Alternatively we start a ticker that just prints the status of the queue ever 5 seconds, if not empty.
]]--
if #queue > 0 then
for i, definition in ipairs(queue) do
print(string.format("Module: %s was not loaded", definition.name))
end
error("Not all modules could be loaded")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment