summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--acf/model/field.lua44
-rw-r--r--acf/model/init.lua103
-rw-r--r--acf/model/model.lua4
-rw-r--r--acf/model/node.lua51
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')