Module Metatable Workaround

I need my modules to be able to access each other without long and complicated paths. Hence, metatables are the obvious solution. However, this is the issue I’m running into:

local moduleTable = require(module)
setmetatable(moduleTable, {
	__index = function()
		return "Test"
	end,
})

This is the code that “loads” each module. For testing, I turned __index into a function rather than a table of the other modules. The issue that I’m having here is that the metatable is not set until after the module is required. Therefore paths cannot be defined at the top of my code. Here is what I’m talking about:

local module = {}
print(module.Foo) -- This does not print "Test" as previously defined in my other code sample because the metatable has not been set yet.

return module
local module = {}

spawn(function()
	wait() -- This does print "Test" because the metatable is set by the time wait() is completed in this new thread
	print(module.Test)
end)

return module

I could always just define the paths within each function, but that kind of defeats my original intentions of removing long paths to other modules and is repetitive.

Does anyone have any suggestions to how I can work around this (besides the bandaid solution I posted above)? Kind of stuck atm.

1 Like

I don’t really understand your problem, but this may help:

--//ModuleScript
local module = {}
setmetatable(module, {
	__index = function()
		return "Test"
	end,
})

return module
--//Requiring Script
local table = require(ModuleScript)

print(table.Index) --//"Test"

That is backwards. The problem is not the order in which I’m setting the metatable. I gave examples towards the bottom of my original post.

The issue is that __index will not be invoked at runtime because the code at the top of the module is ran at require() before the metatable is set.

Not sure what you’re expecting to happen here? Lua runs off of a single thread (typically, granted there are ways to run semi-asynchronously, coroutine library, etc…), and therefor everything is linear.

You require the module > the module runs the code within it > you set the metamethods.

The majority of frameworks (if not all), which in theory is what you’re sort of attempting to make here, tend to have starting functions. Keep in mind when using ModuleScripts it’s good habbit to always define functions and execute them, rather then executing code on initialization of the module script.

An example of a ‘start’ function would be as follows.

local ModuleScript = {}
local path1, path2;

function ModuleScript:Start()
      path1, path2 = ... -- define paths properly (ex; self.path1, self.path2)
end

return ModuleScript 
local requiredModule = require(...)
local paths = {
   path1 = ...;
   path2 = ...;
}

setmetatable(requiredModule, {
   __index = paths
})

requiredModule:Start()