summaryrefslogtreecommitdiffstats
path: root/acf/model
diff options
context:
space:
mode:
Diffstat (limited to 'acf/model')
-rw-r--r--acf/model/combination.lua47
-rw-r--r--acf/model/field.lua61
-rw-r--r--acf/model/init.lua94
-rw-r--r--acf/model/net.lua88
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