#MonthOfCode - Day 23: meta

My entry for the 23rd day of the month of code. The theme for today is: meta.

Lua’s objects are called tables. It also has metatables: tables that change the behaviour of tables.

Code after the break.

You can do a lot of things with metatables but today, I’ll only talk about property access. We’ll implement the composite design pattern.

Here’s how we want it to work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
local circle = {
draw = function(color) print(color..' circle') end
}
local square = {
draw = function(color) print(color..' square') end
}
local triangle = {
draw = function(color) print(color..' triangle') end
}

-- make a composite object
local groupOfShapes = Composite(circle, square, triangle)

-- call the draw method of all objects as if there was only one
groupOfShapes.draw('red')

-- and we can make composite of composites
local ellipse = {
draw = function(color) print(color..' ellipse') end
}

local not_drawable = {} -- will be ignored silently

local biggerGroup = Composite(ellipse, not_drawable, groupOfShapes) -- nested composite

-- still use at as if there was only one object
biggerGroup.draw('green')

Now, the implementation. We’ll use a metatable that intercepts access to methods and calls the method of all the contained objects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
local meta = {
__index = function(table, key)
-- if we are here, it means that the composite object doesn't have the method
-- let's create it and store it
table[key] = function (...)
-- execute the method on all items
for i, item in ipairs(table.items) do
if type(item[key]) == 'function' then
item[key](...)
end
end
end
return table[key]
end
}

function Composite(...)
local composite = {}
composite.items = {...} -- store the items to compose
setmetatable(composite, meta)
return composite
end

Output:

1
2
3
4
5
6
7
red circle
red square
red triangle
green ellipse
green circle
green square
green triangle