--[[ 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