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
121
122
123
124
|
--[[
Copyright (c) 2012-2014 Kaarle Ritvanen
See LICENSE file for license details
--]]
local ErrorDict = require('aconf.error').ErrorDict
local root = require('aconf.model.root')
local object = require('aconf.object')
local super = object.super
local pth = require('aconf.path')
local util = require('aconf.util')
local copy = util.copy
local ModelTransaction = object.class(
require('aconf.transaction.base').Transaction
)
function ModelTransaction:init(backend, validate)
super(self, ModelTransaction):init(backend)
self.validate = validate
self.validable = {}
self.root = root.RootModel{txn=self}
end
function ModelTransaction:committing()
return self.commit_val and true or false
end
function ModelTransaction:check()
if not self.backend then error('Transaction already committed') end
end
function ModelTransaction:get(path)
self:check()
return super(self, ModelTransaction):get(path)
end
function ModelTransaction:set_multiple(mods)
super(self, ModelTransaction):set_multiple(mods)
for _, mod in ipairs(mods) do
local addr, value = unpack(mod)
if value == nil then
for _, val in ipairs{self.validable, self.commit_val} do
local done
repeat
done = true
for path, a in pairs(copy(val)) do
if a == addr then
for p, _ in pairs(copy(val)) do
if pth.is_subordinate(p, path) then val[p] = nil end
end
done = false
break
end
end
until done
end
end
end
end
function ModelTransaction:check_deleted(path)
-- assume one-level refs for now
local top = root.topology(pth.parent(path))
if top then
local errors = ErrorDict()
for _, refs in ipairs(top.referrers) do
for _, ref in ipairs(self.root:search_refs(refs)) do
errors:collect(ref.deleted, ref, path)
end
end
errors:raise()
end
end
function ModelTransaction:fetch(path) return self.root:fetch(path) end
function ModelTransaction:meta(path) return self.root:meta(path) end
function ModelTransaction:commit()
self:check()
if self.validate then
self.commit_val = copy(self.validable)
local errors = ErrorDict()
local function validate(path)
if path > '/' then validate(pth.parent(path)) end
if not self.commit_val[path] then return end
errors:collect(getmetatable(self:fetch(path)).validate)
self.commit_val[path] = nil
end
while next(self.commit_val) do validate(next(self.commit_val)) end
self.commit_val = nil
errors:raise()
end
super(self, ModelTransaction):commit()
if not self.validate then
util.update(self.backend.validable, self.validable)
end
self.backend = nil
end
local store = require('aconf.persistence')
local def_store = require('aconf.persistence.defer')
return function(options)
options = options or {}
return ModelTransaction(
options.parent or (options.allow_commit_defer and def_store or store),
not (options.parent and options.defer_validation)
)
end
|