Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

module

lua

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?
like image 584
Chen Avatar asked Aug 09 '13 16:08

Chen


4 Answers

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.

like image 71
greatwolf Avatar answered Oct 08 '22 15:10

greatwolf


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.

like image 34
Yu Hao Avatar answered Oct 08 '22 15:10

Yu Hao


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
like image 26
Doug Currie Avatar answered Oct 08 '22 15:10

Doug Currie


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"
like image 25
leesei Avatar answered Oct 08 '22 13:10

leesei