summaryrefslogtreecommitdiffstats
path: root/acf
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-06-27 20:18:54 +0300
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-06-28 13:24:36 +0300
commit5c960909594d1978f45968f8155ab2afab381298 (patch)
tree40d6de63ce5818a46b05794a4596329c28d4bd5f /acf
parent0d5dade5ab61e5cfd3116893aadcb9b16ba5cfb5 (diff)
downloadaconf-5c960909594d1978f45968f8155ab2afab381298.tar.bz2
aconf-5c960909594d1978f45968f8155ab2afab381298.tar.xz
eliminate data type arguments from transaction and persistence manager intefaces
utilize model topology information instead
Diffstat (limited to 'acf')
-rw-r--r--acf/model/field.lua17
-rw-r--r--acf/model/init.lua22
-rw-r--r--acf/model/node.lua2
-rw-r--r--acf/model/root.lua21
-rw-r--r--acf/path.lua8
-rw-r--r--acf/persistence/backends/augeas.lua25
-rw-r--r--acf/persistence/backends/files.lua53
-rw-r--r--acf/persistence/backends/json.lua8
-rw-r--r--acf/persistence/backends/null.lua2
-rw-r--r--acf/persistence/backends/volatile.lua6
-rw-r--r--acf/persistence/init.lua37
-rw-r--r--acf/transaction/backend.lua14
-rw-r--r--acf/transaction/init.lua37
13 files changed, 144 insertions, 108 deletions
diff --git a/acf/model/field.lua b/acf/model/field.lua
index a784b44..98a4397 100644
--- a/acf/model/field.lua
+++ b/acf/model/field.lua
@@ -77,15 +77,15 @@ function Field:meta(context)
return res
end
-function Field:btype(context) return self.dtype end
-
function Field:topology(context)
- return {{path=context.path, addr=context.addr}}
+ return {
+ {path=context.path, addr=context.addr, type=self.dtype}
+ }
end
function Field:load(context)
if not context.txn then return setmetatable({}, context) end
- local value = context.txn:get(context.addr, self:btype(context))
+ local value = context.txn:get(context.addr)
if value == nil then return self.default end
return value
end
@@ -104,9 +104,7 @@ end
function Field:validate(context, value) end
function Field:save(context, value)
- context.txn:set(
- context.addr, self:btype(context), self:_validate(context, value)
- )
+ context.txn:set(context.addr, self:_validate(context, value))
end
function Field:validate_saved(context)
@@ -179,13 +177,14 @@ TreeNode = class(Field)
function TreeNode:topology(context)
local res = super(self, TreeNode):topology(context)
+ res[1].type = 'table'
util.extend(res, node.topology(self:load(context, true)))
return res
end
function TreeNode:load(context, create)
if context.txn and not (
- create or self.create or context.txn:get(context.addr, 'table')
+ create or self.create or context.txn:get(context.addr)
) then return end
return self.itype(context, self.iparams)
end
@@ -206,7 +205,7 @@ function TreeNode:save(context, value)
raise(path, 'Cannot assign primitive value')
end
- context.txn:set(context.addr, 'table')
+ context.txn:set(context.addr, {})
local new = self:load(context, true)
local errors = err.ErrorDict()
diff --git a/acf/model/init.lua b/acf/model/init.lua
index 7a51652..1870849 100644
--- a/acf/model/init.lua
+++ b/acf/model/init.lua
@@ -52,32 +52,30 @@ function Reference:init(params)
if not self.scope then self.scope = '/' end
end
-function Reference:abs_scope(context)
- return pth.to_absolute(self.scope, node.path(context.parent))
+function Reference:topology(context)
+ local res = super(self, Reference):topology(context)
+ res[1].scope = self.scope
+ return res
end
-function Reference:scope_addr(context)
- local txn = context.txn
- return node.addr(
- relabel('system', txn.search, txn, self:abs_scope(context))
- )
+function Reference:abs_scope(context)
+ return pth.to_absolute(self.scope, node.path(context.parent))
end
function Reference:meta(context)
local res = super(self, Reference):meta(context)
res.scope = self:abs_scope(context)
- local objs = context.txn:get(self:scope_addr(context), 'table') or {}
+ local txn = context.txn
+ local objs = txn:get(
+ node.addr(relabel('system', txn.search, txn, res.scope))
+ ) or {}
res.choice = map(function(p) return pth.join(res.scope, p) end, objs)
res['ui-choice'] = objs
return res
end
-function Reference:btype(context)
- return 'reference'..self:scope_addr(context)
-end
-
function Reference:follow(context, value)
return context.txn:search(pth.rawjoin(self:abs_scope(context), value))
end
diff --git a/acf/model/node.lua b/acf/model/node.lua
index 4b4808d..f3f9d28 100644
--- a/acf/model/node.lua
+++ b/acf/model/node.lua
@@ -139,7 +139,7 @@ function Collection:init(context, params)
return res
end
- function mt.members() return mt.txn:get(mt.addr, 'table') or {} end
+ function mt.members() return mt.txn:get(mt.addr) or {} end
function mt.validate()
if #mt.members() > 0 then return end
diff --git a/acf/model/root.lua b/acf/model/root.lua
index 0c8d866..da15411 100644
--- a/acf/model/root.lua
+++ b/acf/model/root.lua
@@ -60,12 +60,31 @@ function register(name, field, params)
params.create = true
RootModel[name] = model.to_field(field, params)
- for _, record in ipairs(node.topology(RootModel():search(name))) do
+ local root = RootModel()
+
+ for _, record in ipairs(node.topology(root:search(name))) do
local top = topology(record.addr, true)
setdefault(top, 'order', order)
order = order + 1
+ local function set(k, v)
+ setdefault(top, k, v)
+ assert(top[k] == v)
+ end
+
+ set('type', record.type)
table.insert(top.paths, record.path)
+
+ if record.scope then
+ set(
+ 'scope',
+ node.addr(
+ root:search(
+ pth.to_absolute(record.scope, pth.parent(record.path))
+ )
+ )
+ )
+ end
end
end
diff --git a/acf/path.lua b/acf/path.lua
index 3a5647d..cd7ea2c 100644
--- a/acf/path.lua
+++ b/acf/path.lua
@@ -70,6 +70,14 @@ function split(path)
end
+function is_unique(path)
+ for _, comp in ipairs(split(path)) do
+ if comp == wildcard then return false end
+ end
+ return true
+end
+
+
function to_absolute(path, base)
if not is_absolute(path) then
path = base..(base ~= '/' and '/' or '')..path
diff --git a/acf/persistence/backends/augeas.lua b/acf/persistence/backends/augeas.lua
index e25a568..165cf8b 100644
--- a/acf/persistence/backends/augeas.lua
+++ b/acf/persistence/backends/augeas.lua
@@ -24,16 +24,16 @@ backend = require('acf.object').class()
function backend:init() self.aug = require('augeas').init() end
-function backend:find(path, tpe)
+function backend:find(path, leaf)
util.map(
function(comp)
- return comp == strip_name(comp) and not string.match(comp, '^%.+$')
+ assert(comp == strip_name(comp) and not string.match(comp, '^%.+$'))
end,
path
)
local res = aug_path(path)
- if #self.aug:match(res) == 0 and #path > 1 and tpe ~= 'table' then
+ if #self.aug:match(res) == 0 and #path > 1 and leaf then
local index = path[#path]
if type(index) == 'number' then
local ppath = copy(path)
@@ -51,9 +51,10 @@ function backend:find(path, tpe)
return res
end
-function backend:get(path, tpe)
+function backend:get(path, top)
+ local tpe = top and top.type
local leaf = tpe and tpe ~= 'table'
- local apath, mvpath = self:find(path, tpe)
+ local apath, mvpath = self:find(path, leaf)
local matches = self.aug:match(apath)
if mvpath then
@@ -90,13 +91,14 @@ function backend:set(mods)
local gcpaths = {}
for _, mod in ipairs(mods) do
- local path, tpe, value = unpack(mod)
+ local path, value = unpack(mod)
- self.aug:rm(aug_path(path)..(tpe and '/*' or ''))
+ local delete = value == nil
+ self.aug:rm(aug_path(path)..(delete and '' or '/*'))
- local apath, mvpath, index = self:find(path, tpe)
+ local apath, mvpath, index = self:find(path, type(value) ~= 'table')
- if tpe and mvpath then
+ if not delete and mvpath then
local size = #self.aug:match(mvpath)
while size < index do
self.aug:insert(ipath(mvpath, size), pth.name(mvpath))
@@ -104,10 +106,11 @@ function backend:set(mods)
end
end
- if tpe or mvpath then self.aug:set(apath, value)
+ if type(value) == 'table' then value = nil end
+ if not delete or mvpath then self.aug:set(apath, value)
elseif apath > '/' then apath = pth.parent(apath) end
- if value == nil or value == '' then gcpaths[mvpath or apath] = true end
+ if delete or value == '' then gcpaths[mvpath or apath] = true end
end
local function gc(path)
diff --git a/acf/persistence/backends/files.lua b/acf/persistence/backends/files.lua
index f0ac5a6..782674d 100644
--- a/acf/persistence/backends/files.lua
+++ b/acf/persistence/backends/files.lua
@@ -5,19 +5,32 @@ See LICENSE file for license details
module(..., package.seeall)
+local topology = require('acf.model.root').topology
local pth = require('acf.path')
local util = require('acf.persistence.util')
+local copy = require('acf.util').copy
require 'posix'
require 'stringy'
+local function get_scope(top)
+ if not top or top.type ~= 'reference' or not pth.is_unique(top.scope) then
+ return
+ end
+
+ return stringy.startswith(top.scope, '/files/') and string.sub(
+ top.scope, 7, -1
+ ) or nil
+end
+
+
backend = require('acf.object').class()
-- TODO cache expiration
function backend:init() self.cache = {} end
-function backend:get(path, tpe)
+function backend:get(path, top)
local name = pth.join('/', unpack(path))
if not self.cache[name] then
@@ -31,10 +44,10 @@ function backend:get(path, tpe)
-- TODO handle relative symlinks
local target = posix.readlink(name)
assert(target)
- if not tpe then return target end
- assert(stringy.startswith(tpe, 'reference/files/'))
- local scope = string.sub(tpe, 16, -1)..'/'
+ local scope = get_scope(top)
+ assert(scope)
+ scope = scope..'/'
local slen = string.len(scope)
assert(string.sub(target, 1, slen) == scope)
@@ -57,10 +70,10 @@ end
function backend:set(mods)
for _, mod in pairs(mods) do
- local path, tpe, value = unpack(mod)
+ local path, value = unpack(mod)
local name = pth.join('/', unpack(path))
- if not tpe then
+ if value == nil then
print('DEL', name)
local t = posix.stat(name, 'type')
@@ -70,24 +83,24 @@ function backend:set(mods)
self.cache[name] = nil
- elseif tpe == 'table' then
+ elseif type(value) == 'table' then
assert(posix.mkdir(name))
- elseif stringy.startswith(tpe, 'reference/files/') then
- -- TODO use relative symlink
- os.remove(name)
- assert(
- posix.link(
- pth.to_absolute(value, string.sub(tpe, 16, -1)), name, true
- )
- )
-
else
- local file = util.open_file(name, 'w')
- file:write(tostring(value))
- file:close()
+ local scope = get_scope(topology('/files'..name))
+
+ if scope then
+ -- TODO use relative symlink
+ os.remove(name)
+ assert(posix.link(pth.to_absolute(value, scope), name, true))
- self.cache[name] = value
+ else
+ local file = util.open_file(name, 'w')
+ file:write(tostring(value))
+ file:close()
+
+ self.cache[name] = value
+ end
end
end
end
diff --git a/acf/persistence/backends/json.lua b/acf/persistence/backends/json.lua
index 65fc987..d2e6196 100644
--- a/acf/persistence/backends/json.lua
+++ b/acf/persistence/backends/json.lua
@@ -51,21 +51,21 @@ function backend:split_path(path)
end
end
-function backend:get(path, tpe)
+function backend:get(path, top)
local fpath, jpath = self:split_path(path)
if not self.cache[fpath] then
self.cache[fpath] = Cache(json.decode(util.read_file(fpath)))
end
- return self.cache[fpath]:get(jpath, tpe)
+ return self.cache[fpath]:get(jpath, top)
end
function backend:set(mods)
local dirty = {}
for _, mod in ipairs(mods) do
- local path, tpe, value = unpack(mod)
+ local path, value = unpack(mod)
local fpath, jpath = self:split_path(path)
- self.cache[fpath]:_set(jpath, tpe, value)
+ self.cache[fpath]:_set(jpath, value)
dirty[fpath] = true
end
diff --git a/acf/persistence/backends/null.lua b/acf/persistence/backends/null.lua
index 45b6f22..0343cbf 100644
--- a/acf/persistence/backends/null.lua
+++ b/acf/persistence/backends/null.lua
@@ -6,5 +6,5 @@ See LICENSE file for license details
module(..., package.seeall)
backend = require('acf.object').class()
-function backend:get(path, tpe) if #path == 0 then return {} end end
+function backend:get(path, top) if #path == 0 then return {} end end
function backend:set(mods) end
diff --git a/acf/persistence/backends/volatile.lua b/acf/persistence/backends/volatile.lua
index 7322323..1b1b4cd 100644
--- a/acf/persistence/backends/volatile.lua
+++ b/acf/persistence/backends/volatile.lua
@@ -22,13 +22,13 @@ function backend:_get(path)
return res
end
-function backend:get(path, tpe)
+function backend:get(path, top)
local res = self:_get(path)
return type(res) == 'table' and util.keys(res) or res
end
-function backend:_set(path, tpe, value)
- if tpe == 'table' then value = {} end
+function backend:_set(path, value)
+ if type(value) == 'table' then value = {} end
if #path == 0 then self.data = value
diff --git a/acf/persistence/init.lua b/acf/persistence/init.lua
index 3e319c6..80534c2 100644
--- a/acf/persistence/init.lua
+++ b/acf/persistence/init.lua
@@ -6,6 +6,7 @@ See LICENSE file for license details
module(..., package.seeall)
local loadmods = require('acf.loader').loadmods
+local topology = require('acf.model.root').topology
local object = require('acf.object')
local pth = require('acf.path')
local util = require('acf.util')
@@ -31,26 +32,29 @@ function DataStore:split_path(path)
return backend, comps
end
-function DataStore:get(path, t)
+function DataStore:get(path)
local backend, comps = self:split_path(path)
- local res = backend:get(comps, t)
+ local top = topology(path)
- if t ~= nil and res ~= nil then
- local atype = type(res)
+ local res = backend:get(comps, top)
- if t == 'table' then assert(atype == 'table')
+ if top then
+ local t = top.type
+ if t and res ~= nil then
+ local atype = type(res)
- else
- assert(atype ~= 'table')
+ if t == 'table' then assert(atype == 'table')
- if t == 'string' then res = tostring(res)
- elseif t == 'number' then res = tonumber(res)
- elseif t == 'boolean' then res = res and true or false
-
- elseif stringy.startswith(t, 'reference/') then
- assert(atype == 'string')
+ else
+ assert(atype ~= 'table')
- else assert(false) end
+ if t == 'string' then res = tostring(res)
+ elseif t == 'number' then res = tonumber(res)
+ elseif t == 'boolean' then
+ res = (res and res ~= 'false') and true or false
+ elseif t == 'reference' then assert(atype == 'string')
+ else assert(false) end
+ end
end
end
@@ -61,10 +65,9 @@ function DataStore:_set_multiple(mods)
local bms = {}
for _, mod in ipairs(mods) do
- local path, t, value = unpack(mod)
- if not t and value ~= nil then t = type(value) end
+ local path, value = unpack(mod)
local backend, comps = self:split_path(path)
- table.insert(util.setdefault(bms, backend, {}), {comps, t, value})
+ table.insert(util.setdefault(bms, backend, {}), {comps, value})
end
for backend, bm in pairs(bms) do backend:set(bm) end
diff --git a/acf/transaction/backend.lua b/acf/transaction/backend.lua
index d778a40..659fca2 100644
--- a/acf/transaction/backend.lua
+++ b/acf/transaction/backend.lua
@@ -23,14 +23,14 @@ TransactionBackend = require('acf.object').class()
function TransactionBackend:init() self.mod_time = {} end
-function TransactionBackend:get_if_older(path, timestamp, t)
- local value, ts = self:get(path, t)
+function TransactionBackend:get_if_older(path, timestamp)
+ local value, ts = self:get(path)
if ts > timestamp then err.raise('conflict', path) end
return value, ts
end
-function TransactionBackend:set(path, t, value)
- self:set_multiple{{path, t, value}}
+function TransactionBackend:set(path, value)
+ self:set_multiple{{path, value}}
end
function TransactionBackend:set_multiple(mods)
@@ -41,11 +41,11 @@ function TransactionBackend:set_multiple(mods)
local function tostr(s) return s ~= nil and tostring(s) or nil end
for _, mod in ipairs(mods) do
- local path, t, value = unpack(mod)
+ local path, value = unpack(mod)
- if t == 'table' or type(
+ if type(value) == 'table' or type(
self:get(path)
- ) == 'table' or self:get(path, t) ~= value then
+ ) == 'table' or self:get(path) ~= value then
table.insert(effective, mod)
self.mod_time[path] = timestamp
diff --git a/acf/transaction/init.lua b/acf/transaction/init.lua
index ec83aae..b18c57a 100644
--- a/acf/transaction/init.lua
+++ b/acf/transaction/init.lua
@@ -53,26 +53,25 @@ function Transaction:check()
if not self.backend then error('Transaction already committed') end
end
-function Transaction:get(path, t)
+function Transaction:get(path)
self:check()
if self.deleted[path] then return nil, self.mod_time[path] end
for _, tbl in ipairs{self.added, self.modified} do
if tbl[path] ~= nil then
- return copy(tbl[path][2]), self.mod_time[path]
+ return copy(tbl[path]), self.mod_time[path]
end
end
- local value, timestamp = self.backend:get_if_older(path, self.started, t)
+ local value, timestamp = self.backend:get_if_older(path, self.started)
self.access_time[path] = timestamp
return value, timestamp
end
function Transaction:_set_multiple(mods)
- local function set(path, t, value, new)
+ local function set(path, value, new)
local delete = value == nil
- value = not delete and {t, value} or nil
if self.added[path] == nil and (not new or self.deleted[path]) then
self.modified[path] = value
@@ -81,35 +80,33 @@ function Transaction:_set_multiple(mods)
end
for _, mod in ipairs(mods) do
- local path, t, value = unpack(mod)
+ local path, value = unpack(mod)
local ppath = pth.parent(path)
- local parent = self:get(ppath, 'table')
+ local parent = self:get(ppath)
if parent == nil then
- self:set(ppath, 'table', true)
parent = {}
+ self:set(ppath, parent)
end
local name = pth.name(path)
local old = self:get(path)
- local is_table = t == 'table'
- local delete = not is_table and value == nil
+ local delete = value == nil
if type(old) == 'table' then
if delete then
for _, child in ipairs(old) do self:set(pth.join(path, child)) end
- elseif is_table then return
+ elseif type(value) == 'table' then return
elseif #old > 0 then
error('Cannot assign a primitive value to non-leaf node '..path)
end
end
- if is_table then value = {} end
- set(path, not delete and t or nil, value, old == nil)
+ set(path, value, old == nil)
local function set_parent()
- set(ppath, 'table', parent)
+ set(ppath, parent)
self.mod_time[ppath] = self.mod_time[path]
end
@@ -143,9 +140,9 @@ function Transaction:commit()
local mods = {}
local handled = {}
- local function insert(path, t, value)
+ local function insert(path, value)
assert(not handled[path])
- table.insert(mods, {path, t, value})
+ table.insert(mods, {path, value})
handled[path] = true
end
@@ -153,10 +150,7 @@ function Transaction:commit()
if not handled[path] then
local pp = pth.parent(path)
if self.added[pp] then insert_add(pp) end
-
- local t, value = unpack(self.added[path])
- if t == 'table' then value = nil end
- insert(path, t, value)
+ insert(path, self.added[path])
end
end
@@ -179,8 +173,7 @@ function Transaction:commit()
end
for path, value in pairs(self.modified) do
- local t, v = unpack(value)
- if t ~= 'table' then insert(path, t, v) end
+ if type(value) ~= 'table' then insert(path, value) end
end
for path, _ in pairs(self.added) do insert_add(path) end