diff options
Diffstat (limited to 'aconf/transaction/init.lua')
-rw-r--r-- | aconf/transaction/init.lua | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/aconf/transaction/init.lua b/aconf/transaction/init.lua new file mode 100644 index 0000000..9e508b9 --- /dev/null +++ b/aconf/transaction/init.lua @@ -0,0 +1,126 @@ +--[[ +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(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 |