I am writing a couple of functions accepting tables as input parameters. These tables constitute a range of options, which should either be given or inferred from default tables.
Concrete use cases can be found in the unit tests below.
The main question: Is the code idiomatic Lua – especially the use of metatables?
Code
local defvalue = {}
--[[
Provides a means to fill a table with default options if they are not
already present.
This function is based on meta tables and their __index() function.
table: Your (input) table.
defTable: A table containing all default values.
recursive: A boolean indicating if sub-tables should also be bound to the
values found in defTable.
--]]
function defvalue.bind_table(table, defTable, recursive)
local mt = {
__index = function (table, key)
return defTable[key]
end
}
setmetatable(table, mt)
if recursive then
for key, value in pairs(table) do
if type(value) == "table" then
defvalue.bind_table(table[key], defTable[key], true)
end
end
end
end
return defvalue
Unit tests (using luaunit):
require('luaunit/luaunit')
local defvalue = require('defvalue')
TestDefaultValue = {} -- class
function TestDefaultValue:testDefaultValue()
local actualTable = {}
local defTable = {
property = "value"
}
defvalue.bind_table(actualTable, defTable, false)
assertEquals(actualTable["property"], "value")
end
function TestDefaultValue:testRecursiveValues()
local actualTable = {
-- test merging of sub-tables
secondSubTable = {
}
}
local defTable = {
subTable = {
property = "value"
};
secondSubTable = {
secondSubProperty = "secondSubValue"
}
}
defvalue.bind_table(actualTable, defTable, true)
assertEquals(actualTable["subTable"]["property"], "value")
assertEquals(actualTable["secondSubTable"]["secondSubProperty"], "secondSubValue")
end
function TestDefaultValue:testNonRecursiveValues()
local actualTable = {
subTable = {}
}
local defTable = {
subTable = {
property = "Hello World!"
}
}
defvalue.bind_table(actualTable, defTable, false)
assertEquals(actualTable["subTable"]["property"], nil)
end
function TestDefaultValue:testDynamicChange()
local actualTable = {}
local defTable = {}
defvalue.bind_table(actualTable, defTable, false)
assertEquals(actualTable["property"], nil)
defTable["property"] = 42
assertEquals(actualTable["property"], 42)
end
function TestDefaultValue:testPropertyShadowing()
local actualTable = {
property = "value";
subTable = {
subProperty = "subValue"
};
secondSubTable = {
secondSubProperty = "secondSubValue"
}
}
local defTable = {
property = "hello";
subTable = {
subProperty = "world"
};
secondSubTable = 42
}
defvalue.bind_table(actualTable, defTable, true)
assertEquals(actualTable["property"], "value")
assertEquals(actualTable["subTable"]["subProperty"], "subValue")
assertEquals(actualTable["secondSubTable"]["secondSubProperty"], "secondSubValue")
end
-- class TestDefaultValue
LuaUnit:run()
value
instead oftable[key]
indefvalue.bind_table(table[key]
– hjpotter92 Jan 7 at 0:29table
is a reserved keyword in Lua. Use some other variable name instead :) – hjpotter92 Jan 7 at 0:29table
is simply a default module/global but the same remedy is correct. =) – Etan Reisner Jan 9 at 15:37defTable[key]
is a table in thatrecursive
loop too. If it isn't the lookup for a default value later might error. – Etan Reisner Jan 9 at 15:40secondSubTable
fromactualTable
in that test and run it you get an error because you cannot index a number value. That was my point. – Etan Reisner Jan 9 at 17:51