summaryrefslogtreecommitdiffstats
path: root/acf
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-06-28 07:00:01 +0300
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-06-28 13:40:05 +0300
commit00166624a85afdbe24722c7e2e3320316cfbae93 (patch)
treef9484b7eb7c2c889c048b318a402d19a7e8ac5ff /acf
parent37396d77b3e911b1c5351ab56ff3ec5797e2f62d (diff)
downloadaconf-00166624a85afdbe24722c7e2e3320316cfbae93.tar.bz2
aconf-00166624a85afdbe24722c7e2e3320316cfbae93.tar.xz
prevent deletion of objects referred to by other objects
Diffstat (limited to 'acf')
-rw-r--r--acf/model/init.lua8
-rw-r--r--acf/model/node.lua39
-rw-r--r--acf/model/root.lua13
-rw-r--r--acf/transaction/init.lua18
4 files changed, 66 insertions, 12 deletions
diff --git a/acf/model/init.lua b/acf/model/init.lua
index a53cc0d..e1a4dd9 100644
--- a/acf/model/init.lua
+++ b/acf/model/init.lua
@@ -114,6 +114,14 @@ function Reference:_validate(context, value)
return value
end
+function Reference:deleted(context, addr)
+ local target = self:load(context)
+ if target and node.addr(target) == addr then
+ -- TODO raise error for the target object
+ raise(context.path, 'Refers to '..addr)
+ end
+end
+
Model = fld.Model
diff --git a/acf/model/node.lua b/acf/model/node.lua
index 4c05d4d..0e22d63 100644
--- a/acf/model/node.lua
+++ b/acf/model/node.lua
@@ -6,8 +6,10 @@ See LICENSE file for license details
module(..., package.seeall)
local raise = require('acf.error').raise
+
local object = require('acf.object')
local class = object.class
+local isinstance = object.isinstance
local super = object.super
local pth = require('acf.path')
@@ -103,6 +105,38 @@ function TreeNode:fetch(path, create)
return TreeNode.fetch(next, path, create)
end
+function TreeNode:search_refs(path)
+ if type(path) == 'string' then path = pth.split(path) end
+
+ if #path == 0 then return {} end
+
+ local mt = getmetatable(self)
+ local name = path[1]
+ table.remove(path, 1)
+
+ local function collect(name)
+ local next = mt.load(name)
+ if not next then return {} end
+
+ local member = mt.member(name)
+ if member.deleted then return {member} end
+
+ return isinstance(next, TreeNode) and TreeNode.search_refs(
+ next, path
+ ) or {}
+ end
+
+ if name == pth.wildcard then
+ local res = {}
+ for _, member in ipairs(mt.members()) do
+ util.extend(res, collect(member))
+ end
+ return res
+ end
+
+ return collect(name)
+end
+
Collection = class(TreeNode)
@@ -111,6 +145,7 @@ function Collection:init(context, params)
self.init = nil
self.fetch = nil
+ self.search_refs = nil
local mt = getmetatable(self)
local field = BoundMember(self, pth.wildcard, params.field)
@@ -221,7 +256,7 @@ local rawpairs = pairs
local rawipairs = ipairs
function pairs(tbl)
- if not object.isinstance(tbl, TreeNode) then return rawpairs(tbl) end
+ if not isinstance(tbl, TreeNode) then return rawpairs(tbl) end
local mt = getmetatable(tbl)
local res = {}
for _, member in rawipairs(mt.members()) do res[member] = mt.load(member) end
@@ -235,6 +270,6 @@ local function _ipairs(mt, i)
return i, v
end
function ipairs(tbl)
- if not object.isinstance(tbl, TreeNode) then return rawipairs(tbl) end
+ if not isinstance(tbl, TreeNode) then return rawipairs(tbl) end
return _ipairs, getmetatable(tbl), 0
end
diff --git a/acf/model/root.lua b/acf/model/root.lua
index d17f8a9..2d3b2a0 100644
--- a/acf/model/root.lua
+++ b/acf/model/root.lua
@@ -40,7 +40,7 @@ function topology(addr, create)
else addr = pth.split(addr) end
local function defaults(top)
- return util.setdefaults(top, {members={}, paths={}})
+ return util.setdefaults(top, {members={}, paths={}, referrers={}})
end
while #addr > 0 do
@@ -77,14 +77,11 @@ function register(name, field, params)
table.insert(top.paths, record.path)
if record.scope then
- set(
- 'scope',
- node.addr(
- root:fetch(
- pth.to_absolute(record.scope, pth.parent(record.path))
- )
- )
+ local scope = node.addr(
+ root:fetch(pth.to_absolute(record.scope, pth.parent(record.path)))
)
+ set('scope', scope)
+ table.insert(topology(scope, true).referrers, record.path)
end
end
end
diff --git a/acf/transaction/init.lua b/acf/transaction/init.lua
index a76e7bf..76a8d47 100644
--- a/acf/transaction/init.lua
+++ b/acf/transaction/init.lua
@@ -6,7 +6,7 @@ See LICENSE file for license details
module(..., package.seeall)
local ErrorDict = require('acf.error').ErrorDict
-local RootModel = require('acf.model.root').RootModel
+local root = require('acf.model.root')
local object = require('acf.object')
local pth = require('acf.path')
local be_mod = require('acf.transaction.backend')
@@ -46,7 +46,7 @@ function Transaction:init(backend, validate)
self.validate = validate
self.validable = {}
- self.root = RootModel(self)
+ self.root = root.RootModel(self)
end
function Transaction:check()
@@ -94,6 +94,20 @@ function Transaction:_set_multiple(mods)
local delete = value == nil
+ if delete then
+ -- assume one-level refs for now
+ local top = root.topology(ppath)
+ 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
+
if type(old) == 'table' then
if delete then
for _, child in ipairs(old) do self:set(pth.join(path, child)) end