--[[ 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