diff options
author | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2013-06-27 20:18:54 +0300 |
---|---|---|
committer | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2013-06-28 13:24:36 +0300 |
commit | 5c960909594d1978f45968f8155ab2afab381298 (patch) | |
tree | 40d6de63ce5818a46b05794a4596329c28d4bd5f /acf | |
parent | 0d5dade5ab61e5cfd3116893aadcb9b16ba5cfb5 (diff) | |
download | aconf-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.lua | 17 | ||||
-rw-r--r-- | acf/model/init.lua | 22 | ||||
-rw-r--r-- | acf/model/node.lua | 2 | ||||
-rw-r--r-- | acf/model/root.lua | 21 | ||||
-rw-r--r-- | acf/path.lua | 8 | ||||
-rw-r--r-- | acf/persistence/backends/augeas.lua | 25 | ||||
-rw-r--r-- | acf/persistence/backends/files.lua | 53 | ||||
-rw-r--r-- | acf/persistence/backends/json.lua | 8 | ||||
-rw-r--r-- | acf/persistence/backends/null.lua | 2 | ||||
-rw-r--r-- | acf/persistence/backends/volatile.lua | 6 | ||||
-rw-r--r-- | acf/persistence/init.lua | 37 | ||||
-rw-r--r-- | acf/transaction/backend.lua | 14 | ||||
-rw-r--r-- | acf/transaction/init.lua | 37 |
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 |