diff options
Diffstat (limited to 'acf/model')
-rw-r--r-- | acf/model/field.lua | 40 | ||||
-rw-r--r-- | acf/model/init.lua | 1 | ||||
-rw-r--r-- | acf/model/model.lua | 123 | ||||
-rw-r--r-- | acf/model/node.lua | 19 | ||||
-rw-r--r-- | acf/model/root.lua | 2 |
5 files changed, 135 insertions, 50 deletions
diff --git a/acf/model/field.lua b/acf/model/field.lua index 436ce2c..26d4621 100644 --- a/acf/model/field.lua +++ b/acf/model/field.lua @@ -29,12 +29,27 @@ local function auto_ui_name(name) end -Field = class() +Member = class() -function Field:init(params) +function Member:init(params) for k, v in pairs(params or {}) do if self[k] == nil then self[k] = v end end +end + +function Member:meta(context) + return { + name=self.name, + description=self.description, + ['ui-name']=self['ui-name'] or auto_ui_name(self.name) + } +end + + +Field = class(Member) + +function Field:init(params) + super(self, Field):init(params) if self.choice and not self['ui-choice'] then self['ui-choice'] = map(auto_ui_name, self.choice) @@ -47,17 +62,16 @@ end function Field:meta(context) assert(self.dtype) - return { - name=self.name, - type=self.dtype, - required=self.required, - default=self.default, - choice=self.choice, - description=self.description, - ['ui-name']=self['ui-name'] or auto_ui_name(self.name), - widget=self.widget, - ['ui-choice']=self['ui-choice'] - } + local res = super(self, Field):meta(context) + + res.type = self.dtype + res.required = self.required + res.default = self.default + res.choice = self.choice + res.widget = self.widget + res['ui-choice'] = self['ui-choice'] + + return res end function Field:load(context) diff --git a/acf/model/init.lua b/acf/model/init.lua index bf67f98..00fc7aa 100644 --- a/acf/model/init.lua +++ b/acf/model/init.lua @@ -13,6 +13,7 @@ local fld = require('acf.model.field') local Field = fld.Field local model = require('acf.model.model') +Action = model.Action new = model.new local to_field = model.to_field 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 diff --git a/acf/model/node.lua b/acf/model/node.lua index 748b20d..7852c9e 100644 --- a/acf/model/node.lua +++ b/acf/model/node.lua @@ -14,9 +14,9 @@ local pth = require('acf.path') local update = require('acf.util').update -BoundField = class() +BoundMember = class() -function BoundField:init(parent, field) +function BoundMember:init(parent, field) local pmt = getmetatable(parent) local mt = {} @@ -80,8 +80,8 @@ function TreeNode:search(path, create) local mt = getmetatable(self) local name = path[1] - if not mt.has_field(name) then - raise(mt.path, 'Field does not exist: '..name) + if not mt.valid_member(name) then + raise(mt.path, 'Member does not exist: '..name) end local next = mt.get(name, create) @@ -106,12 +106,13 @@ function Collection:init(context, params) self.init = nil self.search = nil + local field = BoundMember(self, params.field) + local mt = getmetatable(self) - mt.field = BoundField(self, params.field) - mt.meta = {type='collection', members=mt.field:meta('$')} + mt.meta = {type='collection', members=field:meta('$')} - function mt.has_field(name) return true end + function mt.valid_member(name) return true end function mt.mmeta(name) return mt.meta.members end function mt.members() return mt.txn:get(mt.addr) or {} end @@ -121,8 +122,8 @@ function Collection:init(context, params) end end - function mt.load(k, create) return mt.field:load(k, create) end - function mt.save(k, v) mt.field:save(k, v) end + function mt.load(k, create) return field:load(k, create) end + function mt.save(k, v) field:save(k, v) end end diff --git a/acf/model/root.lua b/acf/model/root.lua index 8571527..b2bebb1 100644 --- a/acf/model/root.lua +++ b/acf/model/root.lua @@ -14,7 +14,7 @@ local pth = require('acf.path') RootModel = model.new() function RootModel:init(txn) - object.super(self, RootModel):init{txn=txn, path='/', addr='/volatile'} + object.super(self, RootModel):init{txn=txn, path='/', addr='/null/root'} end function RootModel:has_permission(user, permission) |