From e4361842fcdec369fbd4466f2528b2815f504ff9 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 16 Dec 2012 19:10:38 +0200 Subject: initial version --- acf/model/model.lua | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 acf/model/model.lua (limited to 'acf/model/model.lua') 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 -- cgit v1.2.3