Module:Require when needed

From The Languages of David J. Peterson
Jump to navigation Jump to search

Documentation for this module may be created at Module:Require when needed/documentation

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