summaryrefslogtreecommitdiffstats
path: root/aconf/persistence/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'aconf/persistence/init.lua')
-rw-r--r--aconf/persistence/init.lua113
1 files changed, 113 insertions, 0 deletions
diff --git a/aconf/persistence/init.lua b/aconf/persistence/init.lua
new file mode 100644
index 0000000..991776a
--- /dev/null
+++ b/aconf/persistence/init.lua
@@ -0,0 +1,113 @@
+--[[
+Copyright (c) 2012-2014 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 pth = require('aconf.path')
+
+local util = require('aconf.util')
+local contains = util.contains
+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={}}
+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 = pth.split(path)
+ local backend = self.backends[comps[1]]
+ assert(backend)
+ table.remove(comps, 1)
+ return backend, comps
+end
+
+function DataStore:get(path)
+ local backend, comps = self:split_path(path)
+ local top = topology(path)
+
+ local res = backend:get(comps, top)
+
+ if top then
+ local t = top.type
+ if t and res ~= nil then
+ local atype = type(res)
+
+ if t == 'table' then assert(atype == 'table')
+
+ else
+ assert(atype ~= 'table')
+
+ if t == 'string' then res = tostring(res)
+ elseif t == 'number' then res = tonumber(res)
+
+ elseif t == 'boolean' then
+ if atype == 'string' then res = res:lower() end
+ if res == 1 or contains({'1', 't', 'true', 'y', 'yes'}, res) then
+ res = true
+ elseif res == 0 or contains(
+ {'0', 'f', 'false', 'n', 'no'}, res
+ ) then
+ res = false
+ else res = res and true or false end
+
+ elseif contains({'binary', 'reference'}, t) then
+ assert(atype == 'string')
+
+ else assert(false) end
+ end
+ end
+ end
+
+ return util.copy(res), self.mod_time[path] or 0
+end
+
+function DataStore:_set_multiple(mods)
+ local bms = {}
+ local trigger = {}
+
+ for _, mod in ipairs(mods) do
+ local path, value = unpack(mod)
+
+ local tp = path
+ while not trigger[tp] do
+ trigger[tp] = true
+ tp = pth.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()