1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
--[[
Copyright (c) 2012 Kaarle Ritvanen
See LICENSE file for license details
--]]
module(..., package.seeall)
local node = require('acf.model.node')
local object = require('acf.object')
local class = object.class
local super = object.super
local map = require('acf.util').map
local function contains(list, value)
for k, v in ipairs(list) do if v == value then return true end end
return false
end
local function auto_ui_name(name)
if not name then return end
return string.gsub(string.upper(string.sub(name, 1, 1))..string.sub(name, 2),
'_', ' ')
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
if self.choice and not self['ui-choice'] then
self['ui-choice'] = map(auto_ui_name, self.choice)
end
if not self.widget then
self.widget = self.choice and 'combobox' or 'field'
end
end
function Field:meta(txn, path, addr)
assert(self.dtype)
return {
name=self.name,
type=self.dtype,
required=self.required,
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']
}
end
function Field:load(txn, path, addr)
local value = txn:get(addr)
if value == nil then return self.default end
return value
end
function Field:_validate(txn, path, value)
if self.required and value == nil then
error('Required value not set: '..path)
end
if self.choice and value ~= nil and not contains(self.choice, value) then
error('Invalid value for '..path..': '..value)
end
if value ~= nil then self:validate(txn, path, value) end
return value
end
function Field:validate(txn, path, value) end
function Field:save(txn, path, addr, value)
-- 2nd argument currenly not much used by backends
txn:set(addr, self.dtype, self:_validate(txn, path, value))
end
function Field:validate_saved(txn, path, addr)
self:save(txn, path, addr, self:load(txn, path, addr))
end
TreeNode = class(Field)
function TreeNode:save(txn, path, addr, value)
-- TODO hack, allow preserving old instance on parent update
if value == path then return end
if object.isinstance(value, node.TreeNode) then
-- TODO clone if TreeNode has wrong path
assert(node.path(value) == path)
return
end
txn:set(addr)
if value then
assert(type(value) == 'table')
txn:set(addr, 'table')
local new = self:load(txn, path, addr, true)
for k, v in pairs(value) do new[k] = v end
end
end
Model = class(TreeNode)
function Model:init(params)
super(self, Model):init(params)
assert(self.model)
self.dtype = 'model'
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)
end
|