diff options
author | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2012-12-16 19:10:38 +0200 |
---|---|---|
committer | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2012-12-16 19:10:38 +0200 |
commit | e4361842fcdec369fbd4466f2528b2815f504ff9 (patch) | |
tree | 4694ca56f9e9a4869763d8fe1f31a81f6f62ac0b /acf/model/node.lua | |
download | acf2-e4361842fcdec369fbd4466f2528b2815f504ff9.tar.bz2 acf2-e4361842fcdec369fbd4466f2528b2815f504ff9.tar.xz |
initial version
Diffstat (limited to 'acf/model/node.lua')
-rw-r--r-- | acf/model/node.lua | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/acf/model/node.lua b/acf/model/node.lua new file mode 100644 index 0000000..39a0d92 --- /dev/null +++ b/acf/model/node.lua @@ -0,0 +1,129 @@ +--[[ +Copyright (c) 2012 Kaarle Ritvanen +See LICENSE file for license details +--]] + +module(..., package.seeall) + +local object = require('acf.object') +local class = object.class +local super = object.super + +local pth = require('acf.path') + + +BoundField = class() + +function BoundField:init(parent, field) + local pmt = getmetatable(parent) + local mt = {} + + function mt.__index(t, k) + local member = field[k] + if type(member) ~= 'function' then return member end + return function(self, ...) + local name + if field.name then name = field.name + else + name = arg[1] + table.remove(arg, 1) + end + return member(field, + pmt.txn, + pth.join(pmt.path, name), + pmt.addr and pth.join(pmt.addr, name), + unpack(arg)) + end + end + + setmetatable(self, mt) +end + + +TreeNode = class() + +function TreeNode:init(txn, path, addr) + local mt = getmetatable(self) + mt.txn = txn + mt.path = path + mt.addr = addr +end + +function TreeNode:search(path) + if #path == 0 then return self end + local next = path[1] + table.remove(path, 1) + return TreeNode.search(self[next], path) +end + + +Collection = class(TreeNode) + +function Collection:init(txn, path, addr, field, required) + super(self, Collection):init(txn, path, addr) + + if required then + txn.validate[path] = function() + if #txn:get(addr) == 0 then + error('Collection cannot be empty: '..path) + end + end + end + + self.init = nil + self.search = nil + + local mt = getmetatable(self) + + mt.field = BoundField(self, field) + mt.meta = {type='collection', members=mt.field:meta()} + function mt.mmeta(name) return mt.meta.members end + function mt.members() return txn:get(addr) or {} end + + function mt.__index(t, k) return mt.field:load(k) end + function mt.__newindex(t, k, v) mt.field:save(k, v) end +end + + +PrimitiveList = class(Collection) + +function PrimitiveList:init(txn, path, addr, field, required) + super(self, PrimitiveList):init(txn, path, addr, field, required) + + local mt = getmetatable(self) + local index = mt.__index + + function mt.__index(t, k) + if type(k) == 'number' then return index(t, k) end + + for i, j in ipairs(txn:get(addr) or {}) do + assert(i == tonumber(j)) + if mt.field:load(i) == k then return k end + end + error('Value does not exist: '..k) + end +end + + +-- experimental +Mixed = class(Collection) + +function Mixed:init(txn, path, addr, field, required) + super(self, Mixed):init(txn, path, addr, field, required) + -- TODO dynamic meta: list non-leaf children + getmetatable(self).meta = {type='mixed'} +end + + +local function meta_func(attr) + return function(node, ...) + local res = getmetatable(node)[attr] + if type(res) == 'function' then return res(unpack(arg)) end + return res + end +end + +members = meta_func('members') +meta = meta_func('meta') +mmeta = meta_func('mmeta') +path = meta_func('path') |