How terrible is my configuration class?

I’ve written a configuration class to load very simple key/value files, in the format “key = value”. I’m not the best at Lua, so I was wondering if anyone could provide feedback on my code. I’d prefer constructive criticism, but do what you will. So, yeah. On a scale of 1-10, how horrifying is my code?

is.lua[LUA]IS = {}

— Throw an error caused by invalid arguments.
@param count The index of the invalid argument.
@param fname The name of the function that the invalid argument was passed to.
@param extype The expected type being passed.
@param actype The actual type passed.
function IS.arg_error(count, fname, extype, actype)
if type(count) ~= ‘number’ then
IS.arg_error(1, ‘arg_error’, ‘number’, type(count))
end

if type(fname) ~= ‘string’ then
IS.arg_error(2, ‘arg_error’, ‘string’, type(fname))
end

if type(extype) ~= ‘string’ then
IS.arg_error(3, ‘arg_error’, ‘string’, type(extype))
end

if type(actype) ~= ‘string’ then
IS.arg_error(4, ‘arg_error’, ‘string’, type(actype))
end

local msg = string.format(‘bad argument #%d to ‘%s’ (%s expected, got %s)’, count, fname, extype, actype)
error(msg)
end

— Throw an argument error if two types do not match.
@param count The index of the invalid argument.
@param fname The name of the function that the invalid argument was passed to.
@param extype The expected type being passed.
@param actype The actual type passed.
function IS.enforce_arg(count, fname, extype, actype)
if type(count) ~= ‘number’ then
IS.arg_error(1, ‘enforce_arg’, ‘number’, type(count))
end

if type(fname) ~= ‘string’ then
IS.arg_error(2, ‘enforce_arg’, ‘string’, type(fname))
end

if type(extype) ~= ‘string’ then
IS.arg_error(3, ‘enforce_arg’, ‘string’, type(extype))
end

if type(actype) ~= ‘string’ then
IS.arg_error(4, ‘enforce_arg’, ‘string’, type(actype))
end

if extype ~= actype then
IS.arg_error(count, fname, extype, actype)
end
end

— Throw an argument error if all of the values in a table do not match the type provided.
@param count The index of the invalid argument.
@param fname The name of the function that the invalid argument was passed to.
@param extype The expected type being passed.
@param tbl The table of valuess to compare.
function IS.enforce_arg_table(count, fname, extype, tbl)
if type(count) ~= ‘number’ then
IS.arg_error(1, ‘enforce_all_values’, ‘number’, type(count))
end

if type(fname) ~= ‘string’ then
IS.arg_error(2, ‘enforce_all_values’, ‘string’, type(fname))
end

if type(extype) ~= ‘string’ then
IS.arg_error(3, ‘enforce_all_values’, ‘string’, type(extype))
end

if type(tbl) ~= ‘table’ then
IS.arg_error(4, ‘enforce_all_values’, ‘table’, type(tbl))
end

for _, v in pairs(tbl) do
if not IS.is(v, extype) then
IS.arg_error(count, fname, extype, type(v))
end
end
end

— Check whether obj is of type typename.
@param obj The object to check.
@param typename The type(s) to check against.
@return Whether obj is of type typename.
function IS.is(obj, typename)
if type(typename) ~= ‘string’ and type(typename) ~= ‘table’ then
IS.arg_error(2, ‘is’, ‘string or table’, type(typename))
end

if type(typename) == ‘table’ then
for _, v in pairs(typename) do
if type(v) ~= ‘string’ then
IS.arg_error(2, ‘is’, ‘string’, type(v))
end

  if type(obj) == v then return true end
end

else
if type(obj) == typename then return true end
end

return false
end

— Check whether all values are of type typename.
@param tbl The table to check.
@param typename The type(s) to check against.
@return Whether all of tbl’s values are of type typename.
function IS.is_all_values(tbl, typename)
if type(tbl) ~= ‘table’ then
IS.arg_error(1, ‘is_all_values’, ‘table’, type(tbl))
end

if type(typename) ~= ‘string’ and type(typename) ~= ‘table’ then
IS.arg_error(2, ‘is_all_values’, ‘string or table’, type(typename))
end

for _, v in pairs(tbl) do
if not IS.is(v, typename) then return false end
end

return true
end
[/LUA]

cfg.lua[LUA]include(‘is.lua’)

CFG = {}

CFG.Instance = false
CFG.Filename = ‘’
CFG.Data = {}

— Calculate the length of a table, even with named keys.
@param tbl The table to calculate the length of.
@return The length of the table.
local function Length(tbl)
IS.enforce_arg(1, ‘Length’, ‘table’, type(tbl))

local length = 0

for _, __ in pairs(tbl) do
length = length + 1
end

return length
end

— Create a configuration with the provided filename.
@param filename The name of the configuration file to create.
@return The newly created configuration object
function CFG:Create(filename)
IS.enforce_arg(1, ‘Create’, ‘string’, type(filename))

local cfg = table.Copy(CFG)
cfg.Instance = true
cfg.Filename = filename

return cfg
end

— Add default values to the configuration.
@param defaults A table of defaults to load.
function CFG:AddDefaults(defaults)
IS.enforce_arg(1, ‘AddDefaults’, ‘table’, type(defaults))
IS.enforce_arg_table(1, ‘AddDefaults’, ‘string’, defaults)

if not self.Instance then
error(‘not an instance’)
end

for k, v in pairs(defaults) do
self.Data[k] = v
end
end

— Get the value associated to the provided key.
@param key The key to find the value for.
@return The value for the key provided.
function CFG:Get(key)
IS.enforce_arg(1, ‘Get’, ‘string’, type(key))

if not self.Instance then
error(‘not an instance’)
end

return self.Data[key]
end

— Set a value with the provided key.
@param key The key for the key/value pair.
@param value The value for the key/value pair.
function CFG:Set(key, value)
IS.enforce_arg(1, ‘Set’, ‘string’, type(key))

if not self.Instance then
error(‘not an instance’)
end

self.Data[key] = value
end

— Parse a configuration entry into two pieces.
@param line The line to parse.
@return The key for the key/value pair.
@return The value for the key/value pair.
function CFG:ParseLine(line)
IS.enforce_arg(1, ‘ParseLine’, ‘string’, type(line))

if not self.Instance then
error(‘not an instance’)
end

if #line == 0 then return end

local pieces = string.Explode(’=[ ]’, line, true)

if #pieces ~= 2 then return end

return pieces[1], pieces[2]
end

— Parse multiple configuration entries into a table.
@param lines The lines to parse.
@return A table containing all provided key/value pairs.
function CFG:Parse(lines)
IS.enforce_arg(1, ‘Parse’, ‘table’, type(lines))
IS.enforce_arg_table(1, ‘Parse’, ‘string’, lines)

if not self.Instance then
error(‘not an instance’)
end

if #lines == 0 then return end

local out = {}

for _, line in pairs(lines) do
if #line > 0 then
local k, v = self:ParseLine(line)

  if not IS.is(k, 'nil') and not IS.is(v, 'nil') then
    out[k] = v
  end
end

end

return out
end

— Load the configuration from file.
– This will overwrite any loaded data.
function CFG:Load()
if not self.Instance then
error(‘not an instance’)
end

if not file.Exists(self.Filename, ‘DATA’) then
file.Write(self.Filename, ‘’)
end

local raw = file.Read(self.Filename, ‘DATA’)
if #raw == 0 then return end

local lines = string.Explode(’
', raw)
local data = self:Parse(lines)

for k, v in pairs(data) do
self.Data[k] = v
end
end

— Save the configuration to file.
– This will overwrite any changes made to the file.
function CFG:Save()
if not self.Instance then
error(‘not an instance’)
end

local lines = {}

for k, v in pairs(self.Data) do
local line = string.format(’%s = %s’, k, v)

table.insert(lines, line)

end

local data = table.concat(lines, ’
')

file.Write(self.Filename, data)
end[/LUA]

Example Usage[LUA]local my_config = CFG:Create(“my_config.txt”)
my_config:Load()
my_config:AddDefaults({ cake = ‘GG’ })
my_config:Set(‘Hello’, ‘World’)
print(my_config:Get(‘cake’))
my_config:Save()[/LUA]

[editline]25th August 2014[/editline]

Does this place have spoiler tags? Those are some serious walls of code.

[editline]25th August 2014[/editline]

Also why are only 2 of my 3 Lua blocks highlighted?

[editline]25th August 2014[/editline]

Looks like I broke the forum’s syntax highlighter… Here’s the code on GitHub.

One of the things that I’d suggest is for you to use enumeration instead of text for each option. In fact, if you look at the networking code it gmod, there are enumerations for data-types already set up.

After taking a look at the net library, I agree that is a very good way of doing things. Will most likely implement this.