diff options
-rw-r--r-- | acf/model/field.lua | 44 | ||||
-rw-r--r-- | acf/model/init.lua | 103 | ||||
-rw-r--r-- | acf/model/model.lua | 4 | ||||
-rw-r--r-- | acf/model/node.lua | 51 |
4 files changed, 106 insertions, 96 deletions
diff --git a/acf/model/field.lua b/acf/model/field.lua index e1bdeaf..7e3aa1d 100644 --- a/acf/model/field.lua +++ b/acf/model/field.lua @@ -32,7 +32,9 @@ end Field = class() function Field:init(params) - for k, v in pairs(params or {}) do if not self[k] then self[k] = v end end + for k, v in pairs(params or {}) do + if self[k] == nil then self[k] = v end + end if self.choice and not self['ui-choice'] then self['ui-choice'] = map(auto_ui_name, self.choice) @@ -43,7 +45,7 @@ function Field:init(params) end end -function Field:meta(txn, path, addr) +function Field:meta(context) assert(self.dtype) return { name=self.name, @@ -58,38 +60,40 @@ function Field:meta(txn, path, addr) } end -function Field:load(txn, path, addr) - local value = txn:get(addr) +function Field:load(context) + local value = context.txn:get(context.addr) if value == nil then return self.default end return value end -function Field:_validate(txn, path, value) +function Field:_validate(context, value) if self.required and value == nil then - raise(path, 'Required value not set') + raise(context.path, 'Required value not set') end if self.choice and value ~= nil and not contains(self.choice, value) then - raise(path, 'Invalid value') + raise(context.path, 'Invalid value') end - if value ~= nil then self:validate(txn, path, value) end + if value ~= nil then self:validate(context, value) end return value end -function Field:validate(txn, path, value) end +function Field:validate(context, value) end -function Field:save(txn, path, addr, value) +function Field:save(context, value) -- 2nd argument currenly not much used by backends - txn:set(addr, self.dtype, self:_validate(txn, path, value)) + context.txn:set(context.addr, self.dtype, self:_validate(context, value)) end -function Field:validate_saved(txn, path, addr) - self:save(txn, path, addr, self:load(txn, path, addr)) +function Field:validate_saved(context) + self:save(context, self:load(context)) end TreeNode = class(Field) -function TreeNode:save(txn, path, addr, value) +function TreeNode:save(context, value) + local path = context.path + -- TODO hack, allow preserving old instance on parent update if value == path then return end @@ -101,15 +105,15 @@ function TreeNode:save(txn, path, addr, value) return end - txn:set(addr) + context.txn:set(context.addr) if value then if type(value) ~= 'table' then raise(path, 'Cannot assign primitive value') end - txn:set(addr, 'table') - local new = self:load(txn, path, addr, true) + context.txn:set(context.addr, 'table') + local new = self:load(context, true) local errors = err.ErrorDict() for k, v in pairs(value) do @@ -129,7 +133,7 @@ function Model:init(params) self.widget = self.dtype end -function Model:load(txn, path, addr, create) - if not create and not txn:get(addr) then return end - return self.model(txn, path, addr) +function Model:load(context, create) + if not create and not context.txn:get(context.addr) then return end + return self.model(context) end diff --git a/acf/model/init.lua b/acf/model/init.lua index 44b1f5e..6b1fd64 100644 --- a/acf/model/init.lua +++ b/acf/model/init.lua @@ -36,9 +36,9 @@ require 'stringy' local Primitive = class(Field) -function Primitive:validate(txn, path, value) +function Primitive:validate(context, value) local t = self.dtype - if type(value) ~= t then raise(path, 'Not a '..t) end + if type(value) ~= t then raise(context.path, 'Not a '..t) end end @@ -49,15 +49,15 @@ function String:init(params) self.dtype = 'string' end -function String:validate(txn, path, value) - super(self, String):validate(txn, path, value) +function String:validate(context, value) + super(self, String):validate(context, value) if self['max-length'] and string.len(value) > self['max-length'] then - raise(path, 'Maximum length exceeded') + raise(context.path, 'Maximum length exceeded') end end -function String:meta(txn, path, addr) - local res = super(self, String):meta(txn, path, addr) +function String:meta(context) + local res = super(self, String):meta(context) res['max-length'] = self['max-length'] return res end @@ -70,10 +70,9 @@ function Number:init(params) self.dtype = 'number' end -function Number:_validate(txn, path, value) +function Number:_validate(context, value) return super(self, Number):_validate( - txn, - path, + context, value and tonumber(value) or value ) end @@ -81,9 +80,9 @@ end Integer = class(Number) -function Integer:validate(txn, path, value) - super(self, Integer):validate(txn, path, value) - if math.floor(value) ~= value then raise(path, 'Not an integer') end +function Integer:validate(context, value) + super(self, Integer):validate(context, value) + if math.floor(value) ~= value then raise(context.path, 'Not an integer') end end @@ -103,12 +102,10 @@ function Range:init(params) if not self.type then self.type = Integer end end -function Range:validate(txn, path, value) +function Range:validate(context, value) local comps = stringy.split(value, '-') - if #comps > 2 then raise(path, 'Invalid range') end - for _, v in ipairs(comps) do - to_field(self.type):_validate(txn, path, v) - end + if #comps > 2 then raise(context.path, 'Invalid range') end + for _, v in ipairs(comps) do to_field(self.type):_validate(context, v) end end @@ -120,16 +117,17 @@ function Reference:init(params) if not self.scope then self.scope = '/' end end -function Reference:abs_scope(path) - return pth.to_absolute(self.scope, pth.parent(path)) +function Reference:abs_scope(context) + return pth.to_absolute(self.scope, node.path(context.parent)) end -function Reference:meta(txn, path, addr) - local res = super(self, Reference):meta(txn, path, addr) - res.scope = self:abs_scope(path) +function Reference:meta(context) + local res = super(self, Reference):meta(context) + res.scope = self:abs_scope(context) + local txn = context.txn local objs = txn:get( - getmetatable(relabel('system', txn.search, txn, res.scope)).addr + node.addr(relabel('system', txn.search, txn, res.scope)) ) or {} res.choice = map(function(p) return pth.join(res.scope, p) end, objs) res['ui-choice'] = objs @@ -137,25 +135,26 @@ function Reference:meta(txn, path, addr) return res end -function Reference:follow(txn, path, value) - return txn:search(pth.join(self:abs_scope(path), value)) +function Reference:follow(context, value) + return context.txn:search(pth.join(self:abs_scope(context), value)) end -function Reference:load(txn, path, addr) - local ref = super(self, Reference):load(txn, path, addr) - return ref and self:follow(txn, path, ref) or nil +function Reference:load(context) + local ref = super(self, Reference):load(context) + return ref and self:follow(context, ref) or nil end -function Reference:_validate(txn, path, value) - super(self, Reference):_validate(txn, path, value) +function Reference:_validate(context, value) + super(self, Reference):_validate(context, value) if value == nil then return end - if object.isinstance(value, node.TreeNode) then - value = getmetatable(value).path - end - if value and pth.is_absolute(value) then - local scope = self:abs_scope(path) + if object.isinstance(value, node.TreeNode) then value = node.path(value) end + + local path = context.path + + if pth.is_absolute(value) then + local scope = self:abs_scope(context) local prefix = scope..'/' if not stringy.startswith(value, prefix) then raise(path, 'Reference out of scope ('..scope..')') @@ -169,7 +168,7 @@ function Reference:_validate(txn, path, value) end -- TODO check instance type - relabel(path, self.follow, self, txn, path, value) + relabel(path, self.follow, self, context, value) return value end @@ -188,9 +187,12 @@ function Collection:init(params, ctype) self.widget = self.dtype end -function Collection:load(txn, path, addr) +function Collection:load(context) -- automatically create missing collection (TODO: make this configurable?) - return self.ctype(txn, path, addr, to_field(self.type), self.required) + return self.ctype( + context, + {field=to_field(self.type), required=self.required} + ) end @@ -208,30 +210,29 @@ function Mixed:init() super(self, Mixed):init({type=Mixed}, node.Mixed) end -function Mixed:load(txn, path, addr) - local value = Primitive.load(self, txn, path, addr) - if type(value) == 'table' then - return super(self, Mixed):load(txn, path, addr) - end +function Mixed:load(context) + local value = Primitive.load(self, context) + if type(value) == 'table' then return super(self, Mixed):load(context) end return value end -function Mixed:save(txn, path, addr, value) - if type(value) == 'table' then - super(self, Mixed):save(txn, path, addr, value) - else Primitive.save(self, txn, path, addr, value) end +function Mixed:save(context, value) + if type(value) == 'table' then super(self, Mixed):save(context, value) + else Primitive.save(self, context, value) end end RootModel = new() -function RootModel:init(txn) super(self, RootModel):init(txn, '/') end +function RootModel:init(txn) super(self, RootModel):init{txn=txn, path='/'} end function register(name, path, field) local field = to_field(field) - function field:compute(txn, pth, addr) - return self:load(txn, '/'..name, path) + function field:compute(context) + context.path = '/'..name + context.addr = path + return self:load(context) end RootModel[name] = field end diff --git a/acf/model/model.lua b/acf/model/model.lua index aa27312..5fd96a7 100644 --- a/acf/model/model.lua +++ b/acf/model/model.lua @@ -59,8 +59,8 @@ end Model = new(node.TreeNode) -function Model:init(txn, path, addr) - super(self, Model):init(txn, path, addr) +function Model:init(context) + super(self, Model):init(context) local mt = getmetatable(self) diff --git a/acf/model/node.lua b/acf/model/node.lua index b0c6f8a..7a818be 100644 --- a/acf/model/node.lua +++ b/acf/model/node.lua @@ -11,6 +11,7 @@ local class = object.class local super = object.super local pth = require('acf.path') +local update = require('acf.util').update BoundField = class() @@ -29,11 +30,16 @@ function BoundField:init(parent, field) name = arg[1] table.remove(arg, 1) end - return member(field, - pmt.txn, - pth.join(pmt.path, name), - pmt.addr and pth.join(pmt.addr, name), - unpack(arg)) + return member( + field, + { + txn=pmt.txn, + parent=parent, + path=pth.join(pmt.path, name), + addr=pmt.addr and pth.join(pmt.addr, name) + }, + unpack(arg) + ) end end @@ -43,13 +49,10 @@ end TreeNode = class() -function TreeNode:init(txn, path, addr) +function TreeNode:init(context) local mt = getmetatable(self) - mt.txn = txn - mt.path = path - mt.addr = addr - - txn.validable[path] = addr + update(mt, context) + mt.txn.validable[mt.path] = mt.addr end function TreeNode:search(path) @@ -66,22 +69,22 @@ end Collection = class(TreeNode) -function Collection:init(txn, path, addr, field, required) - super(self, Collection):init(txn, path, addr) +function Collection:init(context, params) + super(self, Collection):init(context) self.init = nil self.search = nil local mt = getmetatable(self) - mt.field = BoundField(self, field) + mt.field = BoundField(self, params.field) mt.meta = {type='collection', members=mt.field:meta('$')} function mt.mmeta(name) return mt.meta.members end - function mt.members() return txn:get(addr) or {} end + function mt.members() return mt.txn:get(mt.addr) or {} end function mt.validate() - if required and #txn:get(addr) == 0 then - raise(path, 'Collection cannot be empty') + if params.required and #mt.txn:get(mt.addr) == 0 then + raise(mt.path, 'Collection cannot be empty') end end @@ -92,8 +95,8 @@ end PrimitiveList = class(Collection) -function PrimitiveList:init(txn, path, addr, field, required) - super(self, PrimitiveList):init(txn, path, addr, field, required) +function PrimitiveList:init(context, params) + super(self, PrimitiveList):init(context, params) local mt = getmetatable(self) local index = mt.__index @@ -101,11 +104,11 @@ function PrimitiveList:init(txn, path, addr, field, required) function mt.__index(t, k) if type(k) == 'number' then return index(t, k) end - for i, j in ipairs(txn:get(addr) or {}) do + for i, j in ipairs(mt.txn:get(mt.addr) or {}) do assert(i == tonumber(j)) if mt.field:load(i) == k then return k end end - raise(path, 'Value does not exist: '..k) + raise(mt.path, 'Value does not exist: '..k) end end @@ -113,8 +116,8 @@ end -- experimental Mixed = class(Collection) -function Mixed:init(txn, path, addr, field, required) - super(self, Mixed):init(txn, path, addr, field, required) +function Mixed:init(context, params) + super(self, Mixed):init(context, params) -- TODO dynamic meta: list non-leaf children getmetatable(self).meta = {type='mixed'} end @@ -128,8 +131,10 @@ local function meta_func(attr) end end +addr = meta_func('addr') members = meta_func('members') meta = meta_func('meta') mmeta = meta_func('mmeta') +parent = meta_func('parent') path = meta_func('path') validate = meta_func('validate') |