summaryrefslogtreecommitdiffstats
path: root/acf/model
diff options
context:
space:
mode:
Diffstat (limited to 'acf/model')
-rw-r--r--acf/model/aaa.lua87
-rw-r--r--acf/model/combination.lua50
-rw-r--r--acf/model/field.lua241
-rw-r--r--acf/model/init.lua205
-rw-r--r--acf/model/model.lua204
-rw-r--r--acf/model/net.lua96
-rw-r--r--acf/model/node.lua275
-rw-r--r--acf/model/permission.lua22
-rw-r--r--acf/model/root.lua91
-rw-r--r--acf/model/set.lua47
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