[library] newmodule.lua - Make Lua module and init.lua ...

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
User avatar
TsT
Party member
Posts: 161
Joined: Thu Sep 25, 2008 7:04 pm
Location: France
Contact:

[library] newmodule.lua - Make Lua module and init.lua ...

Post by TsT »

[library] newmodule.lua - Make Lua module and init.lua becomes easy

If you play with Lua module, You probably know that becomes complicate with lot of modules, directories, path, etc.

Now I use newmodule everywhere.


What is the problem ?

The Lua 5.0 introduce the module() function.
This way to define module was critiqued.
See http://lua-users.org/wiki/LuaModuleFunctionCritiqued.

A better and simple way to define module (without the module() function !) was proposed.
See : http://lua-users.org/wiki/ModulesTutorial.

In Lua 5.2 the module() function was removed.
See http://www.lua.org/manual/5.2/manual.html#8.2


More about Lua modules ?

You should read the following article :
kikito wrote:I wrote about modules, packages and multiple files here:
http://kiki.to/blog/2014/04/12/rule-5-b ... ple-files/
You should also read my next post.


Download

On github : https://github.com/tst2005/lua-newmodule.git


Installation

You can copy it on the main directory.
Or change the package path.

I choose to create my own framework to keep my newmodule in subdirectories (and lot of other stuff)
The framework will be release soon at https://github.com/tst2005/dragoon-framework.git )


Documentation

require("newmodule")

Load the newmodule.
Return: a table

Sample :

Code: Select all

local newmodule = require("newmodule")

require("newmodule")(...)

Use the newmodule to create a new table.
Detect the name of the module (_M._NAME usefull for debug message)
Detect the module name, usefull to require sub module
Detect the module parent name, usefull to require module in the same directory
The table returns follow the format :

Code: Select all

local _M = require("newmodule")(...)
equal to :

Code: Select all

local _M = {
  _NAME = "lua-foo.foo",
  _PATH = "lua-foo.foo",
  _PPATH = "lua-foo",
}
In this case _NAME will always equal to _PATH.

Load a sub module :

Code: Select all

local _M = require("newmodule")(...)
local child = require(_M.PATH .. ".childmodule")
or
local _M, child_require = require("newmodule")(...)
local child = child_require("childmodule")
Load another module in the same directory :

Code: Select all

local _M = require("newmodule")(...)
local bro = require(_M.PPATH .. ".brothermodule")
or
local _M, child_require, brother_require = require("newmodule")(...)
local bro = brother_require("brothermodule")

require("newmodule"):from(a_table, ...)

Returns : the modified table passed as argument a_table
Like the require("newmodule")(...) except you are able a provide the table.
require("newmodule")(...) equals require("newmodule"):from({}, ...)

Code: Select all

local _M = { _VERSION = "0.1" }
require("newmodule"):from(_M, ...)
return _M

require("newmodule"):initload("modulename", ...)

A special stuff for the init.lua file that is only use to redirect the loader to the real module.

Code: Select all

return require("newmodule"):initload("realmodule", ...)
Details about the module fields

The _NAME field will be kept if already exists.
The _PATH, _PPATH and _CALLEDWITH field will be overwritten if exists.
If not exists all fields will be created.

_NAME

In some cases it's interesting to include in error message the name of the module.
I think the _NAME should contains module name readable by human text, like "bar, the sub-module of foo" instead of "foo.bar"

Other implementation of module seems use this field.
I can not use it to manage module path for automatic loading...

_PATH

It's the module path (without ".init" suffix if present)

_PPATH

It's the _PATH without one level.
if _PATH = lib.foo.foo then _PPATH will be lib.foo.

_CALLEDWITH

_CALLEDWITH is most for debug purpose,
It's a copy of the 1st argument passed (like the _PATH but the ".init" suffix is never removed)

Localised Loaders

What is the difference between child and brother module

Code: Select all

parent/
     mod.lua
     bro.lua
     mod/child.lua
In mod.lua the bro.lua is in the same parent directory, it's a brother module, should be loaded with brother_require("bro")
In mod.lua the child.lua is in a subdirectory with the name of mod.lua (mod.child) it's a child of mod and should be loaded with child_require("child"). It equals to brother_require("mod.child").

Where is the child_require and brother_require ?

mod.lua

Code: Select all

local _M, child_require, brother_require = require("newmodule")(...)
local bro = brother_require("bro")
local child = child_require("child")
child_require(childname)

The child_require will internally use the _M._PATH.

mod.lua

Code: Select all

local _M, child_require = require("newmodule")(...)
local child = child_require("child")
brother_require(brothername)

The child_require will internally use the _M._PPATH.

Code: Select all

local _M, child_require, brother_require = require("newmodule")(...)
local bro = brother_require("bro")

Sample of use
  • test.foo.sh
  • foo/
    • init.lua
    • foo.lua
    • x.lua
init.lua :

Code: Select all

return require("newmodule"):initload("foo", ...)

foo.lua :

Code: Select all

local _M, creq, breq = require("newmodule")(...)
print("foo: _NAME=", _M._NAME)
print("foo: _PATH=", _M._PATH)
print("foo: _PPATH=", _M._PPATH)

local x = (breq "x") -- require("foo.x")
return _M
x.lua :

Code: Select all

local _M = {
	__OK = "ok",
}
require'newmodule':from(_M, ...)
print("x ok?", _M.__OK)
return _M
test with :

Code: Select all

lua -l newmodule -e 'require"foo"'
lua -l newmodule -e 'require"foo.init"'
lua -l newmodule -e 'require"foo.foo"'
cd foo ; lua -l newmodule -e 'require"foo"'
Last edited by TsT on Mon Jan 12, 2015 9:10 am, edited 14 times in total.
My projects current projects : dragoon-framework (includes lua-newmodule, lua-provide, lovemodular, , classcommons2, and more ...)
User avatar
TsT
Party member
Posts: 161
Joined: Thu Sep 25, 2008 7:04 pm
Location: France
Contact:

Re: [library] newmodule.lua

Post by TsT »

You wrote a simple module named foo.lua :

Code: Select all

return {
  hello = function() return "Hello I'm foo" end,
}
You have one file :
  • foo.lua
You decide to put advanced stuff in another module :
bar.lua :

Code: Select all

return {
  bye = function() return "Good bye" end,
}
foo.lua modified to be able to load bar.lua :

Code: Select all

return {
  hello = function() return "Hello I'm foo" end,
  bye = function() return require("bar").bye() end,
}
You have two file :
  • foo.lua
  • bar.lua
A simple test.lua :

Code: Select all

local foo = require("foo")
print(foo.hello())
print(foo.bye())

Code: Select all

$ lua test.lua
Hello I'm foo
Good bye
$ 
You are using a git repository, you decide to put it inside a new repository named "lua-foo" :
  • lua-foo/
    • foo.lua
    • bar.lua
    • test.lua
You decided to make a new game.
  • game/
    • main.lua
You remember your work and want use you awesome foo.lua for you game.
  • game/
    • main.lua
    • lua-foo/ <-- here the git clone of lua-foo
      • foo.lua
      • bar.lua
      • test.lua

main.lua :

Code: Select all

function love.load()
  foo = require("lua-foo.foo")
end

function love.draw()
  love.graphics.print(foo.hello())
end
You run the game with love, it print "Hello I'm foo".

Now you decide to use foo.bye()

main.lua :

Code: Select all

function love.load()
  foo = require("lua-foo.foo")
end

function love.draw()
  love.graphics.print(foo.bye())
end
But the game fail with error like :

Code: Select all

Error: lua-foo/foo.lua:3: module 'bar' not found:
	no field package.preload['bar']
	no file 'bar.lua' in LOVE game directories.
	no file 'bar/init.lua' in LOVE game directories.
	no file 'bar.so' in LOVE paths.
	no file './bar.lua'
[...]

You have lot of solutions :

1) modify the foo.lua

from

Code: Select all

  bye = function() return require("bar").bye() end,
to

Code: Select all

  bye = function() return require("lua-foo.bar").bye() end,
It's a bad solution, you don't want modify you lua-foo for all you games.

2) you can do symlink of bar.lua on the game directory
  • game/
    • main.lua
    • bar.lua -> lua-foo/bar.lua
    • lua-foo/
      • foo.lua
      • bar.lua
      • test.lua
symlink is supported on Linux file system, but not on Windows or inside a .love (zip).
It's not a long term solution.

3) you can change the package.path to search inside lua-foo/

Code: Select all

package.path = package.path .. ";lua-foo/?.lua"
Not ideal solution, Do you really add one line for each library ?!

4) Using one directory for all libraries (or third-party) parts

- You can create a "lib" directory
- put lua-foo inside lib
- rename lua-foo to foo
- change the package.path for a ./lib/?/?.lua

Code: Select all

package.path = package.path .. ";./?/?.lua"
  • game/
    • main.lua
    • lib/
      • foo/
        • foo.lua
        • bar.lua
        • test.lua
require("foo") will search in ./lib/foo/foo.lua and success
but require("bar") will search in ./lib/bar/bar.lua and success


5) You discove that when require() is use, the module receive the name of the module as first argument.

foo.lua :

Code: Select all

print(...)
return {
  hello = function() return "Hello I'm foo" end,
  bye = function() return require("bar").bye() end,
}
You can get the module name and use it without the suffix "foo" to load the "bar" with the same call.
  • game/
    • main.lua
    • lib/
      • lua-foo/
        • foo.lua
        • bar.lua
        • test.lua

Code: Select all

$ cd game/
$ lua -e 'require("lib.lua-foo.foo")'
lib.lua-foo.foo
$ cd lib/
$ lua -e 'require("lua-foo.foo")'
lua-foo.foo
$ cd lib/
$ lua -e 'require("foo")'
foo
foo.lua :

Code: Select all

local path = (...):gsub("foo$", "")
print("receive", ...)
print("bar => ".. path .. "bar")
return {
  hello = function() return "Hello I'm foo" end,
  bye = function() return require(path .. "bar").bye() end,
}

Code: Select all

$ cd game/
$ lua -e 'require("lib.lua-foo.foo")'
receive	lib.lua-foo.foo
bar => lib.lua-foo.bar
$ cd lib/
$ lua -e 'require("lua-foo.foo")'
receive	lua-foo.foo
bar => lua-foo.bar
$ cd lua-foo
$ lua -e 'require("foo")'
receive	foo
bar => bar

6) ... using init.lua

7) ...

8) ...

9) ...

10) juste before newmodule solution

I don't know exactly how many solution it exsits but before creating this module I use this kind of code :

init.lua :

Code: Select all

local path = (... or ""):gsub("%.init$")
path = path ~= "" and path .. "." or path
return require(path .. "blah")
or

Code: Select all

local path = (... or ""):gsub("%.init$")
path = path ~= "" and path .. "." or path
require(path .. "bar")
return require(path .. "blah")
blah.lua

Code: Select all

local path = (... or ""):gsub("%.[^%.]+$")
path = path ~= "" and path .. "." or path
-- some module code
...
-- some sub-module loading
... = require( path .. "mod1")
... = require( path .. "mod2")
...
-- some module code
...
It works well but it becomes ugly for my eyes!
all this stuff only to load module ?
Last edited by TsT on Sun Jan 11, 2015 2:06 pm, edited 2 times in total.
My projects current projects : dragoon-framework (includes lua-newmodule, lua-provide, lovemodular, , classcommons2, and more ...)
User avatar
TsT
Party member
Posts: 161
Joined: Thu Sep 25, 2008 7:04 pm
Location: France
Contact:

Re: [library] newmodule.lua

Post by TsT »

Ignore this reply, content moved on the 1st post.
Last edited by TsT on Sun Jan 11, 2015 2:07 pm, edited 1 time in total.
My projects current projects : dragoon-framework (includes lua-newmodule, lua-provide, lovemodular, , classcommons2, and more ...)
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: [library] newmodule.lua

Post by kikito »

I wrote about modules, packages and multiple files here:

http://kiki.to/blog/2014/04/12/rule-5-b ... ple-files/
When I write def I mean function.
User avatar
TsT
Party member
Posts: 161
Joined: Thu Sep 25, 2008 7:04 pm
Location: France
Contact:

Re: [library] newmodule.lua

Post by TsT »

thanks kikito !
My projects current projects : dragoon-framework (includes lua-newmodule, lua-provide, lovemodular, , classcommons2, and more ...)
Post Reply

Who is online

Users browsing this forum: No registered users and 15 guests