├── .gitignore ├── README.md ├── include └── validaterl.hrl ├── rebar.test.config └── src ├── validaterl.app.src └── validaterl.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | ebin 3 | deps 4 | erl_crash.dump 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Validaterl 2 | ========== 3 | 4 | Validaterl is a simple Erlang library for validating (input) data. 5 | 6 | Validation Primitives 7 | --------------------- 8 | 9 | Using records defined in `validaterl/include/validaterl.hrl` one can check individual values against 10 | validators. 11 | 12 | For example: 13 | 14 | ```erlang 15 | > validaterl:validate(1, #range{ to = 10 }) 16 | true 17 | > validaterl:validate(1, #range{ to = 0 }) 18 | greater 19 | ```` 20 | 21 | Also, if the second argument is not a validator, it will be matched against the first argument: 22 | 23 | ```erlang 24 | > validaterl:validate(1, 1). 25 | true 26 | > validaterl:validate(1, 2). 27 | lesser 28 | > validaterl:validate(1, 0). 29 | greater 30 | ``` 31 | 32 | Custom Validators 33 | ----------------- 34 | 35 | One can define custom validators using this layout: 36 | 37 | ```erlang 38 | -record(my_validator, { 39 | '$validator' = fun mymodule:myvalidator/2, 40 | ... %% rest of arguments 41 | }). 42 | ``` 43 | 44 | 45 | Validation Sheets 46 | ----------------- 47 | 48 | Instead of running individual validations, you can define so called "validation sheets" and test them using 49 | `validaterl:validate/1`. 50 | 51 | Validation sheet is a list of validations in the following format: 52 | 53 | ```erlang 54 | {Name :: any(), Value :: any(), Spec :: spec()} 55 | ``` 56 | 57 | For example: 58 | 59 | ```erlang 60 | [ 61 | {'user.name', Username, #length{ is = #range{ from = 3, to = 16 } }}, 62 | {'user.email', Email, #length{ is = #range { from = 3, to = 255 } }}, 63 | {'user.age', Age, #numericality{ allow_string = true }} 64 | ] 65 | ``` 66 | 67 | Just as an example, if you try to put a string with a non-numeric value into Age, you'll get this: 68 | 69 | ```erlang 70 | [{'user.age',"wrong", 71 | #numericality{'$validator' = #Fun, 72 | allow_undefined = false,allow_null = false, 73 | allow_string = true,allow_empty = false,allow_rest = false, 74 | allow_float = true,default = 0}, 75 | number_expected}] 76 | ``` -------------------------------------------------------------------------------- /include/validaterl.hrl: -------------------------------------------------------------------------------- 1 | -define(DEFAULT_VALIDATE, fun validaterl:validate/2). 2 | 3 | -record(numericality, 4 | { 5 | '$validator' = ?DEFAULT_VALIDATE, 6 | allow_undefined = false :: boolean(), 7 | allow_null = false :: boolean(), 8 | allow_string = false :: boolean(), 9 | allow_empty = false :: boolean(), 10 | allow_rest = false :: boolean(), 11 | allow_float = true :: boolean(), 12 | default = 0 :: number() 13 | }). 14 | 15 | -record(range, 16 | { 17 | '$validator' = ?DEFAULT_VALIDATE, 18 | from :: undefined | any(), 19 | to :: undefined | any(), 20 | exclusive = false :: boolean() 21 | }). 22 | 23 | -record(format, 24 | { 25 | '$validator' = ?DEFAULT_VALIDATE, 26 | allow_undefined = false :: boolean(), 27 | allow_null = false :: boolean(), 28 | allow_empty = false :: boolean(), 29 | re = ".*" :: string(), 30 | default = "" :: string() 31 | }). 32 | 33 | -record(length, 34 | { 35 | '$validator' = ?DEFAULT_VALIDATE, 36 | is :: any() 37 | }). 38 | 39 | -record(type, 40 | { 41 | '$validator' = ?DEFAULT_VALIDATE, 42 | is :: any() 43 | }). 44 | -------------------------------------------------------------------------------- /rebar.test.config: -------------------------------------------------------------------------------- 1 | {deps, [ 2 | {proper, ".*", {git, "https://github.com/manopapad/proper", {branch, "master"}}} 3 | ]}. 4 | -------------------------------------------------------------------------------- /src/validaterl.app.src: -------------------------------------------------------------------------------- 1 | {application, validaterl, 2 | [ 3 | {description, "Validators"}, 4 | {vsn, git}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib 9 | ]}, 10 | {env, []} 11 | ]}. 12 | -------------------------------------------------------------------------------- /src/validaterl.erl: -------------------------------------------------------------------------------- 1 | -module(validaterl). 2 | -export([validate/1, validate/2]). 3 | -include_lib("validaterl/include/validaterl.hrl"). 4 | -ifdef(TEST). 5 | -include_lib("proper/include/proper.hrl"). 6 | -include_lib("eunit/include/eunit.hrl"). 7 | -endif. 8 | -define(FAILED(X), X). 9 | 10 | -type spec() :: #numericality{} | #range{} | #format{}. 11 | -type name() :: any(). 12 | -type report() :: any(). 13 | -type plan() :: {name(), any(), spec()}. 14 | -type error() :: {name(), any(), spec(), report()}. 15 | 16 | -spec validate(plan()) -> true | list(error()). 17 | validate(Plan) -> 18 | lists:filter(fun({_, _, _, true}) -> 19 | false; 20 | (_) -> 21 | true 22 | end, 23 | [ {Name, Value, Spec, validate(Value, Spec)} || 24 | {Name, Value, Spec} <- Plan ]). 25 | 26 | 27 | %% numericality 28 | validate(undefined, #numericality{ 29 | allow_undefined = false 30 | }) -> 31 | ?FAILED(undefined_not_allowed); 32 | validate(undefined, #numericality{ 33 | allow_undefined = true, 34 | default = Default 35 | } = V) -> 36 | validate(Default, V); 37 | validate(null, #numericality{ 38 | allow_null = false 39 | }) -> 40 | ?FAILED(null_not_allowed); 41 | validate(null, #numericality{ 42 | allow_null = true, 43 | default = Default 44 | } = V) -> 45 | validate(Default, V); 46 | validate(Empty, #numericality{ 47 | allow_string = true, 48 | allow_empty = false 49 | }) when Empty == [] orelse Empty == <<>> -> 50 | ?FAILED(empty_not_allowed); 51 | validate(Empty, #numericality{ 52 | allow_string = true, 53 | allow_empty = true, 54 | default = Default 55 | } = V) when Empty == [] orelse Empty == <<>> -> 56 | validate(Default, V); 57 | validate(S, #numericality{ 58 | allow_string = false 59 | }) when is_list(S) orelse is_binary(S) -> 60 | ?FAILED(string_not_allowed); 61 | validate(S, #numericality{ 62 | allow_string = true, 63 | allow_rest = Rest 64 | } = V) when is_list(S) orelse is_binary(S) -> 65 | Str = to_string(S), 66 | case string:to_integer(Str) of 67 | {error, no_integer} -> 68 | ?FAILED(number_expected); 69 | {Value, []} -> 70 | validate(Value, V); 71 | {Value, _} -> 72 | case string:to_float(Str) of 73 | {error, _Error} -> 74 | if Rest -> 75 | validate(Value, V); 76 | true -> 77 | ?FAILED(rest_not_allowed) 78 | end; 79 | {ValueF, []} -> 80 | validate(ValueF, V); 81 | {ValueF, _} -> 82 | if Rest -> 83 | validate(ValueF, V); 84 | true -> 85 | ?FAILED(rest_not_allowed) 86 | end 87 | end 88 | end; 89 | validate(V, #numericality{ allow_float = false }) when is_float(V) -> 90 | ?FAILED(float_not_allowed); 91 | validate(V, #numericality{}) when is_integer(V) orelse is_float(V) -> 92 | true; 93 | validate(_, #numericality{}) -> 94 | ?FAILED(number_expected); 95 | 96 | validate(_, #range{ from = undefined, 97 | to = undefined }) -> 98 | true; 99 | validate(V, #range{ from = From, 100 | to = undefined, 101 | exclusive = false}) when From /= undefined andalso 102 | V < From -> 103 | ?FAILED(lesser); 104 | validate(V, #range{ from = From, 105 | to = undefined, 106 | exclusive = true}) when From /= undefined andalso 107 | V =< From -> 108 | ?FAILED(lesser); 109 | validate(V, #range{ from = undefined, 110 | to = To, 111 | exclusive = false}) when To /= undefined andalso 112 | V > To -> 113 | ?FAILED(greater); 114 | validate(V, #range{ from = undefined, 115 | to = To, 116 | exclusive = true}) when To /= undefined andalso 117 | V >= To -> 118 | ?FAILED(greater); 119 | validate(V, #range{ from = _From, 120 | to = To, 121 | exclusive = false}) when To /= undefined andalso 122 | V > To -> 123 | ?FAILED(greater); 124 | validate(V, #range{ from = From, 125 | to = _To, 126 | exclusive = false}) when From /= undefined andalso 127 | V < From -> 128 | ?FAILED(lesser); 129 | validate(V, #range{ from = From, 130 | to = _To, 131 | exclusive = true}) when From /= undefined andalso 132 | V =< From -> 133 | ?FAILED(lesser); 134 | validate(V, #range{ from = _From, 135 | to = To, 136 | exclusive = false}) when To /= undefined andalso 137 | V > To -> 138 | ?FAILED(greater); 139 | validate(V, #range{ from = _From, 140 | to = To, 141 | exclusive = true}) when To /= undefined andalso 142 | V >= To -> 143 | ?FAILED(greater); 144 | validate(_, #range{}) -> 145 | true; 146 | 147 | validate(undefined, #format{ 148 | allow_undefined = false 149 | }) -> 150 | ?FAILED(undefined_not_allowed); 151 | validate(undefined, #format{ 152 | allow_undefined = true, 153 | default = Default 154 | } = V) -> 155 | validate(Default, V); 156 | validate(null, #format{ 157 | allow_null = false 158 | }) -> 159 | ?FAILED(null_not_allowed); 160 | validate(null, #format{ 161 | allow_null = true, 162 | default = Default 163 | } = V) -> 164 | validate(Default, V); 165 | validate(Empty, #format{ 166 | allow_empty = false 167 | }) when Empty == [] orelse Empty == <<>> -> 168 | ?FAILED(empty_not_allowed); 169 | validate(Str, #format{ 170 | re = Re 171 | }) when is_list(Str) orelse is_binary(Str) -> 172 | case re:run(Str, Re) of 173 | nomatch -> 174 | ?FAILED(no_match); 175 | {match, _} -> 176 | true; 177 | {error, Error} -> 178 | ?FAILED(Error) 179 | end; 180 | validate(_, #format{}) -> 181 | ?FAILED(string_expected); 182 | 183 | validate(L, #length{ is = Validator }) when is_list(L) -> 184 | validate(length(L), Validator); 185 | 186 | validate(L, #length{ is = Validator }) when is_binary(L) orelse is_tuple(L) -> 187 | validate(size(L), Validator); 188 | 189 | validate(_, #length{ is = Validator }) -> 190 | validate(0, Validator); 191 | 192 | validate(A, #type{ is = number }) when is_number(A) -> 193 | true; 194 | validate(A, #type{ is = boolean }) when A == true orelse A == false -> 195 | true; 196 | validate(A, #type{ is = atom }) when is_atom(A) -> 197 | true; 198 | validate(A, #type{ is = binary }) when is_binary(A) -> 199 | true; 200 | validate(A, #type{ is = reference }) when is_reference(A) -> 201 | true; 202 | validate(A, #type{ is = function }) when is_function(A) -> 203 | true; 204 | validate(A, #type{ is = port }) when is_port(A) -> 205 | true; 206 | validate(A, #type{ is = pid }) when is_pid(A) -> 207 | true; 208 | validate(A, #type{ is = tuple }) when is_tuple(A) -> 209 | true; 210 | validate(A, #type{ is = list }) when is_list(A) -> 211 | true; 212 | validate(_, #type{}) -> 213 | false; 214 | 215 | validate(A, A) -> %% equality validator 216 | true; 217 | 218 | %% custom validators, the convention is that the first 219 | %% record element (2nd tuple element) has to be a reference 220 | %% to a validation fun 221 | validate(V, T) when is_tuple(T) andalso size(T) > 1 andalso is_function(element(2, T)) -> 222 | (element(2, T))(V, T); 223 | 224 | 225 | validate(A, B) when A < B -> 226 | ?FAILED(lesser); 227 | validate(A,B) when A > B -> 228 | ?FAILED(greater). 229 | 230 | 231 | 232 | to_string(S) when is_list(S) -> 233 | S; 234 | to_string(S) when is_binary(S) -> 235 | binary_to_list(S). 236 | 237 | 238 | -ifdef(TEST). 239 | 240 | numericality_test_() -> 241 | [fun numericality_test_undefined/0, 242 | fun numericality_test_null/0, 243 | fun numericality_test_empty/0, 244 | fun numericality_test_string/0, 245 | fun numericality_test_allow_float/0, 246 | fun numericality_test_default/0 247 | ]. 248 | 249 | numericality_test_undefined() -> 250 | ?assertEqual(?FAILED(undefined_not_allowed), validate(undefined, #numericality{})), 251 | ?assertEqual(?FAILED(undefined_not_allowed), validate(undefined, #numericality{ allow_undefined = false })), 252 | ?assert(validate(undefined, #numericality{ allow_undefined = true })). 253 | 254 | numericality_test_null() -> 255 | ?assertEqual(?FAILED(null_not_allowed), validate(null, #numericality{})), 256 | ?assertEqual(?FAILED(null_not_allowed), validate(null, #numericality{ allow_null = false })), 257 | ?assert(validate(null, #numericality{ allow_null = true })). 258 | 259 | numericality_test_empty() -> 260 | ?assertEqual(?FAILED(string_not_allowed), validate("", #numericality{ allow_empty = true })), 261 | ?assertEqual(?FAILED(string_not_allowed), validate(<<>>, #numericality{ allow_empty = true })), 262 | ?assertEqual(true, validate("", #numericality{ allow_empty = true, allow_string = true })), 263 | ?assertEqual(true, validate(<<>>, #numericality{ allow_empty = true, allow_string = true })), 264 | ?assertEqual(?FAILED(empty_not_allowed), validate("", #numericality{ allow_empty = false, allow_string = true })), 265 | ?assertEqual(?FAILED(empty_not_allowed), validate(<<>>, #numericality{ allow_empty = false, allow_string = true })), 266 | ?assertEqual(?FAILED(empty_not_allowed), validate("", #numericality{ allow_string = true })), 267 | ?assertEqual(?FAILED(empty_not_allowed), validate(<<>>, #numericality{ allow_string = true })). 268 | 269 | numericality_test_string() -> 270 | ?assertEqual(true, validate("1", #numericality{ allow_string = true })), 271 | ?assertEqual(true, validate(<<"1">>, #numericality{ allow_string = true })), 272 | ?assertEqual(true, validate("1a", #numericality{ allow_string = true, allow_rest = true })), 273 | ?assertEqual(true, validate(<<"1a">>, #numericality{ allow_string = true, allow_rest = true })), 274 | ?assertEqual(true, validate("1.1", #numericality{ allow_string = true })), 275 | ?assertEqual(true, validate(<<"1.1">>, #numericality{ allow_string = true })), 276 | ?assertEqual(true, validate("1.1a", #numericality{ allow_string = true, allow_rest = true })), 277 | ?assertEqual(true, validate(<<"1.1a">>, #numericality{ allow_string = true, allow_rest = true })), 278 | ?assertEqual(?FAILED(number_expected), validate("garbage", #numericality{ allow_string = true })), 279 | ?assertEqual(?FAILED(number_expected), validate(<<"garbage">>, #numericality{ allow_string = true })). 280 | 281 | numericality_test_allow_float() -> 282 | ?assertEqual(true, validate(1.1, #numericality{})), 283 | ?assertEqual(?FAILED(float_not_allowed), validate(1.1, #numericality{ allow_float = false})). 284 | 285 | numericality_test_default() -> 286 | ?assertEqual(true, validate(undefined, #numericality{ allow_undefined = true })), 287 | ?assertEqual(true, validate(null, #numericality{ allow_null = true })), 288 | ?assertEqual(true, validate("", #numericality{ allow_string = true, allow_empty = true })), 289 | ?assertEqual(true, validate(<<>>, #numericality{ allow_string = true, allow_empty = true })), 290 | 291 | ?assertEqual(true, validate(undefined, #numericality{ allow_undefined = true, default = 1 })), 292 | ?assertEqual(true, validate(null, #numericality{ allow_null = true, default = 1 })), 293 | ?assertEqual(true, validate("", #numericality{ allow_string = true, allow_empty = true, default = 1 })), 294 | ?assertEqual(true, validate(<<>>, #numericality{ allow_string = true, allow_empty = true, default = 1 })), 295 | 296 | ?assertEqual(?FAILED(number_expected), validate(undefined, #numericality{ allow_undefined = true, default = x })), 297 | ?assertEqual(?FAILED(number_expected), validate(null, #numericality{ allow_null = true, default = x })), 298 | ?assertEqual(?FAILED(number_expected), validate("", #numericality{ allow_string = true, allow_empty = true, default = x })), 299 | ?assertEqual(?FAILED(number_expected), validate(<<>>, #numericality{ allow_string = true, allow_empty = true, default = x })). 300 | 301 | qc(T) -> 302 | ?assertEqual(true, proper:quickcheck(T, 303 | [ 304 | {numtests, 1000}, 305 | {on_output, fun(String, Format) -> 306 | io:format(user, String, Format) 307 | end}])). 308 | 309 | 310 | range_test_() -> 311 | [{"include", 312 | fun() -> qc(range_test_inclusive_prop()) end}, 313 | {"exclude", 314 | fun() -> qc(range_test_exclusive_prop()) end}]. 315 | 316 | 317 | range() -> 318 | ?SUCHTHAT({from, F, to, T}, 319 | {from, oneof([undefined, integer()]), 320 | to, oneof([undefined, integer()])}, 321 | if F == undefined -> 322 | true; 323 | T == undefined -> 324 | true; 325 | true -> 326 | F =< T 327 | end). 328 | 329 | range_test_inclusive_prop() -> 330 | ?FORALL({{from, From, to, To}, Random}, {range(), 331 | integer()}, 332 | begin 333 | Validate = validate(Random, #range{ from = From, 334 | to = To }), 335 | if From /= undefined andalso Random < From -> 336 | Validate == ?FAILED(lesser); 337 | To /= undefined andalso Random > To -> 338 | Validate == ?FAILED(greater); 339 | From /= undefined andalso To /= undefined 340 | andalso Random >= From andalso Random =< To -> 341 | Validate == true; 342 | From == undefined andalso To /= undefined 343 | andalso Random =< To -> 344 | Validate == true; 345 | To == undefined andalso From /= undefined 346 | andalso Random >= From -> 347 | Validate == true; 348 | From == undefined andalso To == undefined -> 349 | Validate == true; 350 | Random < From -> 351 | Validate == ?FAILED(lesser); 352 | Random > To -> 353 | Validate == ?FAILED(greater); 354 | true -> 355 | false 356 | end 357 | end). 358 | 359 | range_test_exclusive_prop() -> 360 | ?FORALL({{from, From, to, To}, Random}, {range(), 361 | integer()}, 362 | begin 363 | Validate = validate(Random, #range{ from = From, 364 | to = To, 365 | exclusive = true}), 366 | if 367 | From /= undefined andalso Random =< From 368 | andalso From == To -> 369 | (Validate == ?FAILED(lesser)) orelse 370 | (Validate == ?FAILED(greater)); 371 | From /= undefined andalso Random =< From -> 372 | Validate == ?FAILED(lesser); 373 | To /= undefined andalso Random >= To 374 | andalso From == To -> 375 | (Validate == ?FAILED(lesser)) orelse 376 | (Validate == ?FAILED(greater)); 377 | To /= undefined andalso Random >= To -> 378 | Validate == ?FAILED(greater); 379 | From /= undefined andalso To /= undefined 380 | andalso Random > From andalso Random < To -> 381 | Validate == true; 382 | From == undefined andalso To /= undefined 383 | andalso Random < To -> 384 | Validate == true; 385 | To == undefined andalso From /= undefined 386 | andalso Random > From -> 387 | Validate == true; 388 | From == undefined andalso To == undefined -> 389 | Validate == true; 390 | Random =< From -> 391 | Validate == ?FAILED(lesser); 392 | Random >= To -> 393 | Validate == ?FAILED(greater); 394 | true -> 395 | false 396 | end 397 | end). 398 | 399 | format_test_() -> 400 | [fun format_test_undefined/0, 401 | fun format_test_null/0, 402 | fun format_test_empty/0, 403 | fun format_test_string/0, 404 | fun format_test_default/0 405 | ]. 406 | 407 | format_test_undefined() -> 408 | ?assertEqual(?FAILED(undefined_not_allowed), validate(undefined, #format{})), 409 | ?assertEqual(?FAILED(undefined_not_allowed), validate(undefined, #format{ allow_undefined = false })), 410 | ?assert(validate(undefined, #format{ allow_undefined = true, allow_empty = true })). 411 | 412 | format_test_null() -> 413 | ?assertEqual(?FAILED(null_not_allowed), validate(null, #format{})), 414 | ?assertEqual(?FAILED(null_not_allowed), validate(null, #format{ allow_null = false })), 415 | ?assert(validate(null, #format{ allow_null = true, allow_empty = true })). 416 | 417 | format_test_empty() -> 418 | ?assertEqual(true, validate("", #format{ allow_empty = true })), 419 | ?assertEqual(true, validate(<<>>, #format{ allow_empty = true })), 420 | ?assertEqual(?FAILED(empty_not_allowed), validate("", #format{ allow_empty = false })), 421 | ?assertEqual(?FAILED(empty_not_allowed), validate(<<>>, #format{ allow_empty = false })), 422 | ?assertEqual(?FAILED(empty_not_allowed), validate("", #format{})), 423 | ?assertEqual(?FAILED(empty_not_allowed), validate(<<>>, #format{})). 424 | 425 | format_test_string() -> 426 | ?assertEqual(true, validate("1", #format{ re = "^1$" })), 427 | ?assertEqual(?FAILED(no_match), validate("2", #format{ re = "^1$" })). 428 | 429 | format_test_default() -> 430 | ?assertEqual(true, validate(undefined, #format{ allow_undefined = true, allow_empty = true })), 431 | ?assertEqual(true, validate(null, #format{ allow_null = true, allow_empty = true })), 432 | ?assertEqual(true, validate("", #format{ allow_empty = true })), 433 | ?assertEqual(true, validate(<<>>, #format{allow_empty = true })), 434 | 435 | ?assertEqual(?FAILED(string_expected), validate(undefined, #format{ allow_undefined = true, default = x })), 436 | ?assertEqual(?FAILED(string_expected), validate(null, #format{ allow_null = true, default = x })). 437 | 438 | 439 | custom_validator_test() -> 440 | Validator = fun(_, _) -> 441 | true 442 | end, 443 | ?assert(validate(x,{my_validator, Validator})). 444 | 445 | equality_test() -> 446 | ?assert(validate(1,1)), 447 | ?assertEqual(?FAILED(lesser), validate(1,2)). 448 | 449 | comparison_test() -> 450 | ?assertEqual(?FAILED(lesser), validate(1, 2)), 451 | ?assertEqual(?FAILED(greater), validate(2, 1)). 452 | 453 | length_test() -> 454 | ?assert(validate(atom, #length{ is = 0})), 455 | Tests = [{"a", [{1, true},{2, lesser}, {0, greater}, {#range{ from = 0, to = 2 }, true}]}, 456 | {<<"a">>, [{1, true},{2, lesser}, {0, greater}, {#range{ from = 0, to = 2}, true}]}, 457 | {{a}, [{1, true},{2, lesser}, {0, greater}], {#range{ from = 0, to = 2}, true}}], 458 | 459 | [ 460 | begin 461 | ?assertEqual(?FAILED(Outcome), validate(Data, #length{ is = Is })) 462 | end || {Data, Cases} <- Tests, 463 | {Is, Outcome} <- Cases ]. 464 | 465 | type_test() -> 466 | ?assert(validate(1, #type{ is = number })), 467 | ?assert(validate(1.1, #type{ is = number })), 468 | ?assert(validate(atom, #type{ is = atom })), 469 | ?assert(validate(<<>>, #type{ is = binary })), 470 | ?assert(validate(make_ref(), #type{ is = reference })), 471 | ?assert(validate(fun type_test/0, #type{ is = function })), 472 | ?assert(validate(hd(erlang:ports()), #type{ is = port })), 473 | ?assert(validate(self(), #type{ is = pid })), 474 | ?assert(validate({}, #type{ is = tuple })), 475 | ?assert(validate([], #type{ is = list })), 476 | ?assert(validate(true, #type{ is = boolean })), 477 | ?assert(validate(false, #type{ is = boolean })). 478 | 479 | 480 | 481 | 482 | -endif. 483 | --------------------------------------------------------------------------------