{"id":19917196,"url":"https://github.com/athos/genman","last_synced_at":"2025-05-03T06:30:45.419Z","repository":{"id":57713700,"uuid":"125689834","full_name":"athos/genman","owner":"athos","description":"Generator management utility for clojure.spec","archived":false,"fork":false,"pushed_at":"2018-04-05T01:56:13.000Z","size":50,"stargazers_count":20,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-07T11:51:30.278Z","etag":null,"topics":["clojure","clojurescript","generator","spec"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/athos.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-03-18T03:36:55.000Z","updated_at":"2023-02-02T16:53:18.000Z","dependencies_parsed_at":"2022-09-18T06:55:34.321Z","dependency_job_id":null,"html_url":"https://github.com/athos/genman","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fgenman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fgenman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fgenman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/athos%2Fgenman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/athos","download_url":"https://codeload.github.com/athos/genman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252154732,"owners_count":21702982,"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":["clojure","clojurescript","generator","spec"],"created_at":"2024-11-12T21:49:06.988Z","updated_at":"2025-05-03T06:30:45.162Z","avatar_url":"https://github.com/athos.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Genman\n[![Clojars Project](https://img.shields.io/clojars/v/genman.svg)](https://clojars.org/genman)\n[![CircleCI](https://circleci.com/gh/athos/genman.svg?style=shield)](https://circleci.com/gh/athos/genman)\n\nGenerator management utility for clojure.spec\n\n## Features\n\n- Generator definitions isolated from spec definitions\n    - No more need to mess up your spec definition with a gigantic `s/with-gen` code!\n- Provides switching mechanism between multiple generator implementations for a single spec\n\n## Installation\n\nAdd the following to your `:dependencies`:\n\n[![Clojars Project](https://clojars.org/genman/latest-version.svg)](https://clojars.org/genman)\n\n## Usage\n\n- [`defgenerator` / `gen`](#defgenerator--gen)\n- [`with-gen-group` / `use-gen-group`](#with-gen-group--use-gen-group)\n- [`merge-groups` / `extend-group`](#merge-groups--extend-group)\n- [`def-gen-group`](#def-gen-group)\n- [`-\u003eoverrides-map`](#-overrides-map)\n\n### `defgenerator` / `gen`\n\nFirst, define a generator which you want to use via Genman as follows:\n\n```clj\n(require '[clojure.spec.alpha :as s]\n         '[clojure.test.check.generators :as gen]\n         '[genman.core :as genman :refer [defgenerator]])\n\n(s/def ::id int?)\n(s/def ::name string?)\n\n(defgenerator ::id\n  (s/gen #{0 1 2}))\n```\n\nOnce a generator is defined using `defgenerator`, you can use it with `genman/gen` instead of `s/gen`:\n\n```clj\n(gen/generate (genman/gen ::id))\n;; =\u003e 0\n\n(gen/generate (genman/gen ::id))\n;; =\u003e 2\n\n(gen/generate (genman/gen ::id))\n;; =\u003e 1\n```\n\n### `with-gen-group` / `use-gen-group`\n\nIn addition, Genman provides switching mechanism between multiple generator implementations for a single spec. To use this facility, a generator should be defined in a **generator group**. In the example below, two more implementations for each `::id` and `::name` are being defined in the generator groups named `:dev` and `:test`:\n\n```clj\n(genman/with-gen-group :dev\n  (defgenerator ::id\n    (gen/return 42))\n\n  (defgenerator ::name\n    (gen/return \"foo\")))\n\n(genman/with-gen-group :test\n  (defgenerator ::id\n    (gen/return 101))\n\n  (defgenerator ::name\n    (gen/return \"bar\")))\n```\n\nAnd then, specify the generator group with `with-gen-group` to choose those specific implementations:\n\n```clj\n(genman/with-gen-group :dev\n  (gen/generate (genman/gen (s/tuple ::id ::name))))\n;; =\u003e [42 \"foo\"]\n\n(genman/with-gen-group :test\n  (gen/generate (genman/gen (s/tuple ::id ::name))))\n;; =\u003e [101 \"bar\"]\n```\n\nIf no generator group is specified, `genman/gen` and `defgenerator` will behave as if the `:default` generator group were specified.\n\nNote that since `with-gen-group` is built on top of dynamic var binding, once a generator got out of that scope, it could lose the effect of specifying the generator group:\n\n```clj\n(def g\n  (genman/with-gen-group :dev\n    (fn []\n      (genman/gen (s/tuple ::id ::name)))))\n\n(gen/generate (g))\n;; =\u003e [0 \"\"]\n(gen/generate (g))\n;; =\u003e [2 \"O2ltmsM\"]\n```\n\nTo avoid this behavior, use `use-gen-group` instead:\n\n```clj\n(def g\n  (genman/use-gen-group :dev\n    (fn []\n      (genman/gen (s/tuple ::id ::name)))))\n\n(gen/generate (g))\n;; =\u003e [42 \"foo\"]\n(gen/generate (g))\n;; =\u003e [42 \"foo\"]\n```\n\nAs a rule of thumb, `with-gen-group` works well for test fixtures, and `use-gen-group` suits for use in each (property-based) test case.\n\n### `merge-groups` / `extend-group`\n\nAlso, there are some ways to create a new generator group based on existing ones (which we call an *adhoc* generator group).\n\n`merge-groups` merges more than one generator groups (in the left-to-right manner as with `clojure.core/merge`):\n\n```clj\n(genman/with-gen-group :test1\n  (defgenerator ::id\n    (gen/return 42))\n    \n  (defgenerator ::name\n    (gen/return \"foo\")))\n\n(genman/with-gen-group :test2\n  (defgenerator ::name\n    (gen/return \"bar\")))\n    \n(genman/with-gen-group (genman/merge-groups :test1 :test2)\n  (gen/generate (genman/gen (s/tuple ::id ::name))))\n;; =\u003e [42 \"bar\"]\n```\n\nOr, you can simply pass a map, from spec name keys to fns returning a generator, to override an existing generator group:\n\n```clj\n(genman/with-gen-group (genman/merge-groups :test1 {::name #(gen/return \"baz\")})\n  (gen/generate (genman/gen (s/tuple ::id ::name))))\n;; =\u003e [42 \"baz\"]\n```\n\nIf you would like to wrap the existing generator implementation instead, `extend-group` would be useful:\n\n```clj\n(genman/with-gen-group (genman/extend-group :test1 {::id (fn [g] (gen/fmap #(* % 100) g))})\n  (gen/generate (genman/gen ::id)))\n;; =\u003e 4200\n```\n\n### `def-gen-group`\n\nAlthough adhoc generator groups have no need to have their own name, occasionally it's convenient to reference them with a fixed name to reuse them in several places. To define a name for an adhoc generator group, use `def-gen-group` as follows:\n\n```clj\n(require '[genman.core :as genman :refer [def-gen-group]])\n\n(genman/with-gen-group :dev\n  (defgenerator ::id\n    (s/gen #{0 1 2})))\n\n(def-gen-group :dev'\n  (genman/extend-group :dev\n    {::id (fn [g] (gen/such-that even? g))}))\n\n(genman/with-gen-group :dev'\n  (gen/generate (genman/gen ::id)))\n;; =\u003e 2\n\n(genman/with-gen-group :dev'\n  (gen/generate (genman/gen ::id)))\n;; =\u003e 0\n```\n\nA named adhoc generator group can be used in exactly the same way as ordinary generator groups.\n\n### `-\u003eoverrides-map`\n\nSometimes there are situations where you would like to cooperate with clojure.spec APIs such as `clojure.spec.test.alpha/check` and `clojure.spec.test.alpha/instrument`, enabling a generator group. In those cases, `-\u003eoverrides-map` would do to make their `:gen` optional argument:\n\n```clj\n(require '[clojure.spec.test.alpha :as st])\n\n(s/def ::amount nat-int?)\n\n(defn double-amount [amount]\n  (* 2 amount))\n\n(s/fdef double-amount\n  :args (s/cat :amount ::amount)\n  :ret ::amount)\n\n(genman/with-gen-group :dev\n  (defgenerator ::amount\n    (s/gen (s/int-in 0 10000))))\n\n(st/check `double-amount)\n;; throws an ArithmeticException (integer overflow)\n\n(st/check `double-amount {:gen (genman/-\u003eoverrides-map :dev)})\n;; pass the check\n```\n\n## License\n\nCopyright © 2018 Shogo Ohta\n\nDistributed under the Eclipse Public License version 1.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathos%2Fgenman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fathos%2Fgenman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fathos%2Fgenman/lists"}