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
|
--[[
Copyright (c) 2012-2013 Kaarle Ritvanen
See LICENSE file for license details
--]]
module(..., package.seeall)
local raise = require('acf.error').raise
local fld = require('acf.model.field')
local Field = fld.Field
local node = require('acf.model.node')
local object = require('acf.object')
local class = object.class
local super = object.super
local isinstance = object.isinstance
local util = require('acf.util')
function to_field(obj)
if object.issubclass(obj, Model) then return fld.Model{model=obj} end
return getmetatable(obj).class and obj or obj()
end
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)
local mt = util.copy(getmetatable(res))
function mt.__index(t, k) return base[k] end
function mt.__newindex(t, k, v)
assert(v)
local override = t[k]
if type(v) == 'table' then v = to_field(v) end
rawset(t, k, v)
if isinstance(v, Field) then
v.name = k
if not override then table.insert(t._fields, k) end
end
end
setmetatable(res, mt)
if isinstance(base, Model) then util.setdefaults(res, base) end
return res
end
Model = new(node.TreeNode)
function Model:init(txn, path, addr)
super(self, Model):init(txn, path, addr)
local mt = getmetatable(self)
function mt.field(name)
local res = mt.class[name]
return isinstance(res, Field) and node.BoundField(self, res) or nil
end
function mt.mmeta(name) return mt.field(name):meta() end
mt.meta = {type='model',
fields=util.map(function(f) return mt.mmeta(f) end,
self._fields)}
function mt.members()
return util.map(function(f) return f.name end, mt.meta.fields)
end
function mt.__index(t, k)
local f = mt.field(k)
if f then
if f.compute then return f:compute() end
return f:load()
end
return mt.class[k]
end
function mt.__newindex(t, k, v)
local f = mt.field(k)
if not f then raise(mt.path, 'Field named '..k..' does not exist') end
f:save(v)
end
txn.validate[mt.path] = function() self:validate() end
end
function Model:validate()
local mt = getmetatable(self)
for _, name in ipairs(mt.members()) do
local field = mt.field(name)
if not field.compute then field:validate_saved() end
end
end
|