56

If I have a file structure like this:

./main.lua
./mylib/mylib.lua
./mylib/mylib-utils.lua
./mylib/mylib-helpers.lua
./mylib/mylib-other-stuff.lua

From main.lua the file mylib.lua can be loaded with full path require('mylib.mylib'). But inside mylib.lua I would also like to load other necessary modules and I don't feel like always specifying the full path (e.g. mylib.mylib-utils). If I ever decide to move the folder I'm going to have a lot of search and replace. Is there a way to use just the relative part of the path?

UPD. I'm using Lua with Corona SDK, if that matters.

RocketR
  • 3,626
  • 2
  • 25
  • 38

5 Answers5

51

There is a way of deducing the "local path" of a file (more concretely, the string that was used to load the file).

If you are requiring a file inside lib.foo.bar, you might be doing something like this:

require 'lib.foo.bar'

Then you can get the path to the file as the first element (and only) ... variable, when you are outside all functions. In other words:

-- lib/foo/bar.lua
local pathOfThisFile = ... -- pathOfThisFile is now 'lib.foo.bar'

Now, to get the "folder" you need to remove the filename. Simplest way is using match:

local folderOfThisFile = (...):match("(.-)[^%.]+$") -- returns 'lib.foo.'

And there you have it. Now you can prepend that string to other file names and use that to require:

require(folderOfThisFile .. 'baz')     -- require('lib.foo.baz')
require(folderOfThisFile .. 'bazinga') -- require('lib.foo.bazinga')

If you move bar.lua around, folderOfThisFile will get automatically updated.

kikito
  • 51,734
  • 32
  • 149
  • 189
  • Thanks, this worked for loading files. But when I access module's public properties I still need to specify the full path (e.g. `lib.foo.bar.some_value`). – RocketR Feb 05 '12 at 11:05
  • 4
    That has nothing to do with loading files; it simply reflects how you have chosen to structure your Lua. You can (for example) return a table on each require, and store it in a local var : `local baz = require(folderOfThisFile .. 'baz')` and then do `baz.some_value` – kikito Feb 05 '12 at 14:09
  • 2
    Cool, I didn't know about the `...` outside functions being the string that was used to require the file. – Seth Carnegie Feb 05 '12 at 17:26
  • @kikito But it forces to use the full namespace-path even between the files of the same library. If I declare `mylib.var1` then in `mylib-utils` I have to specify `some.long.path.mylib.var1` which is very awkward. I think, I'll just stick with a single directory containing everything. It's unmaintainable but works. – RocketR Feb 05 '12 at 21:55
  • @RockeR : are you sure you have read my comment correctly? It handles *precisely* that issue: it allows you to do `baz.some_value` instead of `lib.foo.bar.some_value`. – kikito Feb 06 '12 at 00:55
  • I understand. But `mylib.lua` is the main file and it requires all others, particularly `require('mylib-utils')`. To access `mylib.var1` from `mylib-utils`, I would have to `require('mylib')` and get a recursive reference. – RocketR Feb 07 '12 at 14:21
  • In your situation, I would split mylib into two parts; mylib-base.lua and mylib.lua. Set the variables in mylib-base and use them in mylib, utils and wherever you need them. – kikito Feb 08 '12 at 09:33
  • @SethCarnegie @RocketR I have just realized that you actually don't need to "encapsulate" `...` into a table. It's shorter and simpler to use `...` directly (you get rid of a table conversion and access). I've updated my answer. – kikito Feb 09 '12 at 15:58
  • Just add to this solution, people can also require modules using '/' instead of '.', so a more general approach is: `local folderOfThisFile = (...):match("(.-)[^%(.|/)]+$")"` – houqp Oct 05 '14 at 06:31
  • @houqp That behaviour might work on certain implementations or environments, but it is not universally portable. The best option for everyone is always using dots as path separators. – kikito Oct 05 '14 at 15:29
  • @kikito, you are right. But from the Lua module's perspective, it would be better to support both '.' and '/' so even "unportable" code can use it ;) – houqp Oct 09 '14 at 03:58
  • @houqp I disagree. In my opinion it is better that modules enforce standards and raise an error. It's the same scenario where you are expecting a number in a parameter, and they give you a string. You should throw an error saying "a number is expected here", not silently convert it to a number with `tonumber`. – kikito Oct 09 '14 at 07:56
27

You can do

package.path = './mylib/?.lua;' .. package.path

Or

local oldreq = require
local require = function(s) return oldreq('mylib.' .. s) end

Then

-- do all the requires
require('mylib-utils')
require('mylib-helpers')
require('mylib-other-stuff')

-- and optionally restore the old require, if you did it the second way
require = oldreq
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Strange. I tried changing `package.path` already and thought I was doing something wrong because it says `no field package.preload['mylib-utils']`, `no file './mylib/mylib-utils.lua'`. But I'm using Lua inside the Corona SDK, maybe it has some peculiarities with loading files. – RocketR Feb 04 '12 at 23:57
  • This will not work if cwd has been changed (ie. you run the script from another program/path). – klenium Sep 19 '18 at 13:36
  • This will also not work if any of the things included with the modified `require()` themselves call `require()` internally and expect it to function as normal. – Caleb Jun 06 '19 at 10:17
1

I'm using the following snippet. It should work both for files loaded with require, and for files called via the command line. Then use requireRel instead of require for those you wish to be loaded with a relative path.

local requireRel
if arg and arg[0] then
    package.path = arg[0]:match("(.-)[^\\/]+$") .. "?.lua;" .. package.path
    requireRel = require
elseif ... then
    local d = (...):match("(.-)[^%.]+$")
    function requireRel(module) return require(d .. module) end
end
jcai
  • 3,448
  • 3
  • 21
  • 36
0

Under the Conky's Lua environment I've managed to include my common.lua (in the same directory) as require(".common"). Note the leading dot . character.

Vladius
  • 4,268
  • 2
  • 20
  • 21
0

Try this:Here is my findings:

module1= require(".\\moduleName")
module2= dofile("..\\moduleName2.lua")
module3 =loadfile("..\\module3.lua")

To load from current directory. Append a double backslash with a prefix of a fullstop.

To specify a directory above use a prefix of two fullstops and repeat this pattern for any such directory.e.g

module4=require("..\\..\\module4")
prometheus
  • 96
  • 1
  • 6