diff options
Diffstat (limited to 'acf/model')
-rw-r--r-- | acf/model/aaa.lua | 87 | ||||
-rw-r--r-- | acf/model/combination.lua | 50 | ||||
-rw-r--r-- | acf/model/field.lua | 241 | ||||
-rw-r--r-- | acf/model/init.lua | 205 | ||||
-rw-r--r-- | acf/model/model.lua | 204 | ||||
-rw-r--r-- | acf/model/net.lua | 96 | ||||
-rw-r--r-- | acf/model/node.lua | 275 | ||||
-rw-r--r-- | acf/model/permission.lua | 22 | ||||
-rw-r--r-- | acf/model/root.lua | 91 | ||||
-rw-r--r-- | acf/model/set.lua | 47 |
10 files changed, 0 insertions, 1318 deletions
diff --git a/acf/model/aaa.lua b/acf/model/aaa.lua deleted file mode 100644 index 17ad98c..0000000 --- a/acf/model/aaa.lua +++ /dev/null @@ -1,87 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = require('acf.model') -local object = require('acf.object') - -local digest = require('crypto').digest - - -Role = M.new() -Role.permissions = M.Set{type=M.Reference{scope='../../../permissions'}} - - -local function hash_password(algorithm, salt, password) - return algorithm..'$'..salt..'$'..digest(algorithm, salt..password) -end - -local hash_pattern = '^(%w+)%$(%w+)%$%x+$' - - -local Password = object.class(M.String) - -function Password:_validate(context, value) - value = object.super(self, M.String):_validate(context, value) - if not value or value:find(hash_pattern) then return value end - - local salt = '' - for i = 1,12 do - local c = math.random(48, 109) - if c > 57 then c = c + 7 end - if c > 90 then c = c + 6 end - salt = salt..string.char(c) - end - return hash_password('sha256', salt, value) -end - - -User = M.new() -User.password = Password -User['real-name'] = M.String -User.superuser = M.Boolean{default=false} -User.roles = M.Set{type=M.Reference{scope='../../../roles'}} - -function User:check_password(password) - if not self.password then return false end - local _, _, algorithm, salt = self.password:find(hash_pattern) - if not salt then return false end - return hash_password(algorithm, salt, password) == self.password -end - -function User:check_permission(permission) - -- TODO audit trail - print('check permission', permission) - - if self.superuser then return true end - - assert(getmetatable(self).txn:fetch('/auth/permissions')[permission]) - - for _, role in M.node.pairs(self.roles, true) do - for _, p in M.node.pairs(role.permissions, true) do - if p == permission then return true end - end - end - return false -end - - -Authentication = M.new() -Authentication.users = M.Collection{type=User} -Authentication.roles = M.Collection{type=Role} -Authentication.permissions = M.Set{ - type=M.String, - addr='/volatile/aaa/permissions' -} - -M.register( - 'auth', - Authentication, - { - addr='/json'..require('posix').getcwd()..'/config/aaa.json', - ui_name='Authentication' - } -) - -M.permission.defaults('/auth') diff --git a/acf/model/combination.lua b/acf/model/combination.lua deleted file mode 100644 index 19f84cc..0000000 --- a/acf/model/combination.lua +++ /dev/null @@ -1,50 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local err = require('acf.error') -local raise = err.raise - -local fld = require('acf.model.field') -local String = fld.String - -local to_field = require('acf.model.model').to_field - -local object = require('acf.object') -local class = object.class -local super = object.super - - -local stringy = require('stringy') - - -M.Range = class(String) - -function M.Range:init(params) - super(self, M.Range):init(params) - if not self.type then self.type = fld.Integer end -end - -function M.Range:validate(context, value) - local comps = stringy.split(value, '-') - if #comps > 2 then raise(context.path, 'Invalid range') end - for _, v in ipairs(comps) do to_field(self.type):_validate(context, v) end -end - - -M.Union = class(String) - -function M.Union:validate(context, value) - super(self, M.Union):validate(context, value) - for _, tpe in ipairs(self.types) do - local field = to_field(tpe) - if err.call(field.validate, field, context, value) then return end - end - raise(context.path, self.error or 'Invalid value') -end - - -return M diff --git a/acf/model/field.lua b/acf/model/field.lua deleted file mode 100644 index 4d539e8..0000000 --- a/acf/model/field.lua +++ /dev/null @@ -1,241 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local err = require('acf.error') -local raise = err.raise - -local node = require('acf.model.node') - -local object = require('acf.object') -local class = object.class -local super = object.super - -local util = require('acf.util') - - -local function contains(list, value) - for k, v in ipairs(list) do if v == value then return true end end - return false -end - -M.Member = class() - -function M.Member:init(params) - for k, v in pairs(params or {}) do - if self[k] == nil then self[k] = v end - end -end - -function M.Member:auto_ui_name(name) - if not name then return end - return (name:sub(1, 1):upper()..name:sub(2)):gsub('-', ' ') -end - -function M.Member:meta(context) - return { - name=self.name, - description=self.description, - ['ui-name']=self.ui_name or self:auto_ui_name(self.name) - } -end - - -M.Field = class(M.Member) - -function M.Field:init(params) - super(self, M.Field):init(params) - - if self.choice and not self['ui-choice'] then - self['ui-choice'] = util.map( - function(name) return self:auto_ui_name(name) end, - self.choice - ) - end - - if not self.widget then - self.widget = self.choice and 'combobox' or 'field' - end -end - -function M.Field:meta(context) - assert(self.dtype) - local res = super(self, M.Field):meta(context) - - res.type = self.dtype - res.required = self.required - res.default = self.default - res.choice = self.choice - res.widget = self.widget - res['ui-choice'] = self['ui-choice'] - - return res -end - -function M.Field:topology(context) - return { - {path=context.path, addr=context.addr, type=self.dtype} - } -end - -function M.Field:load(context) - if not context.txn then return setmetatable({}, context) end - local value = context.txn:get(context.addr) - if value == nil then return self.default end - return value -end - -function M.Field:_validate(context, value) - if self.required and value == nil then - raise(context.path, 'Required value not set') - end - if self.choice and value ~= nil and not contains(self.choice, value) then - raise(context.path, 'Invalid value') - end - if value ~= nil then self:validate(context, value) end - return value -end - -function M.Field:validate(context, value) end - -function M.Field:save(context, value) - context.txn:set(context.addr, self:_validate(context, value)) -end - -function M.Field:validate_saved(context) - self:save(context, self:load(context)) -end - - -local Primitive = class(M.Field) - -function Primitive:validate(context, value) - local t = self.dtype - if type(value) ~= t then raise(context.path, 'Not a '..t) end -end - - -M.String = class(Primitive) - -function M.String:init(params) - super(self, M.String):init(params) - self.dtype = 'string' -end - -function M.String:validate(context, value) - super(self, M.String):validate(context, value) - if self['max-length'] and value:len() > self['max-length'] then - raise(context.path, 'Maximum length exceeded') - end - if self.pattern and not value:match('^'..self.pattern..'$') then - raise(context.path, 'Invalid value') - end -end - -function M.String:meta(context) - local res = super(self, M.String):meta(context) - res['max-length'] = self['max-length'] - return res -end - - -M.Number = class(Primitive) - -function M.Number:init(params) - super(self, M.Number):init(params) - self.dtype = 'number' -end - -function M.Number:_validate(context, value) - return super(self, M.Number):_validate( - context, - value and tonumber(value) or value - ) -end - - -M.Integer = class(M.Number) - -function M.Integer:validate(context, value) - super(self, M.Integer):validate(context, value) - if math.floor(value) ~= value then raise(context.path, 'Not an integer') end -end - - -M.Boolean = class(Primitive) - -function M.Boolean:init(params) - super(self, M.Boolean):init(params) - self.dtype = 'boolean' - self.widget = self.dtype -end - - -M.TreeNode = class(M.Field) - -function M.TreeNode:init(params) - if not params.widget then params.widget = 'link' end - super(self, M.TreeNode):init(params) -end - -function M.TreeNode:topology(context) - local res = super(self, M.TreeNode):topology(context) - res[1].type = 'table' - util.extend(res, node.topology(self:load(context, {create=true}))) - return res -end - -function M.TreeNode:load(context, options) - if context.txn and not ( - ( - options and options.create - ) or self.create or context.txn:get(context.addr) - ) then return end - return self.itype(context, self.iparams) -end - -function M.TreeNode:save(context, value) - local path = context.path - - if value == path then return end - if type(value) == 'string' then value = context.txn:fetch(value) end - if object.isinstance(value, node.TreeNode) and node.path(value) == path then - return - end - - context.txn:set(context.addr) - - if value then - if type(value) ~= 'table' then - raise(path, 'Cannot assign primitive value') - end - - context.txn:set(context.addr, {}) - local new = self:load(context, {create=true}) - - local errors = err.ErrorDict() - for k, v in node.pairs(value) do - errors:collect(self.save_member, new, k, v) - end - errors:raise() - end -end - -function M.TreeNode.save_member(node, k, v) node[k] = v end - - -M.Model = class(M.TreeNode) - -function M.Model:init(params) - super(self, M.Model):init(params) - - assert(self.model) - self.itype = self.model - self.dtype = 'model' -end - - -return M diff --git a/acf/model/init.lua b/acf/model/init.lua deleted file mode 100644 index 1de5202..0000000 --- a/acf/model/init.lua +++ /dev/null @@ -1,205 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -M.error = require('acf.error') -local raise = M.error.raise - -local combination = require('acf.model.combination') -M.Union = combination.Union -M.Range = combination.Range - -local fld = require('acf.model.field') -local Field = fld.Field -M.Boolean = fld.Boolean -M.Integer = fld.Integer -M.Number = fld.Number -M.String = fld.String - -local model = require('acf.model.model') -M.Action = model.Action -M.new = model.new -local to_field = model.to_field - -M.net = require('acf.model.net') - -local node = require('acf.model.node') -M.node = {} -for _, m in ipairs{ - 'List', - 'Set', - 'TreeNode', - 'has_permission', - 'insert', - 'meta', - 'mmeta', - 'path', - 'pairs', - 'ipairs' -} do M.node[m] = node[m] end - -M.permission = require('acf.model.permission') -M.register = require('acf.model.root').register -M.node.Set = require('acf.model.set').Set - -local object = require('acf.object') -local class = object.class -local isinstance = object.isinstance -local super = object.super - -local pth = require('acf.path') -local map = require('acf.util').map - - -local stringy = require('stringy') - - -M.Reference = class(Field) - -function M.Reference:init(params) - if not params.widget then params.widget = 'reference' end - super(self, M.Reference):init(params) - self.dtype = 'reference' - if not self.scope then self.scope = '/' end -end - -function M.Reference:topology(context) - local res = super(self, M.Reference):topology(context) - res[1].scope = self.scope - return res -end - -function M.Reference:abs_scope(context) - return pth.to_absolute(self.scope, node.path(context.parent)) -end - -function M.Reference:meta(context) - local res = super(self, M.Reference):meta(context) - res.scope = self:abs_scope(context) - return res -end - -function M.Reference:follow(context, value) - return context.txn:fetch(pth.rawjoin(self:abs_scope(context), value)) -end - -function M.Reference:load(context, options) - local ref = super(self, M.Reference):load(context) - return ( - (not options or options.dereference ~= false) and context.txn and ref - ) and self:follow(context, ref) or ref -end - -function M.Reference:_validate(context, value) - super(self, M.Reference):_validate(context, value) - - if value == nil then return end - - if isinstance(value, node.TreeNode) then value = node.path(value) end - - local path = context.path - if type(value) ~= 'string' then raise(path, 'Path name must be string') end - - if pth.is_absolute(value) then - local scope = self:abs_scope(context) - local prefix = scope..'/' - if not stringy.startswith(value, prefix) then - raise(path, 'Reference out of scope ('..scope..')') - end - value = value:sub(prefix:len() + 1, -1) - end - - -- assume one-level ref for now - if #pth.split(value) > 1 then - raise(path, 'Subtree references not yet supported') - end - - -- TODO check instance type - M.error.relabel(path, self.follow, self, context, value) - - return value -end - -function M.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 - - -M.Model = fld.Model - - -M.Collection = class(fld.TreeNode) - -function M.Collection:init(params, itype) - if params.create == nil then params.create = true end - super(self, M.Collection):init(params) - - assert(self.type) - self.itype = itype or node.Collection - self.iparams = { - destroy=self.destroy, - layout=self.layout, - required=self.required, - ui_member=self.ui_member - } - - self.dtype = 'collection' -end - -function M.Collection:auto_ui_name(name) - if not name then return end - if name:sub(-1, -1) ~= 's' then name = name..'s' end - return super(self, M.Collection):auto_ui_name(name) -end - -function M.Collection:load(context, options) - if not self.iparams.field then self.iparams.field = to_field(self.type) end - return super(self, M.Collection):load(context, options) -end - - -M.List = class(M.Collection) -function M.List:init(params) super(self, M.List):init(params, node.List) end - - -M.Set = class(M.Collection) -function M.Set:init(params) - if not params.widget and isinstance(params.type, M.Reference) then - params.widget = 'checkboxes' - end - super(self, M.Set):init(params, M.node.Set) -end -function M.Set.save_member(tn, k, v) node.insert(tn, v) end - - --- experimental -M.Mixed = class(M.Collection) - -function M.Mixed:init(params) - params.type = M.Mixed - super(self, M.Mixed):init(params, node.Mixed) - self.pfield = Field() -end - -function M.Mixed:topology(context) return {} end - -function M.Mixed:load(context) - local value = self.pfield:load(context) - if type(value) == 'table' then return super(self, M.Mixed):load(context) end - return value -end - -function M.Mixed:save(context, value) - if type(value) == 'table' then super(self, M.Mixed):save(context, value) - else self.pfield:save(context, value) end -end - - -return M diff --git a/acf/model/model.lua b/acf/model/model.lua deleted file mode 100644 index 6878497..0000000 --- a/acf/model/model.lua +++ /dev/null @@ -1,204 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local raise = require('acf.error').raise - -local fld = require('acf.model.field') -local Field = fld.Field -local Member = fld.Member - -local node = require('acf.model.node') -local BoundMember = node.BoundMember - -local object = require('acf.object') -local class = object.class -local super = object.super -local isinstance = object.isinstance - -local pth = require('acf.path') -local util = require('acf.util') - - -local function to_member(obj, params) - if not params then params = {} end - if object.issubclass(obj, M.Model) then - params.model = obj - return fld.Model(params) - end - local res = getmetatable(obj).class and obj or obj(params) - assert(isinstance(res, Member)) - return res -end - -function M.to_field(obj, params) - local res = to_member(obj, params) - assert(isinstance(res, Field)) - return res -end - - -M.Action = class(Member) - -function M.Action:init(params) - super(self, M.Action):init(params) - - if not self.func then error('Function not defined for action') end - - if self.field then - assert(type(self.field) == 'table') - self.field = M.to_field(self.field) - self.field.addr = '/null/action' - end - - getmetatable(self).__newindex = function(t, k, v) - assert(k == 'name') - rawset(t, k, v) - if t.field then t.field.name = v end - end -end - -function M.Action:meta(context) - local res = super(self, M.Action):meta(context) - if self.field then res.arg = self.field:meta(context) end - return res -end - - -function M.new(base) - if not base then base = M.Model end - - local res = class(base) - res.members = base == node.TreeNode and {} or util.copy(base.members) - - local mt = util.copy(getmetatable(res)) - - function mt.__index(t, k) return base[k] end - - function mt.__newindex(t, k, v) - assert(v) - - local override = t[k] - if type(v) == 'table' then v = to_member(v) end - - rawset(t, k, v) - - if isinstance(v, Member) then - v.name = k - if not override then table.insert(t.members, k) end - end - end - - setmetatable(res, mt) - - if isinstance(base, M.Model) then util.setdefaults(res, base) end - - return res -end - -M.Model = M.new(node.TreeNode) - -function M.Model:init(context) - super(self, M.Model):init(context) - - local mt = getmetatable(self) - - function mt.member(name, loose, tpe) - local m = mt.class[name] - - if not tpe then tpe = Member end - if not isinstance(m, tpe) then m = nil end - - if m == nil then - if loose then return end - raise(mt.path, 'Does not exist: '..name) - end - - return BoundMember(self, name, m) - end - - local function _members(tpe) - local res = {} - for _, name in ipairs(self.members) do - local m = mt.member(name, true, tpe) - if m then table.insert(res, m) end - end - return res - end - - function mt.topology() - local res = {} - for _, f in ipairs(_members(Field)) do util.extend(res, f:topology()) end - return res - end - - function mt.load(k, options) - local v = mt.class[k] - local create = options and options.create - - if isinstance(v, Field) then - v = BoundMember(self, k, v) - if v.compute then return v:compute() end - return v:load{create=create} - end - - assert(mt.txn) - - if isinstance(v, M.Action) then - local f = v.field and BoundMember(self, k, v.field) - if create then return f and f:load{create=true} end - - return function(var) - if f then f:save(var) - elseif var ~= nil then - raise( - pth.join(mt.path, v.name), - 'Action does not accept an input argument' - ) - end - local res = v.func(self, f and f:load()) - if f then mt.txn:set(v.field.addr) end - return res - end - end - - return v - end - - if not mt.txn then return end - - - function mt.mmeta(name) return mt.member(name):meta() end - - function mt.save(k, v) return mt.member(k, false, Field):save(v) end - - local function tmeta(tpe) - return util.map(function(m) return m:meta() end, _members(tpe)) - end - - mt.meta.type = 'model' - mt.meta.fields = tmeta(Field) - mt.meta.actions = tmeta(M.Action) - - function mt.members() - return util.map(function(f) return f.name end, mt.meta.fields) - end - - function mt.validate() - for _, f in ipairs(_members(Field)) do - if not f.compute then f:validate_saved() end - end - end - - if self.has_permission then - function mt.has_permission(user, permission) - return self:has_permission(user, permission) - end - end -end - - -return M diff --git a/acf/model/net.lua b/acf/model/net.lua deleted file mode 100644 index ae82f1e..0000000 --- a/acf/model/net.lua +++ /dev/null @@ -1,96 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local raise = require('acf.error').raise -local Union = require('acf.model.combination').Union - -local fld = require('acf.model.field') -local String = fld.String - -local object = require('acf.object') -local class = object.class -local super = object.super - -local update = require('acf.util').update - - -local stringy = require('stringy') - - -M.IPv4Address = class(String) - -function M.IPv4Address:validate(context, value) - super(self, M.IPv4Address):validate(context, value) - local function test(...) - if #{...} ~= 4 then return true end - for _, octet in ipairs{...} do - if tonumber(octet) > 255 then return true end - end - end - if test(value:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then - raise(context.path, 'Invalid IPv4 address') - end -end - - -M.IPv6Address = class(String) - -function M.IPv6Address:validate(context, value) - super(self, M.IPv6Address):validate(context, value) - - local function invalid() raise(context.path, 'Invalid IPv6 address') end - - if value == '' then invalid() end - - local comps = stringy.split(value, ':') - if #comps < 3 then invalid() end - - local function collapse(i, ofs) - if comps[i] > '' then return end - if comps[i + ofs] > '' then invalid() end - table.remove(comps, i) - end - collapse(1, 1) - collapse(#comps, -1) - if #comps > 8 then invalid() end - - local short = false - for _, comp in ipairs(comps) do - if comp == '' then - if short then invalid() end - short = true - elseif not comp:match('^%x%x?%x?%x?$') then invalid() end - end - if ( - short and #comps == 3 and comps[2] == '' - ) or (not short and #comps < 8) then - invalid() - end -end - - -M.IPAddress = class(Union) - -function M.IPAddress:init(params) - super(self, M.IPAddress):init( - update( - params, - {types={M.IPv4Address, M.IPv6Address}, error='Invalid IP address'} - ) - ) -end - - -M.Port = class(fld.Integer) - -function M.Port:validate(context, value) - super(self, M.Port):validate(context, value) - if value < 0 or value > 65535 then raise(context.path, 'Invalid port') end -end - - -return M diff --git a/acf/model/node.lua b/acf/model/node.lua deleted file mode 100644 index 56d3416..0000000 --- a/acf/model/node.lua +++ /dev/null @@ -1,275 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -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') -local util = require('acf.util') - - -M.BoundMember = class() - -function M.BoundMember:init(parent, name, field) - local pmt = getmetatable(parent) - local mt = {} - - function mt.__index(t, k) - local member = field[k] - if type(member) ~= 'function' then return member end - return function(self, ...) - return member( - field, - { - txn=pmt.txn, - parent=parent, - path=pth.join(pmt.path, name), - addr=pth.to_absolute( - field.addr or pth.escape(name), pmt.addr - ) - }, - ... - ) - end - end - - setmetatable(self, mt) -end - - -M.TreeNode = class() - -function M.TreeNode:init(context) - local mt = getmetatable(self) - util.update(mt, context) - - mt.dereference = true - mt.meta = {} - function mt.get(k, create) return mt.load(k, {create=create}) end - - if not mt.txn then return end - - if mt.parent then - mt.meta['ui-name'] = getmetatable(mt.parent).mmeta( - pth.name(mt.path) - )['ui-name'] - end - - function mt.save(k, v) rawset(self, k, v) end - function mt.__index(t, k) return mt.get(k) end - function mt.__newindex(t, k, v) mt.save(k, v) end - - function mt.has_permission(user, permission) - local p = permission..mt.path - if mt.txn:fetch('/auth/permissions')[p] then - return user:check_permission(p) - end - - if ({create=true, delete=true})[permission] then - permission = 'modify' - end - return M.has_permission(mt.parent, user, permission) - end - - mt.txn.validable[mt.path] = mt.addr -end - -function M.TreeNode:fetch(path, create) - if type(path) == 'string' then path = pth.split(path) end - - if #path == 0 then return self end - - local mt = getmetatable(self) - local name = path[1] - if not mt.member(name) then - raise(mt.path, 'Member does not exist: '..name) - end - - local next = mt.get(name, create) - if next == nil and (not create or #path > 1) then - raise(mt.path, 'Subordinate does not exist: '..name) - end - - table.remove(path, 1) - if #path > 0 and type(next) ~= 'table' then - raise(pth.join(mt.path, name), 'Is a primitive value') - end - - return M.TreeNode.fetch(next, path, create) -end - -function M.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, M.TreeNode) and M.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 - - -M.Collection = class(M.TreeNode) - -function M.Collection:init(context, params) - super(self, M.Collection):init(context) - - self.init = nil - self.fetch = nil - self.search_refs = nil - - local mt = getmetatable(self) - local field = M.BoundMember(self, pth.wildcard, params.field) - - function mt.topology() return field:topology() end - - function mt.member(name) - return M.BoundMember(self, name, params.field) - end - - function mt.load(k, options) return mt.member(k):load(options) end - - if not mt.txn then return end - - mt.meta.type = 'collection' - mt.meta.members = field:meta() - mt.meta['ui-member'] = params.ui_member or mt.meta['ui-name']:gsub('s$', '') - mt.meta.widget = params.layout - - function mt.mmeta(name) - local res = util.copy(mt.meta.members) - if name ~= pth.wildcard then - res['ui-name'] = mt.meta['ui-member']..' '..name - end - return res - end - - function mt.members() return mt.txn:get(mt.addr) or {} end - - function mt.validate() - if #mt.members() > 0 then return end - if params.required then raise(mt.path, 'Collection cannot be empty') end - if params.destroy then - mt.txn:set(mt.addr) - validate(mt.parent) - end - end - - function mt.save(k, v) mt.member(k):save(v) end -end - - -M.List = class(M.Collection) - -function M.List:init(context, params) - super(self, M.List):init(context, params) - - local mt = getmetatable(self) - mt.meta.type = 'list' - - local save = mt.save - function mt.save(k, v) - assert(type(k) == 'number') - if v == nil then - local len = #mt.members() - while k < len do - mt.save(k, mt.load(k + 1, {dereference=false})) - k = k + 1 - end - end - save(k, v) - end - - function mt.insert(v, i) - local len = #mt.members() - if not i then i = len + 1 end - for j = len,i,-1 do mt.save(j + 1, mt.load(j, {dereference=false})) end - mt.save(i, v) - end -end - - --- experimental -M.Mixed = class(M.Collection) - -function M.Mixed:init(context, params) - super(self, M.Mixed):init(context, params) - - -- TODO dynamic meta: list non-leaf children - local mt = getmetatable(self) - mt.meta = {type='mixed', ['ui-name']=mt.path} - function mt.mmeta(name) - return {type='mixed', ['ui-name']=pth.join(mt.path, name)} - end -end - - -local function meta_func(attr) - return function(node, ...) - local res = getmetatable(node)[attr] - if type(res) == 'function' then return res(...) end - return res - end -end - -for _, mf in ipairs{ - 'addr', 'has_permission', 'insert', 'meta', 'mmeta', 'path', 'topology' -} do M[mf] = meta_func(mf) end - - -function M.pairs(tbl, dereference) - if not isinstance(tbl, M.TreeNode) then return pairs(tbl) end - - local mt = getmetatable(tbl) - if dereference == nil then dereference = mt.dereference end - - local res = {} - for _, member in ipairs(mt.members()) do - res[member] = mt.load(member, {dereference=dereference}) - end - return pairs(res) -end - -local function _ipairs(mt, i) - i = i + 1 - local v = mt.load(i) - if v == nil then return end - return i, v -end -function M.ipairs(tbl) - if not isinstance(tbl, M.TreeNode) then return ipairs(tbl) end - return _ipairs, getmetatable(tbl), 0 -end - - -return M diff --git a/acf/model/permission.lua b/acf/model/permission.lua deleted file mode 100644 index 271d478..0000000 --- a/acf/model/permission.lua +++ /dev/null @@ -1,22 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local insert = require('acf.model.node').insert -local start_txn = require('acf.transaction') - -function M.define(path, ...) - local txn = start_txn() - local db = txn:fetch('/auth/permissions') - for _, permission in ipairs{...} do insert(db, permission..path) end - txn:commit() -end - -function M.defaults(path) - M.define(path, 'read', 'create', 'modify', 'delete') -end - -return M diff --git a/acf/model/root.lua b/acf/model/root.lua deleted file mode 100644 index 17c5cfb..0000000 --- a/acf/model/root.lua +++ /dev/null @@ -1,91 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local model = require('acf.model.model') -local node = require('acf.model.node') -local object = require('acf.object') -local pth = require('acf.path') - -local util = require('acf.util') -local setdefault = util.setdefault - - -M.RootModel = model.new() - -function M.RootModel:init(txn) - object.super(self, M.RootModel):init{txn=txn, path='/', addr='/null/root'} -end - -function M.RootModel:has_permission(user, permission) - return permission == 'read' -end - -function M.RootModel:meta(path) - local obj = self:fetch(path, true) - if object.isinstance(obj, node.TreeNode) then return node.meta(obj) end - return node.mmeta(self:fetch(pth.parent(path), true), pth.name(path)) -end - - -local _topology = {} -local order = 0 - -function M.topology(addr, create) - local top = _topology - if type(addr) == 'table' then addr = util.copy(addr) - else addr = pth.split(addr) end - - local function defaults(top) - return util.setdefaults(top, {members={}, paths={}, referrers={}}) - end - - while #addr > 0 do - if create then - top = setdefault(defaults(top).members, addr[1], {}) - else - top = top.members[addr[1]] or top.members[pth.wildcard] - if not top then return end - end - table.remove(addr, 1) - end - - return defaults(top) -end - -function M.register(name, field, params) - if not params then params = {} end - params.create = true - M.RootModel[name] = model.to_field(field, params) - - local root = M.RootModel() - - for _, record in ipairs(node.topology(root:fetch(name))) do - local top = M.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 - local scope = node.addr( - root:fetch(pth.to_absolute(record.scope, pth.parent(record.path))) - ) - set('scope', scope) - table.insert(M.topology(scope, true).referrers, record.path) - end - end -end - - -return M diff --git a/acf/model/set.lua b/acf/model/set.lua deleted file mode 100644 index b5b8dc3..0000000 --- a/acf/model/set.lua +++ /dev/null @@ -1,47 +0,0 @@ ---[[ -Copyright (c) 2012-2013 Kaarle Ritvanen -See LICENSE file for license details ---]] - -local M = {} - -local TreeNode = require('acf.model.field').TreeNode -local npairs = require('acf.model.node').pairs -local object = require('acf.object') - - -M.Set = object.class(require('acf.model.node').List) - -function M.Set:init(context, params) - assert(not object.isinstance(params.field, TreeNode)) - object.super(self, M.Set):init(context, params) - - local function find(value) - for i, member in npairs(self) do - if member == value then return i end - end - end - - local mt = getmetatable(self) - mt.dereference = false - mt.meta.type = 'set' - - function mt.get(k, create) - local i = find(k) - if i then return mt.load(i) end - if create then return k end - end - - function mt.__newindex(t, k, v) - assert(v == nil) - local i = find(k) - if not i then return end - mt.save(i, nil) - end - - local insert = mt.insert - function mt.insert(v) if not find(v) then insert(v) end end -end - - -return M |