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
|
--[[
Copyright (c) 2012-2015 Kaarle Ritvanen
See LICENSE file for license details
--]]
local loadmods = require('aconf.loader')
local topology = require('aconf.model.root').topology
local object = require('aconf.object')
local address = require('aconf.path.address')
local util = require('aconf.util')
local contains = util.contains
local copy = util.copy
local setdefault = util.setdefault
local stringy = require('stringy')
local DataStore = object.class(
require('aconf.transaction.base').TransactionBackend
)
function DataStore:init()
object.super(self, DataStore):init()
self.backends = util.map(
function(m) return m() end,
loadmods('persistence/backends')
)
self.triggers = {pre={}, post={}}
self:flush_cache()
end
function DataStore:flush_cache() self.cache = {} end
function DataStore:trigger(phase, path, func)
local funcs = setdefault(self.triggers[phase], path, {})
if not contains(funcs, func) then table.insert(funcs, func) end
end
function DataStore:split_path(path)
local comps = address.split(path)
local backend = self.backends[comps[1]]
assert(backend)
table.remove(comps, 1)
return backend, comps
end
function DataStore:get(path)
if not self.cache[path] then
local backend, comps = self:split_path(path)
local top = topology(path)
local value, ts = backend:get(comps, top)
if top then
local t = top.type
if t and value ~= nil then
local atype = type(value)
if t == 'table' then assert(atype == 'table')
else
assert(atype ~= 'table')
if t == 'string' then value = tostring(value)
elseif t == 'number' then value = tonumber(value)
elseif t == 'boolean' then
if atype == 'string' then value = value:lower() end
if value == 1 or contains(
{'1', 't', 'true', 'y', 'yes'}, value
) then
value = true
elseif value == 0 or contains(
{'0', 'f', 'false', 'n', 'no'}, value
) then
value = false
else value = value and true or false end
elseif contains({'binary', 'reference'}, t) then
assert(atype == 'string')
else assert(false) end
end
end
end
self.cache[path] = {copy(value), ts or self.mod_time[path] or 0}
end
local value, ts = table.unpack(self.cache[path])
return copy(value), ts
end
function DataStore:_set_multiple(mods)
self:flush_cache()
local bms = {}
local trigger = {}
for _, mod in ipairs(mods) do
local path, value = table.unpack(mod)
local tp = path
while not trigger[tp] do
trigger[tp] = true
tp = address.parent(tp)
end
local backend, comps = self:split_path(path)
table.insert(setdefault(bms, backend, {}), {comps, value})
end
local function exec_triggers(phase)
for path, _ in pairs(trigger) do
for _, func in ipairs(self.triggers[phase][path] or {}) do func() end
end
end
exec_triggers('pre')
for backend, bm in pairs(bms) do backend:set(bm) end
exec_triggers('post')
end
return DataStore()
|