diff options
Diffstat (limited to 'acf/model/field.lua')
-rw-r--r-- | acf/model/field.lua | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/acf/model/field.lua b/acf/model/field.lua new file mode 100644 index 0000000..cfa14b9 --- /dev/null +++ b/acf/model/field.lua @@ -0,0 +1,120 @@ +--[[ +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 |