--[[ Copyright (c) 2012-2014 Kaarle Ritvanen See LICENSE file for license details --]] local loadmods = require('acf2.loader') local topology = require('acf2.model.root').topology local object = require('acf2.object') local pth = require('acf2.path') local util = require('acf2.util') local contains = util.contains local setdefault = util.setdefault local stringy = require('stringy') local DataStore = object.class( require('acf2.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()