lua-users home
lua-l archive

Re: local "module" pattern without module [was Re: [ANN] SLAXML - pure Lua, robust-ish, SAX-like streaming XML processor]

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Put more simply: how would you rewrite the following suite of files so that the user can "require 'master'" and not spam the global namespace with "MASTER", but still have all the assertions pass?

### _test_usage_.lua
require 'master'
assert(MASTER.Simple)
assert(MASTER.simple)
assert(MASTER.Shared)
assert(MASTER.Shared.go)
assert(MASTER.Simple.ref1()==MASTER.Multi1)
assert(pcall(MASTER.Simple.ref2))
### master.lua
MASTER = {}
require 'simple'
require 'multi'
require 'shared1'
require 'shared2'
require 'reference'
### simple.lua
MASTER.Simple = {}
function MASTER:simple() end
### multi.lua
MASTER.Multi1 = {}
MASTER.Multi2 = {}
### shared1.lua
MASTER.Shared = {}
### shared2.lua
function MASTER.Shared:go() end
### reference.lua
function MASTER.Simple:ref1() return MASTER.Multi1 end
function MASTER.Simple:ref2() MASTER:simple() end

On Feb 18, 2013, at 10:32 AM, Gavin Kistner <phrogz@me.com> wrote:

On Feb 18, 2013, at 09:59 AM, Justin Cormack <justin@specialbusservice.com> wrote:
all requires should be assigning to a local variable.
[...]
local scxml = {}

scxml.state = require "lib/state"
scxml.event = require "lib/event"

return scxml
This works for the simplest cases I supplied where each file adds exactly one completely independent table to the master. However, I have three more cases that aren't quite as clean:
* Multiple Datatypes per File
* Multiple Files Augmenting the Same Table
* One File Referencing Tables from Another


1) Multiple Datatypes per File
One of the file declares a few (currently-global) new datatypes that are used by various methods throughout. Would you recommend:

# lxsc.lua
local LXSC = {}
...require others...
for name,t in pairs(require('lib/datatypes') do LXSC[name]=t end
return LXSC

# lib/datatypes.lua
local Queue = {}; ...
local OrderedSet = {}; ...
local List = {}; ...
return {Queue=Queue, OrderedSet=OrderedSet, List=List}

This is easily worked around by making one file per 'object', but that feels like an ugly burden. Further, while this makes these classes available on the master LXSC table, there is no way for the sub-tables to access this master. Which brings me more explicitly to problem #2:


2) Multiple Files Augmenting the Same Table
Some of the files do not add a new table, but augment another existing table. For example:

# lxsc.lua
LXSC = {}
require 'lib/scxml'
require 'lib/runtime'

# lib/scxml.lua
LXSC.SCXML = {}

# lib/runtime.lua
function LXSC.SCXML:go() ... end -- modify a table defined elsewhere

If there was only one case like this, I could 'invert' the hierarchy like so:

# lxsc.lua
local LXSC = {}
LXSC.SCXML = require 'lib/runtime'
return LXSC

# lib/runtime.lua
local SCXML = require 'lib/scxml'
function SCXML:go() ... end -- modify a table defined elsewhere
return SCXML

# lib/scxml.lua
local SCXML = {}
return SCXML

...but that technique does not work when multiple files augment the same datatype. Is there a pattern that allows multiple files to work on the same common table without polluting the global namespace?


3) One File Referencing Tables from Another
Various files and methods need to talk to other high-level objects in the system. For example:

# lib/parse.lua
function LXSC:parse(...) -- augments the master table
local name = getSomeNameString()
local object = LXSC[name]() -- dynamically picks methods from the master table
end

# lib/transition.lua
function Transition:addTarget(...)
self.targets = LXSC.List() -- accesses a datatype from the namespace
end

How would I cause the master table to be exposed to each child file that needs to access it?

Is setfenv() the proper way to go about all this? Is there something better?



On Mon, Feb 18, 2013 at 2:43 PM, Gavin Kistner <phrogz@me.com> wrote:
On Feb 17, 2013, at 10:15 PM, Miles Bader <miles@gnu.org> wrote:

> Gavin Kistner <phrogz@me.com> writes:
>> * Adds only a single `SLAXML` key to the environment; there is no spam
>> of utility functions polluting the global namespace.
>
> It should not add _any_ keys to the global environment.
>
>> ## Usage
>> require 'slaxml'
>
> local slaxml = require 'slaxml'

Thank you for the suggestion. I've updated the library to use this pattern.

This works fine for this project where I only have two files. How would others suggest enforcing the same pattern for a different project that has many files all augmenting the same table?

For example, in LXSC[1] I currently have 10+ files like so:

lxsc.lua
lib/event.lua
lib/scxml.lua
lib/state.lua
lib/...etc...

and I use this to build the common object like so:

# lxsc.lua
LXSC = { VERSION="0.3.1" }
require 'lib/state'
require 'lib/scxml'
require 'lib/event'
...etc.

# lib/event.lua
LXSC.Event = { ... }

# lib/scxml.lua
LXSC.SCXML = { ... }

# lib/state.lua
LXSC.State = { ... }

How might I modify this small multiple of files to work together in a way that doesn't spam the global namespace?

I can't do something like:

# lxsc.lua
local LXSC = { VERSION="0.3.1" }
require 'lib/state'
return LXSC

# lib/state.lua
local LXSC = require 'lxsc'
LXSC.State = { ... }

…because that causes a loop in the loader. Should I instead do this?

LXSC = { VERSION="0.3.1" } # Spam the global for now
require 'lib/state'
require 'lib/scxml'
require 'lib/event'
local LXSC = LXSC
_G.LXSC = nil # remove the global spam
return LXSC

Your experienced advice is requested :)


[1] https://github.com/Phrogz/LXSC


AltStyle によって変換されたページ (->オリジナル) /