Chen
Chen

Reputation: 1672

How to organize Lua module path and write "require" calls without losing flexibility?

Say I have a project, whose folder structure looks like below:

| main.lua
|
|---<model> // this is a folder
|    |a.lua
|    |b.lua
|
|---<view>
     |a.lua
     |b.lua

model/a.lua requries model/b.lua: require "b"

view/a.lua requries view/b.lua: require "b"

main.lua requries files in model and view.


Now I have problem to get these modules loaded correctly. I know I can fix it by changing the require calls to:

model/a.lua: require "model.b"

view/a.lua: require "view.b"

But if I do that, I have to modify these files every time when I change the folder structure.

So my questions are:

  1. How to fix the module path issue without hard code paths in module files?
  2. Why Lua doesn't use the module search rule of Node.js, which looks easier?

Upvotes: 13

Views: 19743

Answers (4)

leesei
leesei

Reputation: 6070

The solution is to add the folder of main.lua (project root) to package.path in main.lua.

A naive way to support folders of 1 level deep:

-- main.lua
package.path = package.path .. ";../?.lua"

Note for requires in (project root) will look up files outside of project root, which is not desirable.

A better way of to use some library (e.g.: paths, penlight) to resolve the absolute path and add it instead:

-- main.lua
local projectRoot = lib.abspath(".")
package.path = package.path .. ";" .. projectRoot .. "/?.lua"

Then in you source use the folder name to scope the files:

-- model/a.lua
require "model.b"
-- you can even do this
require "view.b"

and

-- view/a.lua
require "view.b"

Upvotes: 1

greatwolf
greatwolf

Reputation: 20838

When you require a module, the string parameter from require gets passed into the module which you can access using the variable-argument syntax .... You can use this to include other dependent modules which reside in the same path as the current module being requireed without making it dependent on a fixed hard-coded module name.

For your example, instead of doing:

-- model/a.lua
require "model.b"

and

-- view/a.lua
require "view.b"

You can do:

-- model/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")

and

-- view/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")

Now if you change directory structure, eg. move view to something like control/subcontrol/foobar, then control/subcontrol/foobar/a.lua (formerly view/a.lua) will now try to require control/subcontrol/foobar/b.lua instead and "do the right thing".

Of course main.lua will still need to fully qualify the paths since you need some way to disambiguate between model/a.lua and view/a.lua.

Upvotes: 11

Doug Currie
Doug Currie

Reputation: 41170

There are a couple approaches you can use.

You can add relative paths to package.path as in this SO answer. In your case you'd want to add paths in main.lua that correspond to the various ways you might access the files. This keeps all the changes required when changing your directory structure local to one file.

You can add absolute paths to package.pathusing debug.getinfo -- this may be a little easier since you don't need to account for all the relative accesses, but you still need to do this in main.lua when changing your directory structure, and you need to do string manipulation on the value returned by debug.getinfo to strip the module name and add the subdirectory names.

> lunit = require "lunit"
> info = debug.getinfo(lunit.run, "S")
> =info.source
@/usr/local/share/lua/5.2/lunit.lua
> =info.short_src
/usr/local/share/lua/5.2/lunit.lua

Upvotes: 3

Yu Hao
Yu Hao

Reputation: 122383

How to fix the module path issue without hard code paths in module files?

I don't have any better cross-platform solution, maybe you should plan the folder structure early on.

Why Lua doesn't use the module search rule of Node.js, which looks easier?

Because Lua tries its best to rely only on ANSI C, which is really successful. And in ANSI C, there's no such concept of directories.

Upvotes: 3

Related Questions