diff options
Diffstat (limited to 'acf/model')
-rw-r--r-- | acf/model/combination.lua | 47 | ||||
-rw-r--r-- | acf/model/field.lua | 61 | ||||
-rw-r--r-- | acf/model/init.lua | 94 | ||||
-rw-r--r-- | acf/model/net.lua | 88 |
4 files changed, 209 insertions, 81 deletions
diff --git a/acf/model/combination.lua b/acf/model/combination.lua new file mode 100644 index 0000000..7228233 --- /dev/null +++ b/acf/model/combination.lua @@ -0,0 +1,47 @@ +--[[ +Copyright (c) 2012-2013 Kaarle Ritvanen +See LICENSE file for license details +--]] + +module(..., package.seeall) + +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 + + +require 'stringy' + + +Range = class(String) + +function Range:init(params) + super(self, Range):init(params) + if not self.type then self.type = fld.Integer end +end + +function 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 + + +Union = class(String) + +function Union:validate(context, value) + super(self, 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 diff --git a/acf/model/field.lua b/acf/model/field.lua index ad26818..8927dfd 100644 --- a/acf/model/field.lua +++ b/acf/model/field.lua @@ -106,6 +106,67 @@ function Field:validate_saved(context) end +local Primitive = class(Field) + +function Primitive:validate(context, value) + local t = self.dtype + if type(value) ~= t then raise(context.path, 'Not a '..t) end +end + + +String = class(Primitive) + +function String:init(params) + super(self, String):init(params) + self.dtype = 'string' +end + +function String:validate(context, value) + super(self, String):validate(context, value) + if self['max-length'] and string.len(value) > self['max-length'] then + raise(context.path, 'Maximum length exceeded') + end +end + +function String:meta(context) + local res = super(self, String):meta(context) + res['max-length'] = self['max-length'] + return res +end + + +Number = class(Primitive) + +function Number:init(params) + super(self, Number):init(params) + self.dtype = 'number' +end + +function Number:_validate(context, value) + return super(self, Number):_validate( + context, + value and tonumber(value) or value + ) +end + + +Integer = class(Number) + +function Integer:validate(context, value) + super(self, Integer):validate(context, value) + if math.floor(value) ~= value then raise(context.path, 'Not an integer') end +end + + +Boolean = class(Primitive) + +function Boolean:init(params) + super(self, Boolean):init(params) + self.dtype = 'boolean' + self.widget = self.dtype +end + + TreeNode = class(Field) function TreeNode:load(context, create) diff --git a/acf/model/init.lua b/acf/model/init.lua index f325053..f1f127f 100644 --- a/acf/model/init.lua +++ b/acf/model/init.lua @@ -5,18 +5,28 @@ See LICENSE file for license details module(..., package.seeall) -error = require('acf.error') -local raise = error.raise -local relabel = error.relabel +err = require('acf.error') +local raise = err.raise +local relabel = err.relabel + +local combination = require('acf.model.combination') +Union = combination.Union +Range = combination.Range local fld = require('acf.model.field') local Field = fld.Field +Boolean = fld.Boolean +Integer = fld.Integer +Number = fld.Number +String = fld.String local model = require('acf.model.model') Action = model.Action new = model.new local to_field = model.to_field +net = require('acf.model.net') + node = require('acf.model.node') permission = require('acf.model.permission') register = require('acf.model.root').register @@ -33,84 +43,6 @@ local map = require('acf.util').map require 'stringy' --- TODO object-specific actions - - -local Primitive = class(Field) - -function Primitive:validate(context, value) - local t = self.dtype - if type(value) ~= t then raise(context.path, 'Not a '..t) end -end - - -String = class(Primitive) - -function String:init(params) - super(self, String):init(params) - self.dtype = 'string' -end - -function String:validate(context, value) - super(self, String):validate(context, value) - if self['max-length'] and string.len(value) > self['max-length'] then - raise(context.path, 'Maximum length exceeded') - end -end - -function String:meta(context) - local res = super(self, String):meta(context) - res['max-length'] = self['max-length'] - return res -end - - -Number = class(Primitive) - -function Number:init(params) - super(self, Number):init(params) - self.dtype = 'number' -end - -function Number:_validate(context, value) - return super(self, Number):_validate( - context, - value and tonumber(value) or value - ) -end - - -Integer = class(Number) - -function Integer:validate(context, value) - super(self, Integer):validate(context, value) - if math.floor(value) ~= value then raise(context.path, 'Not an integer') end -end - - -Boolean = class(Primitive) - -function Boolean:init(params) - super(self, Boolean):init(params) - self.dtype = 'boolean' - self.widget = self.dtype -end - - -Range = class(String) - -function Range:init(params) - super(self, Range):init(params) - if not self.type then self.type = Integer end -end - -function 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 - - Reference = class(Field) function Reference:init(params) diff --git a/acf/model/net.lua b/acf/model/net.lua new file mode 100644 index 0000000..725c543 --- /dev/null +++ b/acf/model/net.lua @@ -0,0 +1,88 @@ +--[[ +Copyright (c) 2012-2013 Kaarle Ritvanen +See LICENSE file for license details +--]] + +module(..., package.seeall) + +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 + + +require 'stringy' + + +IPv4Address = class(String) + +function IPv4Address:validate(context, value) + super(self, IPv4Address):validate(context, value) + local function test(...) + if #arg ~= 4 then return true end + for _, octet in ipairs(arg) do + if tonumber(octet) > 255 then return true end + end + end + if test(string.match(value, '^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then + raise(context.path, 'Invalid IPv4 address') + end +end + + +IPv6Address = class(String) + +function IPv6Address:validate(context, value) + super(self, 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 string.match(comp, '^%x%x?%x?%x?$') then invalid() end + end + if not short and #comps < 8 then invalid() end +end + + +IPAddress = class(Union) + +function IPAddress:init(params) + super(self, IPAddress):init( + update( + params, {types={IPv4Address, IPv6Address}, error='Invalid IP address'} + ) + ) +end + + +Port = class(fld.Integer) + +function Port:validate(context, value) + super(self, Port):validate(context, value) + if value < 0 or value > 65535 then raise(context.path, 'Invalid port') end +end |