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
125
126
127
128
129
130
|
--[[
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, user)
super(self, ModelTransaction):init(backend)
self.validate = validate
self.validable = {}
self.user = user
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 = table.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, escalate)
local root = self.root
return (escalate and getmetatable(root).escalate or 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, true)).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),
options.parent and options.parent.user or options.user
)
end
|