Defensive Lua
-- Defensive programming with metatables in Lua
-- Create a function to generate a defensive object
local function createDefensiveObject(initialData)
local data = initialData or {}
local methods = {}
local readOnlyKeys = {}
-- Metatable for the object
local mt = {
__index = function(t, k)
if methods[k] then
return methods[k]
elseif data[k] ~= nil then
return data[k]
else
error("Attempt to access non-existent key: " .. tostring(k), 2)
end
end,
__newindex = function(t, k, v)
if readOnlyKeys[k] then
error("Attempt to modify read-only property: " .. tostring(k), 2)
elseif data[k] ~= nil then
if type(v) ~= type(data[k]) then
error("Type mismatch. Expected " .. type(data[k]) .. ", got " .. type(v), 2)
end
data[k] = v
else
error("Attempt to add new key: " .. tostring(k) .. ". Use addProperty() instead.", 2)
end
end,
__pairs = function()
return next, data, nil
end
}
-- The object itself
local obj = {}
-- Method to add a new property
function methods.addProperty(key, value, readOnly)
if data[key] ~= nil then
error("Property already exists: " .. tostring(key), 2)
end
data[key] = value
if readOnly then
readOnlyKeys[key] = true
end
end
-- Method to add a new method
function methods.addMethod(name, func)
if methods[name] then
error("Method already exists: " .. tostring(name), 2)
end
methods[name] = func
end
-- Method to validate method calls
function methods.validateMethodCall(name, ...)
local method = methods[name]
if not method then
error("Attempt to call non-existent method: " .. tostring(name), 2)
end
-- Add any additional validation logic here
return method(...)
end
return setmetatable(obj, mt)
end
-- Usage example
local person = createDefensiveObject({name = "Alice", age = 30})
-- Add a read-only property
person:addProperty("id", "12345", true)
-- Add a method
person:addMethod("greet", function(self)
return "Hello, I'm " .. self.name
end)
-- Accessing properties
print(person.name) -- Output: Alice
print(person:greet()) -- Output: Hello, I'm Alice
-- These will raise errors:
-- person.id = "54321" -- Attempt to modify read-only property
-- person.newProp = "value" -- Attempt to add new key
-- person.age = "31" -- Type mismatch
-- print(person.nonexistent) -- Attempt to access non-existent key
-- person:nonexistentMethod() -- Attempt to call non-existent method
-- Iterate over properties
for k, v in pairs(person) do
print(k, v)
end