summaryrefslogtreecommitdiffstats
path: root/acf/model/model.lua
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2012-12-16 19:10:38 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2012-12-16 19:10:38 +0200
commite4361842fcdec369fbd4466f2528b2815f504ff9 (patch)
tree4694ca56f9e9a4869763d8fe1f31a81f6f62ac0b /acf/model/model.lua
downloadacf2-e4361842fcdec369fbd4466f2528b2815f504ff9.tar.bz2
acf2-e4361842fcdec369fbd4466f2528b2815f504ff9.tar.xz
initial version
Diffstat (limited to 'acf/model/model.lua')
-rw-r--r--acf/model/model.lua102
1 files changed, 102 insertions, 0 deletions
diff --git a/acf/model/model.lua b/acf/model/model.lua
new file mode 100644
index 0000000..2efcb6f
--- /dev/null
+++ b/acf/model/model.lua
@@ -0,0 +1,102 @@
+--[[
+Copyright (c) 2012 Kaarle Ritvanen
+See LICENSE file for license details
+--]]
+
+module(..., package.seeall)
+
+local fld = require('acf.model.field')
+local Field = fld.Field
+
+local node = require('acf.model.node')
+
+local object = require('acf.object')
+local class = object.class
+local super = object.super
+local isinstance = object.isinstance
+
+local util = require('acf.util')
+
+
+function to_field(obj)
+ if object.issubclass(obj, Model) then return fld.Model{model=obj} end
+ return getmetatable(obj).class and obj or obj()
+end
+
+
+function new(base)
+ if not base then base = Model end
+
+ local res = class(base)
+ res._fields = base == node.TreeNode and {} or util.copy(base._fields)
+
+ local mt = util.copy(getmetatable(res))
+
+ function mt.__index(t, k) return base[k] end
+
+ function mt.__newindex(t, k, v)
+ assert(v)
+
+ local override = t[k]
+ if type(v) == 'table' then v = to_field(v) end
+
+ rawset(t, k, v)
+
+ if isinstance(v, Field) then
+ v.name = k
+ if not override then table.insert(t._fields, k) end
+ end
+ end
+
+ setmetatable(res, mt)
+
+ if isinstance(base, Model) then util.setdefaults(res, base) end
+
+ return res
+end
+
+Model = new(node.TreeNode)
+
+function Model:init(txn, path, addr)
+ super(self, Model):init(txn, path, addr)
+
+ local mt = getmetatable(self)
+
+ function mt.field(name)
+ local res = mt.class[name]
+ return isinstance(res, Field) and node.BoundField(self, res) or nil
+ end
+
+ function mt.mmeta(name) return mt.field(name):meta() end
+
+ mt.meta = {type='model',
+ fields=util.map(function(f) return mt.mmeta(f) end,
+ self._fields)}
+
+ function mt.members()
+ return util.map(function(f) return f.name end, mt.meta.fields)
+ end
+
+ function mt.__index(t, k)
+ local f = mt.field(k)
+ if f then
+ if f.compute then return f:compute() end
+ return f:load()
+ end
+ return mt.class[k]
+ end
+
+ function mt.__newindex(t, k, v)
+ local f = mt.field(k)
+ if not f then error('Field named '..k..' does not exist') end
+ f:save(v)
+ txn.validate[mt.path] = function() self:validate() end
+ end
+end
+
+function Model:validate()
+ local mt = getmetatable(self)
+ for _, name in ipairs(mt.members()) do
+ mt.field(name):validate_saved()
+ end
+end