{"id":18552961,"url":"https://github.com/cryptosense/ppx_factory","last_synced_at":"2025-04-09T22:32:06.774Z","repository":{"id":45051880,"uuid":"168559394","full_name":"cryptosense/ppx_factory","owner":"cryptosense","description":"OCaml preprocessor to derive factory methods and default values from type definitions","archived":false,"fork":false,"pushed_at":"2023-02-10T23:01:31.000Z","size":103,"stargazers_count":15,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-24T13:21:18.996Z","etag":null,"topics":["factory","ocaml","ppx","testing"],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cryptosense.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-31T16:43:46.000Z","updated_at":"2024-11-27T11:01:12.000Z","dependencies_parsed_at":"2022-09-23T10:42:12.710Z","dependency_job_id":null,"html_url":"https://github.com/cryptosense/ppx_factory","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptosense%2Fppx_factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptosense%2Fppx_factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptosense%2Fppx_factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cryptosense%2Fppx_factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cryptosense","download_url":"https://codeload.github.com/cryptosense/ppx_factory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248123805,"owners_count":21051537,"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":["factory","ocaml","ppx","testing"],"created_at":"2024-11-06T21:15:39.456Z","updated_at":"2025-04-09T22:32:05.777Z","avatar_url":"https://github.com/cryptosense.png","language":"OCaml","readme":"# ppx_factory\n\n`ppx_factory` is an OCaml preprocessor to derive factory methods and default values from type\ndefinitions.\n\n## Overview\n\nFactories are functions that let you build test data while only specifying the parts that are\nrelevant to your tests.\n\n`ppx_factory` allows you to derive such functions from type definitions.\n\nLet's take a very basic example. Consider the following type and function:\n```ocaml\ntype person =\n  { first_name : string\n  ; middle_name : string option\n  ; last_name : string\n  ; age : int\n  ; hobbies : Hobby.t list\n  }\n\nlet full_name {first_name; last_name; middle_name; _} =\n  match middle_name with\n  | None -\u003e Printf.sprintf \"%s %s\" first_name last_name\n  | Some m_name -\u003e Printf.sprintf \"%s \\\"%s\\\" %s\" first_name m_name last_name\n```\n\nWhen writing tests for the `full_name` function, you don't want to bother defining `age` and\n`hobbies` every time so you use a factory method that looks like:\n```ocaml\nval person_factory :\n  ?first_name: string -\u003e\n  ?middle_name: string -\u003e\n  ?last_name: string -\u003e\n  ?age: int -\u003e\n  ?hobbies: Hobby.t list -\u003e\n  unit -\u003e\n  person\n```\n\nand let's you write your test in a more concise manner:\n```ocaml\ntest\n  ~input:(person_factory ~first_name:\"John\" ?middle_name:None ~last_name:\"Doe\" ())\n  ~expected:\"John Doe\";\ntest\n  ~input:(person_factory ~first_name:\"Robyn\" ~middle_name:\"Rihanna\" ~last_name:\"Fenty\" ())\n  ~expected:\"Robyn \\\"Rihanna\\\" Fenty\"\n```\n\nBy adding the `[@@deriving factory]` attribute to your type definitions, `ppx_factory` will generate\na single factory function for record types or one per constructor for variant types.\n\nYou can also use `[@@deriving default]` to generate a single default value per type to use in your\ntests.\n\nSee the [Detailed usage](https://github.com/cryptosense/ppx_factory#detailed-usage) section for\nfurther information.\n\n## Installation and usage\n\nYou can install `ppx_factory` using [opam](https://opam.ocaml.org):\n```\n$ opam install ppx_factory\n```\n\nIf you're building your library or app with dune, add the following field to your `library`,\n`executable` or `test` stanza:\n```\n(preprocess (pps ppx_factory))\n```\nor simply add `ppx_factory` to your `preprocess` field if it's already there.\n\nYou can now add the `factory` and/or `default` plugins to `[@@deriving ...]` attributes on type\ndefinitions to derive the corresponding values.\n\n## Detailed usage\n\n### factory\n\nYou can use `[@@deriving factory]` to derive factory functions from type definitions both in module\nand module signatures eg both in your `.ml` and `.mli` files given that it's an explicit record or\nvariant type definition.\n\n#### Record types\n\nYou can derive factory functions from record type definitions. This will derive a single factory\nfunction that has an optional argument per field. The name of the factory function depends on the\nname of the type, `factory` will be derived from type `t` and `\u003ctype_name\u003e_factory` for any other\ntype.\n\nEach optional parameter will expect the same type as the one declared for the corresponding field,\nexcept for option types. A field which has type `a option` will be turned into a optional argument\nexpecting an `a`.\n\nBecause examples are worth a thousand words:\n```ocaml\ntype t =\n  { a : int\n  ; b : string\n  }\n[@@deriving factory]\n\ntype 'a u =\n  { c : 'a list\n  ; d : 'a option\n  }\n[@@deriving factory]\n```\n\nwill derive the following factory functions:\n```ocaml\nval factory : ?a: int -\u003e ?b: string -\u003e unit -\u003e t\n\nval u_factory : ?c: 'a list -\u003e ?d: 'a -\u003e unit -\u003e 'a t\n```\n\n#### Variant types\n\nYou can also derive factory functions from variant type definitions. This will derive one of them\nper constructor. Those functions will be named based on the type and the constructor name and have\na `\u003ctype_name\u003e_\u003clowercased_constructor_name\u003e_` prefix.\n\nConstant constructor factories will have a single `unit` argument, while for constructors with tuple\narguments, including 1-element tuples, they will have `?tup\u003celement_tuple_index\u003e` arguments starting\nat `?tup0` and for constructors with record arguments they will have optional arguments named as the\ncorresponding record field.\n\n```ocaml\ntype t =\n  | A\n  | B of string\n[@@deriving factory]\n\ntype 'a u =\n  | C of int * 'a option\n  | D of\n    { some_int : int\n    ; some_list : 'a list\n    }\n[@@deriving factory]\n```\n\nwill derive the following factory functions:\n```ocaml\nval a_factory : unit -\u003e t\n\nval b_factory : ?tup0: string -\u003e unit -\u003e t\n\nval u_c_factory : ?tup0: int -\u003e ?tup1: 'a -\u003e 'a u\n\nval u_d_factory : ?some_int: int -\u003e ?some_list: 'a list -\u003e 'a u\n```\n\n### default\n\nTo derive factory functions, `ppx_factory` relies on the fact that it's able to derive a default\nvalue from a record field or constructor argument's type. For most core types we derive one of our\nown whereas for custom types we expect to find one in the right place. Eg for a type `t` we expect a\n`default` value to be available in the current scope, for a type `u` we expect `default_u`, for\n`A.B.t`, `A.B.default` and so on.\n\n`ppx_factory` also exposes a deriver to spare you the need to write and update those yourself. You\ncan use it by attaching `[@@deriving default]` to type definitions.\n\nIt can be used with most core types, record and variant types. It will derive a single value\nwhich name will be based on the type name, i.e. `default` for type `t` or `default_\u003ctype_name\u003e`\notherwise.\n\nIn some cases it's impossible to derive a default from a parametrized type. For example you can't\nderive a default value from the following type:\n```ocaml\ntype 'a t = 'a * int\n```\nbecause we can't derive a value of type `'a` for any type `'a`.\n\nIt will only derive a default value if it can derive a generic one. For instance it's possible to\nderive a value that as type `'a option`, `'a list` or `'a array` for any type `'a` (obviously\n`None`, `[]` and `[||]`). The `default` deriver will be able to figure that from your explicit type\ndefinitions. If you consider the following two types:\n```ocaml\ntype ('a, 'b) t1 =\n  | A of 'a\n  | B of 'b\n\ntype ('a, 'b) t2 =\n  | C of 'a\n  | D of 'b\n  | E of int\n```\n`[@@deriving default]` can't derive a default value that as type `('a, 'b) t1` so you will get a\ncompile error if you try to use it with type `t1`.\nOn the other hand it can derive a default value that as type `('a, 'b) t2` such as `E 0`.\n\nIt is worth noting that **you should never rely on what the actual value of such a default is** as\nit is susceptible to change in minor or even patch releases. If your tests depend on a default value\nit means you're not using factories right!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptosense%2Fppx_factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcryptosense%2Fppx_factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptosense%2Fppx_factory/lists"}