{"id":15996237,"url":"https://github.com/zkat/cl-form","last_synced_at":"2026-01-31T08:31:26.303Z","repository":{"id":65998138,"uuid":"2659479","full_name":"zkat/cl-form","owner":"zkat","description":"Generic form validation utility for CL","archived":false,"fork":false,"pushed_at":"2015-01-13T08:50:49.000Z","size":128,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-25T07:09:16.366Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zkat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-10-27T16:20:23.000Z","updated_at":"2022-02-04T15:25:28.000Z","dependencies_parsed_at":"2023-02-19T20:55:24.385Z","dependency_job_id":null,"html_url":"https://github.com/zkat/cl-form","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zkat/cl-form","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fcl-form","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fcl-form/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fcl-form/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fcl-form/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zkat","download_url":"https://codeload.github.com/zkat/cl-form/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zkat%2Fcl-form/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28935401,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T07:49:44.436Z","status":"ssl_error","status_checked_at":"2026-01-31T07:49:34.274Z","response_time":128,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-08T07:40:54.638Z","updated_at":"2026-01-31T08:31:26.291Z","avatar_url":"https://github.com/zkat.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Introduction\n\ncl-form is a presentation-agnostic utility meant to simplify the process of validating web form\nparameters, transforming the strings received into more useful Lisp values, and reporting errors, as\nwell as providing values that web forms can be repopulated with.\n\n    (defun always-error (value)\n      (check-field nil \"No matter what VALUE is, this will error.\")\n      value)\n\n    (defun as-integer (value \u0026aux *read-eval*)\n      (check-field (every #'digit-char-p value) \"~A is not a valid integer.\" value)\n      ;; Validators must return the final, intended field value.\n      (parse-integer value))\n\n    (defun field-length (value \u0026key min-length max-length)\n      (when min-length\n        (check-field (\u003e= (length value) min-length) \"Field must be at least ~A characters long.\" min-length))\n      (when max-length\n        (check-field (\u003c= (length value) max-length) \"Field must be ~A characters long or shorter.\" max-length))\n      value)\n\n    (deform test-form ()\n      ((:whatever #'field-length :min-length 1)\n       (:error-field 'always-error)\n       (:int-field 'as-integer)\n       ((:my-list list) (lambda (val)\n                          (mapcar #'as-integer val)))\n       ((:my-array array) (lambda (val)\n                            (remove nil val)))))\n\n    (let ((form (make-form 'test-form '((\"whatever\" . \"a\")\n                                        (\"error-field\" . \"b\")\n                                        (\"int-field\" . \"42\")\n                                        (\"my-array[5]\" . \"5\")\n                                        (\"my-array[0]\" . \"0\")\n                                        (\"my-list\" . \"1\")\n                                        (\"my-list\" . \"2\")))))\n      (assert (and (form-valid-p form)\n                   (string= \"a\" (field-value form :whatever))\n                   (null (field-value form :error-field))\n                   (eql 42 (field-value form :int-field))\n                   (equal '(1 2) (field-value form :my-list))\n                   (equalp #(\"0\" \"5\") (field-value form :my-array)))))\n\n# Defining\n\nDefining a form is a two-step process:\n\n  1. Define validator functions. These functions should use `check-field` as assertions, and return a possibly-transformed value to be used as the field-value.\n  2. Write a `deform` form that associates field names with these validators.\n\n*[function]* `check-field test error-format \u0026rest error-format-orgs`\n\n  Used like an assertion. If TEST is NIL, validation will exit at this point (not evaluating\n  subsequent forms in the validator function). ERROR-FORMAT and ERROR-FORMAT-ARGS will then be used\n  to generate an error message to be returned by `field-error`\n  \n*[macro]* `deform name nil field-definitions`\n\n  The syntax for FIELD-DEFINITIONS is: `(field-name validator-function \u0026rest validator-args)`.\n  \n  VALIDATOR-FUNCTION can be any function that receives one argument (which will be the 'raw' value\n  for the field -- in most cases a plain string). This function should return a validater, processed\n  value to be returned by `field-value`.\n  \n  Additionally, VALIDATOR-ARGS can be provided to each individual field. These will be applied,\n  after the raw field value, to the validator function. To clarify, (:field-name #'list 1 2 3) will\n  result in (\u003craw-value\u003e 1 2 3) being returned by the 'validator'.\n\n  FIELD-NAME can be either a symbol (which names the field), or a literal list containing a symbol\n  and one of the symbols `LIST` or `ARRAY`.\n  \n  If the second item in FIELD-NAME is the symbol `LIST`, then all items in the binding alist\n  provided to `make-form` that share this field's name will be collected into a list, which will\n  become the raw value of the field. This list is also what the validator for this field will\n  receive, instead of a simple string.\n\n  If the second item in FIELD-NAME is the symbol `ARRAY`, then the binding alist will be searched\n  for keys matching `\"\u003cfield-name\u003e[\u003cinteger\u003e]\"`, and an array will be created containing those items\n  at the provided indices. The array will be as long as the maximum `\u003cinteger\u003e` value + 1. Any\n  missing indices before this maximum item will be NIL. As with `LIST` fields, the array itself will\n  be the raw-value of the field, and that array will be passed to the validator function.\n\n*[special variable]* `*form*`\n\n  When a validator function is called, this variable is bound to the current form object. At this\n  point, the form object's raw values are already populated, and are accessible through\n  `field-raw-value`. Both `field-error` and `field-value` will return NIL for all fields.\n\n# Usage\n\nIn order to actually use defined \n\n*[function]* `make-form name \u0026optional binding-alist`\n\n  Creates an instance of a form named by NAME. If BINDING-ALIST is provided, the form object's\n  fields are immediately populated and all validators are called.\n  \n  The keys in BINDING-ALIST are bound case-insensitively according to the symbolic field names\n  defined in the form's `deform`.\n  \n  If no BINDING-ALIST is provided, the form is considered 'unbound. `field-value`,\n  `field-raw-value`, and `field-error` can still be called, but will always return NIL. This can be\n  useful for rendering functions that use form objects.\n  \n*[function]* `form-valid-p form`\n\n  Returns NIL if any of the form's validators fail, or if the form is unbound.\n\n*[function]* `form-errors form`\n\n  For bound forms, returns an alist of `(field-name-symbol . \"Error Message\")` if any fields failed\n  validation.\n  \n*[function]* `field-raw-value form field-name`\n\n  For bound forms, returns the 'raw' value for a field. For non-list non-array fields, this will\n  always be a string bound according to the field's name, or NIL. For list and array fields, this\n  will be a list and an array of strings, respectively. Missing values for array fields will be\n  populated with NIL.\n\n*[function]* `field-value form field-name`\n\n  For bound forms, returns the validated, possibly transformed value for this field. The specific\n  value returned by this will be the value returned by the field's validator function.\n\n*[function]* `field-error form field-name`\n\n  Returns NIL if the field successfully validated, or a string containing an error message if\n  validation for the field failed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fcl-form","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzkat%2Fcl-form","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzkat%2Fcl-form/lists"}