Skip to content

daninus14/mito-validate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mito-validate

Validation System for mito ORM.

The purpose of this sytem is to make it easier to integrate validation with mito by ensuring validation happens before an insert to the DB and by providing a convenient place to define the validations.

Right now this makes it very easy to add validation based on types and functions to slots, and with a function to the whole object.

TODO: We are planning on adding inferred type validation as well as a macro for ease of defining the class level settings.

Please note GitHub is not rendering TODO items in this org file. The inferred and macro sections are not finished.

NOTE: This is not a data validation library. Rather this is a convenient way to integrate validation with mito objects, and ensure they are validated before a DB call is made. For data validation libraries, take a look at the cliki and the awesome-cl list of projects. Data validation libraries are meant to be used together with this system for ease of use. They are not exclusive of each other.

Getting Started

Getting the code

Right now the source code is hosted here: https://github.com/daninus14/mito-validate

Please check in the future for quicklisp and osicl availability.

Note that because of an implementation error in mito signaling an error unnecessarily, computing effective slots is not possible, so until they merge our PR, please use our up to date (as of 2024/10/31) repository for mito here https://github.com/daninus14/mito

Basic Usage

First, load the system into the lisp image.

(ql:quickload "mito-validate")

Then simply add the mito-validate-metaclass metaclass to the class definition of the DB table.

The keys :validation-type and :validation-function can be used at each slot definition, and the key :validation-function can be used at the class level definition to provide slot level and object level validation respectively.

Examples

Example with defclass

Here’s a simple example:

(defclass customer ()
  ;; only people 18 and older can make purchases
  ((age
    :type :integer
    :col-type (or :null :integer)
    :validation-type (integer 18 )) 
   ;; function below checkes input has only alphabetical characters and is not empty
   (name
    :type :text
    :col-type (or :null :text)
    :validation-function
    (lambda (x)
      (unless
          (cl-ppcre:all-matches-as-strings "^[a-zA-Z ]+$"
                                           x)
        (error "name can only have letters and space!"))))
                                        ; this will automatically check in CL that data is an integer
   (favorite-number
    :type :integer
    :col-type (or :null :integer)))
  (:metaclass mito-validate:mito-validate-metaclass))

=> #<MITO-VALIDATE-METACLASS MITO-VALIDATE::CUSTOMER>
MITO-VALIDATE> (mito:insert-dao (make-instance 'customer :age 16 :name "Phil" :favorite-number 24))
;; Debugger entered on #<TYPE-ERROR expected-type: (INTEGER 18) datum: 16>
[1] MITO-VALIDATE> 
;; Evaluation aborted on #<TYPE-ERROR expected-type: (INTEGER 18) datum: 16>
MITO-VALIDATE> (mito:insert-dao (make-instance 'customer :age 20 :name "Phil18" :favorite-number 24))
;; Debugger entered on #<SIMPLE-ERROR "name can only have letters and space!" {10050BD403}>
[1] MITO-VALIDATE> 
;; Evaluation aborted on #<SIMPLE-ERROR "name can only have letters and space!" {10050BD403}>
MITO-VALIDATE>   (mito:insert-dao (make-instance 'customer :age 20 :name "Phil" :favorite-number "24"))
#<CUSTOMER {1004F654F3}>

This will add automatic Common Lisp validation before sending the request to the DataBase. See the reference below for more details.

See the example below for an object level validation example, and the manual for more details.

Example with the deftablev Macro for Object Level Validation

Here’s an example with the deftablev macro, which is basically a defclass under the hood with the addition of adding the object level validation.

(deftablev c4 ()
  ((items
    :accessor items
    :col-type (or :null :integer))
   (price
    :accessor price
    :col-type (or :null :integer)))
  (:validation-function (lambda (x)
                          (when (< 10 (* (price x)
                                         (items x)))
                            (error "Purchase total cannot exceed 10!"))))
  (:documentation "This is a sample table"))

The above is the equivalent of:

(defclass c4 ()
  ((items
    :accessor items
    :col-type (or :null :integer))
   (price
    :accessor price
    :col-type (or :null :integer))))


(setf (validation-function (find-class c4))
      (lambda (x)
        (when (< 10 (* (price x)
                       (items x)))
          (error "Purchase total cannot exceed 10!")))))

Manual

Convenience Macro for Validation Definitions

The following macro makes it easier to define all the slot level and class level validations, as well as defining a mito table.

(defmacro deftablev (class-name superclasses slot-definitions class-validations &rest options))

Note that options must be after the validation functions.

In addition, the following macro should be helpful for defining object level validations outside of the class definition:

(defmacro set-validation (validation-key validation-value class-name))

Functionality

The way mito-validate works is by providing two types of validation:

  • Slot level validation
  • Object level validation

Any validation can be skipped by adding the appropriate keyword to the metaclass or slot definition.

Validations will be triggered when (mito:insert-dao) or (mito:save-dao) are called.

Slot Level Validation

DB Type Derivied Validation

NOTE: THIS IS NOT YET IMPLEMENTED mito-validate will try to make a validation type based on the provided mito type of the slot. Please note that the validation will be based on CL types.

This functionality is disabled by default.

To infer the validation type on a slot, add to the slot definition :infer-validation T.

To infer the validation type for all the slots on a class, whenever there is no other validation in that slot, apply :infer-validation T to the class itself.

Type Congruence

Here is a list of the mito SQL types and the Common Lisp types that will be used to validate them

list here mito types, and what CL types I'm using to validate the data.

Validation Type Assertion

A type can be provided to any slot with the key :validation-type in the slot definition.

mito-validate will signal an error unless the type of the data fits the provided type as follows:

(error
 'type-error
 :expected-type (validation-type-slot-value slot)
 :datum (slot-value obj
                    (closer-mop:slot-definition-name slot)))

Validation Function

A validation function can be provided to any slot with the key :validation-function in the slot definition.

mito-validate will simply evaluate the function passing it the slot data as the sole argument.

The function must therefore fit the following function:

(lambda (x))

The function should signal an error condition in case the data is invalid; otherwise the data will be assumed to be valid.

Any returned values are ignored.

Object Level Validation

A validation function which will receive the object as its input can be provided in the class definition with the key :validation-function in the metaclass.

The function takes in only one argument, which is the object itself.

(lambda (x))

The function should signal a condition in case the data is invalid; otherwise the data will be assumed to be valid.

Any returned values are ignored.

Here’s an example:

(defclass purchase ()
  ((items
    :accessor items
    :col-type (or :null :integer))
   (price
    :accessor price
    :col-type (or :null :integer)))
  (:metaclass mito-validate-metaclass))

MITO-VALIDATE> (mito:insert-dao (make-instance 'purchase :items 3 :price 4))
#<PURCHASE {100422EAD3}>
MITO-VALIDATE> (price *)
4 (3 bits, #x4, #o4, #b100)
MITO-VALIDATE> (setf (validation-function (find-class 'purchase))
                     (lambda (x)
                       (when (< 10 (* (price x)
                                      (items x)))
                         (error "Purchase total cannot exceed 10!"))))
#<FUNCTION (LAMBDA (X)) {B8011D273B}>
MITO-VALIDATE> (mito:insert-dao (make-instance 'purchase :items 3 :price 4))
                                        ; Debugger entered on #<SIMPLE-ERROR "Purchase total cannot exceed 10!" {1006ECDB93}>
[1] MITO-VALIDATE> 
                                        ; Evaluation aborted on #<SIMPLE-ERROR "Purchase total cannot exceed 10!" {1006ECDB93}>

Skipping Validation

Skipping A Slot Level Validation

By providing the key :skip-validation in the slot definition, the slot level validation will be skipped.

This will skip all validations, whether they be DB Derived Validations, or provided type or function validations.

Skip All Validations

By providing :skip-validation in the class definition, all validations will be skipped even if explicitly declared.

Here’s an example of skipping all class level validations:

MITO-VALIDATE> (skip-validation (find-class 'c2))
NIL
MITO-VALIDATE> (setf (skip-validation (find-class 'c2)) T)
T
MITO-VALIDATE> (mito:insert-dao (make-instance 'c2 :name "ron" :email "[email protected]" :age-claimed 17))
#<C2 {100410B213}>
MITO-VALIDATE> (setf (skip-validation (find-class 'c2)) NIL)
NIL
MITO-VALIDATE> (mito:insert-dao (make-instance 'c2 :name "ron" :email "[email protected]" :age-claimed 17))
; Debugger entered on #<TYPE-ERROR expected-type: (INTEGER 18) datum: 17> ; ; ; ; ; ; ; ; ;
[1] MITO-VALIDATE> 
; Evaluation aborted on #<TYPE-ERROR expected-type: (INTEGER 18) datum: 17> ; ; ; ; ; ; ; ; ;

Skipping All Slot Level Validation

By providing the key :skip-slot-validations in the class definition, all the slot level validations will be skipped.

See above “Skip All Validation” for an example of setting the class level properties.

Skipping Object Level Validation

By providing the key :skip-object-validation in the class definition, the object level validation will be skipped.

See above “Skip All Validation” for an example of setting the class level properties.

TODO Items

Add Inferred Validation Implementation

About

Validation System for mito ORM

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published