--[[ Copyright (c) 2012 Kaarle Ritvanen See LICENSE file for license details --]] module(..., package.seeall) local node = require('acf.model.node') local object = require('acf.object') local class = object.class local super = object.super local map = require('acf.util').map local function contains(list, value) for k, v in ipairs(list) do if v == value then return true end end return false end local function auto_ui_name(name) if not name then return end return string.gsub(string.upper(string.sub(name, 1, 1))..string.sub(name, 2), '_', ' ') end Field = class() function Field:init(params) for k, v in pairs(params or {}) do if not self[k] then self[k] = v end end if self.choice and not self['ui-choice'] then self['ui-choice'] = map(auto_ui_name, self.choice) end if not self.widget then self.widget = self.choice and 'combobox' or 'field' end end function Field:meta(txn, path, addr) assert(self.dtype) return { name=self.name, type=self.dtype, required=self.required, choice=self.choice, description=self.description, ['ui-name']=self['ui-name'] or auto_ui_name(self.name), widget=self.widget, ['ui-choice']=self['ui-choice'] } end function Field:load(txn, path, addr) local value = txn:get(addr) if value == nil then return self.default end return value end function Field:_validate(txn, path, value) if self.required and value == nil then error('Required value not set: '..path) end if self.choice and value ~= nil and not contains(self.choice, value) then error('Invalid value for '..path..': '..value) end if value ~= nil then self:validate(txn, path, value) end return value end function Field:validate(txn, path, value) end function Field:save(txn, path, addr, value) -- 2nd argument currenly not much used by backends txn:set(addr, self.dtype, self:_validate(txn, path, value)) end function Field:validate_saved(txn, path, addr) self:save(txn, path, addr, self:load(txn, path, addr)) end TreeNode = class(Field) function TreeNode:save(txn, path, addr, value) -- TODO hack, allow preserving old instance on parent update if value == path then return end if object.isinstance(value, node.TreeNode) then -- TODO clone if TreeNode has wrong path assert(node.path(value) == path) return end txn:set(addr) if value then assert(type(value) == 'table') txn:set(addr, 'table') local new = self:load(txn, path, addr, true) for k, v in pairs(value) do new[k] = v end end end Model = class(TreeNode) function Model:init(params) super(self, Model):init(params) assert(self.model) self.dtype = 'model' self.widget = self.dtype end function Model:load(txn, path, addr, create) if not create and not txn:get(addr) then return end return self.model(txn, path, addr) end