diff options
Diffstat (limited to 'acf/model/model.lua')
-rw-r--r-- | acf/model/model.lua | 123 |
1 files changed, 96 insertions, 27 deletions
diff --git a/acf/model/model.lua b/acf/model/model.lua index e94d3fb..9808157 100644 --- a/acf/model/model.lua +++ b/acf/model/model.lua @@ -9,22 +9,60 @@ local raise = require('acf.error').raise local fld = require('acf.model.field') local Field = fld.Field +local Member = fld.Member local node = require('acf.model.node') +local BoundMember = node.BoundMember local object = require('acf.object') local class = object.class local super = object.super local isinstance = object.isinstance +local pth = require('acf.path') local util = require('acf.util') -function to_field(obj, addr) +local function to_member(obj, addr) if object.issubclass(obj, Model) then return fld.Model{model=obj, addr=addr} end - return getmetatable(obj).class and obj or obj{addr=addr} + local res = getmetatable(obj).class and obj or obj{addr=addr} + assert(isinstance(res, Member)) + return res +end + +function to_field(obj, addr) + local res = to_member(obj, addr) + assert(isinstance(res, Field)) + return res +end + + +Action = class(Member) + +function Action:init(params) + super(self, Action):init(params) + + if not self.func then error('Function not defined for action') end + + if self.field then + assert(type(self.field) == 'table') + self.field = to_field(self.field) + self.field.addr = '/null/action' + end + + getmetatable(self).__newindex = function(t, k, v) + assert(k == 'name') + rawset(t, k, v) + if t.field then t.field.name = v end + end +end + +function Action:meta(context) + local res = super(self, Action):meta(context) + if self.field then res.arg = self.field:meta(context) end + return res end @@ -32,7 +70,7 @@ 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) + res.members = base == node.TreeNode and {} or util.copy(base.members) local mt = util.copy(getmetatable(res)) @@ -42,13 +80,13 @@ function new(base) assert(v) local override = t[k] - if type(v) == 'table' then v = to_field(v) end + if type(v) == 'table' then v = to_member(v) end rawset(t, k, v) - if isinstance(v, Field) then + if isinstance(v, Member) then v.name = k - if not override then table.insert(t._fields, k) end + if not override then table.insert(t.members, k) end end end @@ -66,34 +104,65 @@ function Model:init(context) local mt = getmetatable(self) - function mt.field(name) - local res = mt.class[name] - return isinstance(res, Field) and node.BoundField(self, res) or nil + local function member(name, strict, tpe) + local m = mt.class[name] + + if not tpe then tpe = Member end + if not isinstance(m, tpe) then m = nil end + + if m == nil then + if strict then raise(mt.path, 'Does not exist: '..name) end + return + end + + return BoundMember(self, m) end - function mt.has_field(name) return mt.field(name) end + function mt.valid_member(name) return member(name) end - function mt.mmeta(name) return mt.field(name):meta() end + function mt.mmeta(name) return member(name, true):meta() end function mt.load(k, create) - local f = mt.field(k) - if f then - if f.compute then return f:compute() end - return f:load(create) + local v = mt.class[k] + + if isinstance(v, Field) then + v = BoundMember(self, v) + if v.compute then return v:compute() end + return v:load(create) + end + + if isinstance(v, Action) then + local f = v.field and BoundMember(self, v.field) + if create then return f and f:load(true) end + + return function(var) + if f then f:save(var) + elseif var ~= nil then + raise( + pth.join(mt.path, v.name), + 'Action does not accept an input argument' + ) + end + local res = v.func(self, f and f:load()) + if f then mt.txn:set(v.field.addr) end + return res + end end - return mt.class[k] - end - function mt.save(k, v) - local f = mt.field(k) - if not f then raise(mt.path, 'Field named '..k..' does not exist') end - f:save(v) + return v end - mt.meta = { - type='model', - fields=util.map(function(f) return mt.mmeta(f) end, self._fields) - } + function mt.save(k, v) return member(k, true, Field):save(v) end + + local function tmeta(tpe) + local res = {} + for _, name in ipairs(self.members) do + local m = member(name, false, tpe) + if m then table.insert(res, m:meta()) end + end + return res + end + mt.meta = {type='model', fields=tmeta(Field), actions=tmeta(Action)} function mt.members() return util.map(function(f) return f.name end, mt.meta.fields) @@ -101,8 +170,8 @@ function Model:init(context) function mt.validate() for _, name in ipairs(mt.members()) do - local field = mt.field(name) - if not field.compute then field:validate_saved() end + local f = member(name, false, Field) + if not f.compute then f:validate_saved() end end end |