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 setmetatable = setmetatable
local tostring = tostring
local unpack = unpack

local function get_nested(a, b, ...)
	if not a then
		return nil
	elseif ... then
		return get_nested(a[b], ...)
	end
	return a[b]
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)
	return (mt[0] or get_obj(mt))(...)
end

local function __index(self, k)
	local mt = getmetatable(self)
	return (mt[0] or get_obj(mt))[k]
end

local function __ipairs(self)
	local mt = getmetatable(self)
	return ipairs(mt[0] or get_obj(mt))
end

local function __newindex(self, k, v)
	local mt = getmetatable(self)
	local t = mt[0] or get_obj(mt)
	t[k] = v
end

local function __pairs(self)
	local mt = getmetatable(self)
	return pairs(mt[0] or get_obj(mt))
end

local function __tostring(self)
	local mt = getmetatable(self)
	return tostring(mt[0] or get_obj(mt))
end

return function(modname, ...)
	local obj = loaded[modname]
	if not obj then
		return setmetatable({}, {
			modname,
			__call = __call,
			__index = __index,
			__ipairs = __ipairs,
			__newindex = __newindex,
			__pairs = __pairs,
			__tostring = __tostring,
			...
		})
	elseif ... then
		return get_nested(obj, ...)
	end
	return obj
end