summaryrefslogtreecommitdiffstats
path: root/acf/model/model.lua
diff options
context:
space:
mode:
Diffstat (limited to 'acf/model/model.lua')
-rw-r--r--acf/model/model.lua123
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