{"id":20237743,"url":"https://github.com/ragmaanir/kontrol","last_synced_at":"2026-05-10T01:12:23.418Z","repository":{"id":136218054,"uuid":"111131713","full_name":"Ragmaanir/kontrol","owner":"Ragmaanir","description":"Kontrol is a DSL to define validations for JSON data","archived":false,"fork":false,"pushed_at":"2018-08-17T12:27:29.000Z","size":33,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-14T01:12:29.038Z","etag":null,"topics":["crystal","dsl","json","validation"],"latest_commit_sha":null,"homepage":null,"language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ragmaanir.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-17T17:26:40.000Z","updated_at":"2018-08-17T12:27:19.000Z","dependencies_parsed_at":"2023-12-08T00:45:09.170Z","dependency_job_id":null,"html_url":"https://github.com/Ragmaanir/kontrol","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ragmaanir%2Fkontrol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ragmaanir%2Fkontrol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ragmaanir%2Fkontrol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ragmaanir%2Fkontrol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ragmaanir","download_url":"https://codeload.github.com/Ragmaanir/kontrol/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241685138,"owners_count":20003021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["crystal","dsl","json","validation"],"created_at":"2024-11-14T08:28:39.397Z","updated_at":"2026-05-10T01:12:23.387Z","avatar_url":"https://github.com/Ragmaanir.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kontrol [![Build Status](https://travis-ci.org/Ragmaanir/kontrol.svg?branch=master)](https://travis-ci.org/Ragmaanir/kontrol)[![Dependency Status](https://shards.rocks/badge/github/ragmaanir/kontrol/status.svg)](https://shards.rocks/github/ragmaanir/kontrol)\n\n### Version: 0.2.3\n\nKontrol is a DSL to define validations for JSON data.\n\n## Installation\n\nAdd this to your application's `shard.yml`:\n\n```yaml\ndependencies:\n  kontrol:\n    github: ragmaanir/kontrol\n```\n\n## Usage\n\n```crystal\nrequire \"kontrol\"\n```\n\n```crystal\n# To define a validation you can use the `Kontrol.object` macro.\n# Validations can be defined by expressions:\n\nval, _ = Kontrol.object(\n  name: {type: String, length: v.size \u003e 4},\n)\n\n# When the constraint `length` is violated it returns:\n\nj = json(name: \"a\")\nassert val.call(j) == {\"name\" =\u003e [:length]}\n\n# The value of the property `name` can be accessed via `v` in the validation expression.\n\n# For the type validation there are two shortcuts since they are so common:\n\n# Shortcut 1\nKontrol.object(\n  name: {type: String},\n)\n\n# Shortcut 2\nKontrol.object(\n  name: String,\n)\n\n# You always have to specify the type (might change).\n\n# You can also define root-level validations for an object:\n\nval, _ = Kontrol.object(\n  {length: v[\"name\"].as_s.size == v[\"name_length\"].as_i},\n  name: String,\n  name_length: Int64\n)\n\n# The `length` validation checks whether the length of the name string matches the name_length.\n# The root-level validations are only executed when all properties are valid to prevent\n# them raising exceptions caused by invalid data. A root level validation error\n# is stored under the key \"@\", because the object might not have a name. So in this case:\nj = json(\n  name: \"test\",\n  name_length: 3\n)\n\nassert val.call(j) == {\n  \"@\" =\u003e [:length],\n}\n\n```\n\nSimple example:\n\n```crystal\n# First object is the validator, second is the converter that can be used\n# to convert the JSON to nested named tuples.\nval, con = Kontrol.object(\n  name: String,\n  percentage: {type: Int64, min: v \u003e 0, max: v \u003c= 100}\n)\n\n# invalid since percentage violates :min constraint\nassert val.call(json(\n  name: \"test\",\n  percentage: -1\n)) == {\"percentage\" =\u003e [:min]}\n\n# invalid since name violates :type-constraint and :min is violated\nassert val.call(json(\n  name: 2,\n  percentage: -1\n)) == {\"name\" =\u003e [:type], \"percentage\" =\u003e [:min]}\n\n# invalid since the required attributes are missing or nil\nassert val.call(json(\n  name: nil\n)) == {\"name\" =\u003e [:required], \"percentage\" =\u003e [:required]}\n\n# valid\nassert val.call(json(\n  name: \"test\",\n  percentage: 45\n)).empty?\n\n# convert input to nested named tuples\nassert con.call(json(\n  name: \"test\",\n  percentage: 45\n)) == {name: \"test\", percentage: 45}\n\n```\n\nAdvanced example (nested objects and root-level-validations):\n\n```crystal\nval, _ = object(\n  {\n    my_book: v[\"author\"].as_s == v[\"book\"][\"author\"].as_s,\n  },\n  author: String,\n  book: object(\n    author: String\n  )\n)\n\nassert val.call(json(\n  author: \"Bob\"\n)) == {\"book\" =\u003e [:required]}\n\nassert val.call(json(\n  author: \"Bob\",\n  book: {\n    author: 1337,\n  }\n)) == {\"book.author\" =\u003e [:type]}\n\nassert val.call(json(\n  author: \"Bob\",\n  book: {\n    author: \"Bobby\",\n  }\n)) == {\"@\" =\u003e [:my_book]}\n\nassert val.call(json(\n  author: \"Bob\",\n  book: {\n    author: \"Bob\",\n  }\n)).empty?\n\n```\n\n## TODOs\n\n- [ ] Support arrays\n- [ ] Support/test required/optional attributes\n- [ ] Consistent handling of unspecified attributes (reject? ignore? errors?)\n- [ ] Cleanup/simplify macros\n- [ ] Use rule-class instances instead of closures?\n\n\n## Contributing\n\n1. Fork it ( https://github.com/ragmaanir/kontrol/fork )\n2. Create your feature branch (git checkout -b my-new-feature)\n3. Commit your changes (git commit -am 'Add some feature')\n4. Push to the branch (git push origin my-new-feature)\n5. Create a new Pull Request\n\n## Contributors\n\n- [ragmaanir](https://github.com/ragmaanir) - creator, maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fragmaanir%2Fkontrol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fragmaanir%2Fkontrol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fragmaanir%2Fkontrol/lists"}