Module:require when needed

From Wiktionary, the free dictionary
Jump to navigation Jump to search

A helper function which can be used in place of require if it's not certain that the module in question will be needed. If the module has already been loaded, then it is simply returned; if it hasn't, then it won't be loaded until it is first used.

This is generally useful when defining variables at the start of a module.


local getmetatable = getmetatable
local ipairs = ipairs
local loaded = package.loaded
local pairs = pairs
local require = require
local select = select
local setmetatable = setmetatable
local tostring = tostring
local unpack = unpack

local function get_nested(obj, ...)
	local n = select("#", ...)
	if n == 0 then
		return obj
	end
	obj = obj[...]
	for i = 2, n do
		obj = obj[select(i, ...)]
	end
	return obj
end

local function get_obj(mt)
	local obj = require(mt[1])
	if #mt > 1 then
		obj = get_nested(obj, unpack(mt, 2))
	end
	mt[0] = obj
	return obj
end

local function __call(self, ...)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	return obj(...)
end

local function __index(self, k)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	return obj[k]
end

local function __ipairs(self)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	return ipairs(obj)
end

local function __newindex(self, k, v)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	obj[k] = v
end

local function __pairs(self)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	return pairs(obj)
end

local function __tostring(self)
	local mt = getmetatable(self)
	local obj = mt[0]
	if obj == nil then
		obj = get_obj(mt)
	end
	return tostring(obj)
end

return function(modname, ...)
	local mod = loaded[modname]
	if mod ~= nil then
		return get_nested(mod, ...)
	end
	return setmetatable({}, {
		modname,
		__call = __call,
		__index = __index,
		__ipairs = __ipairs,
		__newindex = __newindex,
		__pairs = __pairs,
		__tostring = __tostring,
		-- TODO: other metamethods, if needed.
		...
	})
end