Lua - Use Cases for Proxy Tables
Proxy table with metatables is a very powerful combination to perform complex scenarios. In this chapter, we're covering most important ones.
Example - Create Read-Only table
We can create a proxy to read data from a table by implementing __index, while preventing any modification to the underlying table by use of __newindex making it effectively readonly.
main.lua
-- underlying real data
local actual_data = { name = "Julie", age = 30 }
-- an empty proxy table
local proxyTable = {}
-- metatable to prevent write access to actual_data table via proxy table.
local metatableReadOnly = {
__index = actual_data,
__newindex = function(table, key, value)
error("Attempt to modify a read-only table", 2)
end
}
-- set metatable for the proxy table
setmetatable(proxyTable, metatableReadOnly)
-- read name using proxy table, prints Julie
print(proxyTable.name)
-- try to modify age, throws error
proxyTable.age = 31
Output
When we run the above program, we will get the following output−
Julie lua: main.lua:21: Attempt to modify a read-only table stack traceback: [C]: in function 'error' main.lua:11: in metamethod 'newindex' main.lua:21: in main chunk [C]: in ?
Example - Lazy Loading
We often need to load data on first access to improve performance. For example, loading data from database or from network only when accessed instead of loading initially. We can use __index metamethod to handle such kind of data handling in Lua in a lazy loading way.
main.lua
-- data to cache
local cachedData = {}
-- proxy table
local proxyLazyLoad = {}
-- metatable for proxy table
local metatableLazyLoad = {
__index = function(table, key)
if not cachedData[key] then
print("Loading data for key for first time:", key)
-- once data loaded, set in cache
cachedData[key] = "Data loaded for " .. key
end
return cachedData[key]
end
}
-- set the metatable
setmetatable(proxyLazyLoad, metatableLazyLoad)
-- access item1 for the first time
-- prints Loading data for key for first time: item1
print(proxyLazyLoad.item1)
-- access item1 again
-- prints Data loaded for item1
print(proxyLazyLoad.item1)
-- access item1 for the first time
-- prints Loading data for key for first time: item2
print(proxyLazyLoad.item2)
Output
When we run the above program, we will get the following output−
Loading data for key for first time: item1 Data loaded for item1 Data loaded for item1 Loading data for key for first time: item2 Data loaded for item2
Example - Interception and Validation
Using proxy tables, we can intercepts writing attempt to log events, to validate data to be entered as shown in example below −
main.lua
-- underlying table
local actualSettings = {}
-- proxy table
local validatedSettings = {}
-- metatable for validation
local metatableValidation = {
__index = actualSettings,
__newindex = function(table, key, value)
-- if value is a number
if type(value) == "number" and value >= 0 and value <= 1 then
actualSettings[key] = value
else
error("Invalid value for " .. key , 2)
end
end
}
-- set the metatable to proxy
setmetatable(validatedSettings, metatableValidation)
-- set a valid value via proxy
validatedSettings.volume = 0.5
-- get volume from real table, prints 0.5
print(actualSettings.volume)
-- setting invalid value will cause error
validatedSettings.brightness = "high"
Output
When we run the above program, we will get the following output−
0.5 lua: main.lua:27: Invalid value for brightness stack traceback: [C]: in function 'error' main.lua:13: in metamethod 'newindex' main.lua:27: in main chunk [C]: in ?