aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md69
-rw-r--r--valua-0.2.rockspec22
-rw-r--r--valua-test.lua62
-rw-r--r--valua.lua217
4 files changed, 367 insertions, 3 deletions
diff --git a/README.md b/README.md
index bb12aac..c7d2109 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,67 @@
-valua
-=====
+##Valua - Validation for Lua
-A module for making chained validations in Lua. Create your objects, append your tests, use and reuse it!
+A module for making chained validations. Create your objects, append your tests, use and reuse it!
+
+Originally bundled with Sailor MVC Web Framework, now released as a separated module.
+ https://github.com/Etiene/sailor
+
+This module provides tools for validating values, very useful in forms, but also usable elsewhere. It works in appended chains. Create a new validation object and start chaining your test functions. If your value fails a test, it breaks the chain and does not evaluate the rest of it. It returns a boolean and an error string (nil when tests succeeded)
+
+####Usage
+Example 1 - Just create, chain and use:
+```lua
+valua:new().type("string").len(3,5)("test string!") -- false, "should have 3-5 characters"
+```
+Example 2 - Create, chain and later use it multiple times:
+```lua
+local reusable_validation = valua:new().type("string").len(3,5)
+reusable_validation("test string!") -- false, "should have 3-5 characters"
+reusable_validation("test!") -- true
+```
+
+####Current validation functions
+
+ * alnum() -
+Checks if string is alfanumeric.
+ * boolean() -
+Checks if value is a boolean.
+ * compare(another_value) -
+Checks if value is equal to another value.
+ * contains(substr) -
+Check if a string contains a substring.
+ * date() or date(format) -
+Checks if a string is a valid date. Default format is UK (dd/mm/yyyy). Also checks for US and ISO formats.
+ * email() -
+Checks if a string is a valid email address.
+ * empty() -
+Checks if a value is empty.
+ * integer() -
+Checks if a number is an integer;
+ * in_list(list) -
+Checks if a value is inside an array.
+ * len(min,max) -
+Checks if a string's length is between min and max.
+ * match(pattern) -
+Checks if a string matches a given pattern.
+ * max(n) -
+Checks if a number is equal or less than n.
+ * min(n) -
+Checks if a number is equal or greater than n.
+ * not_empty() -
+Checks if a value is not empty.
+ * no_white() -
+Checks if a string contains no white spaces.
+ * number() -
+Checks if a value is a number.
+ * string() -
+Checks if a value is a string.
+ * type(t) -
+Checks if a value is of type t.
+
+
+
+Copyright (c) 2014 Etiene Dalcol
+
+http://etiene.net
+
+License: MIT
diff --git a/valua-0.2.rockspec b/valua-0.2.rockspec
new file mode 100644
index 0000000..8423994
--- /dev/null
+++ b/valua-0.2.rockspec
@@ -0,0 +1,22 @@
+package = "Valua"
+version = "0.2"
+source = {
+ url = "https://github.com/Etiene/valua"
+}
+description = {
+ summary = "Validation for Lua!",
+ detailed = [[
+ This module provides tools for validating values, very useful in forms, but also usable elsewhere. It works in appended chains. Create a new validation object and start chaining your test functions.
+ ]],
+ homepage = "https://github.com/Etiene/valua",
+ license = "MIT"
+}
+dependencies = {
+ "lua >= 5.1, < 5.3"
+}
+build = {
+ type = "builtin",
+ modules = {
+ ["valua"] = "valua.lua",
+ }
+} \ No newline at end of file
diff --git a/valua-test.lua b/valua-test.lua
new file mode 100644
index 0000000..1d53ecf
--- /dev/null
+++ b/valua-test.lua
@@ -0,0 +1,62 @@
+local v = require "valua"
+
+local function check(val_test, test_value, expected, n)
+ local res,err = val_test(test_value)
+ local msg = "Validation "..n.." "
+
+ if res == expected then
+ msg = msg.. "succeeded"
+ else
+ msg = msg.. "FAILED"
+ end
+ msg = msg.." on '"..(tostring(test_value)).."'. Expected: "..tostring(expected)..", result: "..tostring(res)..". "
+ print(msg)
+ if err then print("\tTest Msg: value "..(err or "")) end
+end
+
+local test_values = {
+ "test string!",
+ "hey",
+ "",
+ nil,
+ true,
+ 42,
+ 1337,
+ '26/10/1980',
+ '10-26-1980',
+ '29.02.2014',
+ '29/02/2016',
+ 'a@a.com',
+ 'asd123',
+ 5.7
+}
+
+local tests = {
+ {v:new().type("string").len(3,5),{1,false}},
+ {v:new().type("number").len(3,5), {1,false}},
+ {v:new().not_empty(),{2,true,3,false,4,false}},
+ {v:new().len(2,10),{2,true}},
+ {v:new().type("number"),{2,false}},
+ {v:new().empty(),{3,true,4,true}},
+ {v:new().boolean(),{1,false,5,true}},
+ {v:new().compare("hey"),{1,false,2,true}},
+ {v:new().number().min(45),{2,false,6,false,7,true}},
+ {v:new().number().max(1009),{7,false,6,true}},
+ {v:new().date(),{9,false,10,false,11,true,8,true}},
+ {v:new().date('us'),{8,false,9,true}},
+ {v:new().email(),{13,false,12,true}},
+ {v:new().in_list({"hey",42}),{12,false,6,true,2,true}},
+ {v:new().match("^%d+%p%d+%p%d%d%d%d$"),{1,false,8,true}},
+ {v:new().alnum(),{8,false,13,true}},
+ {v:new().integer(),{14,false,6,true}},
+ {v:new().string(),{14,false,1,true}},
+ {v:new().string().alnum(),{6,false}},
+ {v:new().contains(" "),{2,false,1,true}},
+ {v:new().no_white(),{1,false,2,true}}
+}
+
+for n,t in ipairs(tests) do
+ for i = 1, #t[2], 2 do
+ check(t[1],test_values[t[2][i]],t[2][i+1],n)
+ end
+end
diff --git a/valua.lua b/valua.lua
new file mode 100644
index 0000000..03fb4c8
--- /dev/null
+++ b/valua.lua
@@ -0,0 +1,217 @@
+-- Valua 0.2
+-- Copyright (c) 2014 Etiene Dalcol
+-- License: MIT
+--
+-- Originally bundled with Sailor MVC Web Framework, now released as a separated module.
+-- http://sailorproject.org
+--
+-- This module provides tools for validating values, very useful in forms, but also usable elsewhere.
+-- It works in appended chains. Create a new validation object and start chaining your test functions.
+-- If your value fails a test, it breaks the chain and does not evaluate the rest of it.
+-- It returns a boolean and an error string (nil when tests succeeded)
+--
+-- Example 1 - Just create, chain and use:
+-- valua:new().type("string").len(3,5)("test string!") -- false, "should have 3-5 characters"
+--
+-- Example 2 - Create, chain and later use it multiple times:
+-- local reusable_validation = valua:new().type("string").len(3,5)
+-- reusable_validation("test string!") -- false, "should have 3-5 characters"
+-- reusable_validation("test!") -- true
+--
+
+local valua = {}
+
+local tinsert,setmetatable,len,match,tonumber = table.insert,setmetatable,string.len,string.match,tonumber
+
+-- CORE
+-- Caution, this is confusing
+
+-- creates a new validation object, useful for reusable stuff and creating many validation tests at a time
+function valua:new(obj)
+ obj = obj or {}
+ setmetatable(obj,self)
+ -- __index will be called always when chaining validation functions
+ self.__index = function(t,k)
+ --saves a function named _<index> with its args in a funcs table, to be used later when validating
+ return function(...)
+ local args = {...}
+ local f = function(value) return valua['_'..k](value,unpack(args)) end
+ tinsert(t.funcs,f)
+ return t
+ end
+ end
+
+ -- __call will run only when the value is validated
+ self.__call = function(t,value)
+ local res = true
+ local fres, err
+ -- iterates through all chained validations funcs that were packed, passing the value to be validated
+ for i,f in ipairs(t.funcs) do
+ fres,err = f(value)
+ res = res and fres
+ -- breaks the chain if a test fails
+ if err then
+ break
+ end
+ end
+ -- boolean, error message or nil
+ return res,err
+ end
+ obj.funcs = {}
+ return obj
+end
+--
+
+-- VALIDATION FUNCS
+-- Add new funcs at will, they all should have the value to be validated as first parameter
+-- and their names must be preceded by '_'.
+-- For example, if you want to use .custom_val(42) on your validation chain, you need to create a
+-- function valua._custom_val(<value var>,<other var>). Just remember the value var will be known
+-- at the end of the chain and the other var, in this case, will receive '42'. You can add multiple other vars.
+-- These functions can be called directly (valua._len("test",2,5))in a non-chained and isolated way of life
+-- for quick stuff, but chaining is much cooler!
+-- Return false,'<error message>' if the value fails the test and simply true if it succeeds.
+
+-- aux funcs
+local function empty(v)
+ return not v or len(v) == 0
+end
+--
+
+-- String
+function valua._empty(value)
+ if not empty(value) then return false,"must be empty" end
+ return true
+end
+
+function valua._not_empty(value)
+ if empty(value) then return false,"must not be empty" end
+ return true
+end
+
+function valua._len(value,min,max)
+ local len = len(value or '')
+ if len < min or len >max then return false,"should have "..min.."-"..max.." characters" end
+ return true
+end
+
+function valua._compare(value,another_value)
+ if value ~= another_value then return false, "values are not equal" end
+ return true
+end
+
+function valua._email(value)
+ if not value:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w+") then
+ return false, "is not a valid email address"
+ end
+ return true
+end
+
+function valua._match(value,pattern)
+ if not value:match(pattern) then return false, "does not match pattern" end
+ return true
+end
+
+function valua._alnum(value)
+ if value:match("%W") then return false, "constains improper characters" end
+ return true
+end
+
+function valua._contains(value,substr)
+ if not value:find(substr) then return false, "does not contain '"..substr.."'" end
+ return true
+end
+
+function valua._no_white(value)
+ if value:find("%s") then return false, "must not contain white spaces" end
+ return true
+end
+--
+
+-- Numbers
+function valua._min(value,n)
+ if value < n then return false,"must be greater than "..n end
+ return true
+end
+
+function valua._max(value,n)
+ if value > n then return false,"must not be greater than "..n end
+ return true
+end
+
+function valua._integer(value)
+ if math.floor(value) ~= value then return false, "must be an integer" end
+ return true
+end
+--
+
+-- Date
+
+-- Check for a UK date pattern dd/mm/yyyy , dd-mm-yyyy, dd.mm.yyyy
+-- or US pattern mm/dd/yyyy, mm-dd-yyyy, mm.dd.yyyy
+-- or ISO pattern yyyy/mm/dd, yyyy-mm-dd, yyyy.mm.dd
+-- Default is UK
+function valua._date(value,format)
+ local valid = true
+ if (match(value, "^%d+%p%d+%p%d%d%d%d$")) then
+ local d, m, y
+ if format and format:lower() == 'us' then
+ m, d, y = match(value, "(%d+)%p(%d+)%p(%d+)")
+ elseif format and format:lower() == 'iso' then
+ y, m, d = match(value, "(%d+)%p(%d+)%p(%d+)")
+ else
+ d, m, y = match(value, "(%d+)%p(%d+)%p(%d+)")
+ end
+ d, m, y = tonumber(d), tonumber(m), tonumber(y)
+
+ local dm2 = d*m*m
+ if d>31 or m>12 or dm2==116 or dm2==120 or dm2==124 or dm2==496 or dm2==1116 or dm2==2511 or dm2==3751 then
+ -- invalid unless leap year
+ if not (dm2==116 and (y%400 == 0 or (y%100 ~= 0 and y%4 == 0))) then
+ valid = false
+ end
+ end
+ else
+ valid = false
+ end
+ if not valid then return false, "is not a valid date" end
+ return true
+end
+--
+
+-- Abstract
+function valua._type(value,value_type)
+ if type(value) ~= value_type then return false,"must be a "..value_type end
+ return true
+end
+
+function valua._boolean(value)
+ if type(value) ~= 'boolean' then return false,"must be a boolean" end
+ return true
+end
+
+function valua._number(value)
+ if type(value) ~= 'number' then return false,"must be a number" end
+ return true
+end
+
+function valua._string(value)
+ if type(value) ~= 'string' then return false,"must be a string" end
+ return true
+end
+
+function valua._in_list(value,list)
+ local valid = false
+ for _,v in ipairs(list) do
+ if value == v then
+ valid = true
+ break
+ end
+ end
+ if not valid then return false,"is not in the list" end
+ return true
+end
+--
+
+--
+return valua