summaryrefslogtreecommitdiffstats
path: root/aconf/transaction/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'aconf/transaction/init.lua')
-rw-r--r--aconf/transaction/init.lua126
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