{"id":22196669,"url":"https://github.com/druids/garm","last_synced_at":"2025-09-20T21:33:09.535Z","repository":{"id":62432785,"uuid":"160668357","full_name":"druids/garm","owner":"druids","description":null,"archived":false,"fork":false,"pushed_at":"2019-05-31T08:36:32.000Z","size":49,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-30T02:14:13.730Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","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/druids.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-06T11:57:45.000Z","updated_at":"2019-05-31T08:36:34.000Z","dependencies_parsed_at":"2022-11-01T21:01:26.671Z","dependency_job_id":null,"html_url":"https://github.com/druids/garm","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/druids%2Fgarm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/druids%2Fgarm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/druids%2Fgarm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/druids%2Fgarm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/druids","download_url":"https://codeload.github.com/druids/garm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245366207,"owners_count":20603438,"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":[],"created_at":"2024-12-02T14:16:01.775Z","updated_at":"2025-09-20T21:33:04.481Z","avatar_url":"https://github.com/druids.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"Garm\n====\n\nGarm is a library that decorates Clojure Specs for better understanding.\n\n[![CircleCI](https://circleci.com/gh/druids/garm.svg?style=svg)](https://circleci.com/gh/druids/garm)\n[![Dependencies Status](https://jarkeeper.com/druids/garm/status.png)](https://jarkeeper.com/druids/garm)\n[![License](https://img.shields.io/badge/MIT-Clause-blue.svg)](https://opensource.org/licenses/MIT)\n\n\nLeiningen/Boot\n--------------\n\n```clojure\n[garm \"0.8.0\"]\n```\n\n\nDocumentation\n-------------\n\n### validate\n\n```clojure\n(require '[clojure.spec.alpha :as s]\n         '[garm.core :as garm]\n         '[garm.specs :as specs]))\n\n;; let's define specs\n(s/def ::id specs/uuid)\n(s/def ::price (s/and specs/decimal specs/pos))\n(s/def ::status (specs/enum #{:dispatched :received}))\n\n(s/def ::order (s/keys :req-un [::id ::price ::status]))\n\n;; `validate` function iterates over explained-data and returns a result as\n;; a tuple of `[data errors]`\n;; if data are valid, the `errors` will be `nil` and vice versa\n(garm/validate ::order {:price -1, :status :foo})\n\n;; the result\n[nil {:id [{:args []\n            :id ::specs/must-be-uuid\n            :message \"Must be a UUID\"}]\n      :price [{:args []\n               :id ::specs/must-be-decimal\n               :message \"Must be a decimal\"}]\n      :status [{:args []\n                :id :garm.core/missing-key\n                :message \"This field is required\"}]}]\n\n;; with valid data\n(garm/validate ::order {:status :dispatched\n                        :price 1000\n                        :id #uuid \"0428a077-96fd-4050-870e-ed1449266142\"})\n[{:status :dispatched\n  :price 1000\n  :id #uuid \"0428a077-96fd-4050-870e-ed1449266142\"}\n nil]\n```\n\nNote:\n\nWhen an error is in a nested object a vector is used for path instead of keyword.\nE.g.:\n```clojure\n{[:object-wo-reason :missing-reason] ;; \u003c-- a key path\n [{:args []\n   :id :garm.core/missing-key\n   :message \"This field is required\"}]}]\n\n```\n\n### -\u003ejson-api-response\n\nIt converts a result tuple of `validate` function to JSON API errors format.\nIt returns `nil` when the result of `validate` function is successful.\n\n```clojure\n(-\u003e ::order\n    (garm/validate {:status :foo\n                    :price 1000\n                    :id #uuid \"0428a077-96fd-4050-870e-ed1449266142\"}\n    (garm/-\u003ejson-api-response))\n\n{:errors [{:id :status\n           :meta {:data [{:id :garm.specs/must-be-one-of\n                          :message \"Must be one of %s\"\n                          :args [#{:dispatched :received}]\n                          :title \"Must be one of received, dispatched\"}]}}]}\n;; see formatted message in `:title`\n```\n\nContribution\n------------\n\n### Conventions\n\n* Please follow coding style defined by [`.editorconfig`](http://editorconfig.org)\n and [The Clojure Style Guide](https://github.com/bbatsov/clojure-style-guide)\n* Write [good commit messages](https://chris.beams.io/posts/git-commit/)\n and provide an issue ID in a commit message prefixed by `#`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdruids%2Fgarm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdruids%2Fgarm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdruids%2Fgarm/lists"}