{"id":26186933,"url":"https://github.com/ivarref/gen-fn","last_synced_at":"2025-04-15T00:35:46.035Z","repository":{"id":137669460,"uuid":"491989288","full_name":"ivarref/gen-fn","owner":"ivarref","description":"Generate Datomic function literals from regular Clojure namespaces. On-prem.","archived":false,"fork":false,"pushed_at":"2023-04-29T17:26:16.000Z","size":73,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-28T12:38:24.280Z","etag":null,"topics":["clojure","datomic"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivarref.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":"2022-05-13T17:42:07.000Z","updated_at":"2023-06-20T07:12:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"c6f47481-a38a-4a57-8db4-dd8cd3a05e9d","html_url":"https://github.com/ivarref/gen-fn","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Fgen-fn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Fgen-fn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Fgen-fn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivarref%2Fgen-fn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivarref","download_url":"https://codeload.github.com/ivarref/gen-fn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248985981,"owners_count":21194020,"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","datomic"],"created_at":"2025-03-11T23:35:38.932Z","updated_at":"2025-04-15T00:35:46.026Z","avatar_url":"https://github.com/ivarref.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gen-fn\n\nGenerate Datomic function literals from regular Clojure namespaces. On-prem.\n\n## Installation\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.github.ivarref/gen-fn.svg)](https://clojars.org/com.github.ivarref/gen-fn)\n\n## 2-minute example\n\nAdd a namespace that will contain the generated Datomic function literals:\n\n```clojure\n(ns com.github.ivarref.generated\n  (:require [clojure.edn :as edn]\n            [datomic.api]))\n\n; Generated code below, do not edit:\n(def generated {})\n; End of generated code\n\n(def schema\n  (mapv (fn [s]\n          (edn/read-string\n            {:readers {'db/id  datomic.db/id-literal\n                       'db/fn  datomic.function/construct}}\n            s))\n        (vals generated)))\n```\n\nAdd a namespace containing the function you would like to run on the transactor:\n```clojure\n(ns com.github.ivarref.my-add\n  (:require [datomic.api :as d]))\n\n(defn curr-val [db e a]\n  (d/q '[:find ?v .\n         :in $ ?e ?a\n         :where\n         [?e ?a ?v]]\n       db e a))\n\n(defn my-add [db e a v]\n  [[:db/add e a (+ (curr-val db e a) v)]])\n```\n\nGenerate the Datomic function literal:\n \n```clojure\n(require '[com.github.ivarref.gen-fn :refer [gen-fn!]])\n(require '[com.github.ivarref.my-add :refer [my-add]])\n\n(gen-fn! :my-add #'my-add \"test/com/github/ivarref/generated.clj\")\n```\n\nYou can now see the following contents in the generated namespace (formatting added):\n```clojure\n; Generated code below, do not edit:\n(def generated {:my-add \"\n{:db/ident :my-add \n :db/fn #db/fn {:lang \\\"clojure\\\", \n               :requires [[datomic.api :as d]], \n               :imports [], \n               :params [db e a v], \n               :code (let [] (letfn [(curr-val [db e a] \n                                       (do (d/q (quote [:find ?v . :in $ ?e ?a :where [?e ?a ?v]]) db e a)))]\n                               (let [genfn-coerce-arg (clojure.core/fn [x]\n                                 (clojure.walk/prewalk \n                                   (clojure.core/fn [e]\n                                     (if (some? e)\n                                         (do\n                                             (clojure.core/when (clojure.core/instance? clojure.lang.PersistentTreeMap e)\n                                             (throw (clojure.core/ex-info \\\"Using sorted-map will cause different types in transactor for in-mem and remote\\\" {:val e})))\n                                             \n                                             (clojure.core/when (clojure.core/var? e)\n                                             (throw (clojure.core/ex-info \\\"Using var does not work for remote transactor\\\" {:val e})))\n                                             \n                                             (clojure.core/when (clojure.core/or (clojure.core/= clojure.lang.PersistentList$EmptyList (.getClass e))\n                                                                                 (clojure.core/instance? clojure.lang.PersistentList e))\n                                             (throw (clojure.core/ex-info \\\"Using list will cause indistinguishable types in transactor for in-mem and remote\\\" {:val e})))\n                                             \n                                             (clojure.core/when (clojure.core/instance? clojure.lang.PersistentQueue e)\n                                             (throw (clojure.core/ex-info \\\"Using clojure.lang.PersistentQueue does not work for remote transactor\\\" {:val e})))\n                                             \n                                             (clojure.core/cond\n                                               (clojure.core/instance? java.util.HashSet e)\n                                               (clojure.core/into #{} e)\n                                               \n                                               (clojure.core/and (clojure.core/instance? java.util.List e) (clojure.core/not (clojure.core/vector? e)))\n                                               (clojure.core/vec e)\n                                               \n                                               :else e))\n                                           e))\n                                     x))]\n                                     (let [e (genfn-coerce-arg e)\n                                           a (genfn-coerce-arg a)\n                                           v (genfn-coerce-arg v)]\n                                       (do [[:db/add e a (+ (curr-val db e a) v)]])))))}}\"})\n; End of generated code\n```\n\nYou can see that:\n* The `(ns ...` declaration is rewritten to `#db/fn`.\n* The last `defn` is used as the main body.\n* Other `defn`s are inlined using `letfn`.\n* `defn` bodies are wrapped in `do` in case you need side effects for debugging.\n* `def`s are inlined in the top `let`. No `def`s are used in the example namespace, thus the top `let` is empty.\n* Arguments are converted to \"proper\" Clojure types. This is to say that types will be identical in the in-memory transactor and the remote transactor.\n \n## Test and development usage\n\nOne advantage of writing Datomic database functions using regular Clojure\ncode is that you may test them using plain Clojure.\n\nIf however you'd like to test the function in an actual transaction, you may use\nthe following if you'd like to avoid a hard dependency on generated files:\n\n```clojure\n(require '[com.github.ivarref.gen-fn :refer [datomic-fn]])\n(require '[com.github.ivarref.my-add :refer [my-add]])\n\n...\n@(d/transact conn [(datomic-fn :my-add #'my-add)])\n\n...\n@(d/transact conn [[:my-add some-eid attr value-to-add]])\n```\n\n### Note: fressian serialization and deserialization\n\nOne thing that may surprise you is that parameters may be \nslightly different on an in-memory transactor and on a remote transactor.\nThis is due to tx-data being serialized and deserialized using\n[fressian](https://github.com/Datomic/fressian) only when using\na remote transactor.\n\nAs can be seen by the example above, `gen-fn` will write functions that\nautomatically handles this for you.\n\n## Limitations\n\nOnly a single namespace per database function is supported, i.e.\nthe database function must be contained in a single namespace. It may\nrefer code that already exists on the transactor classpath.\n\n## Alternatives\n\nThere is also [classpath functions](https://docs.datomic.com/on-prem/reference/database-functions.html#classpath-functions). This means that you will need to update your\ntransactor if you need to add or change a function.\n\n## Change log\n\n#### 0.2.46 - 2022-12-14\nHandle nil values in auto conversion.\n\n#### 0.2.45 - 2022-12-14\nAdded auto conversion to \"proper\" Clojure types.\n\n#### 0.1.35 - 2022-06-13\n\nPrettier generated files output. Thanks to [rewrite-clj](https://github.com/clj-commons/rewrite-clj).\n\n#### 0.1.33 - 2022-05-24\nAdd optional keyword argument `:reset?` to `gen-fn`.\nIf `:reset?` is set to true, it will clear the generated map\nbefore associng the given function. Example usage:\n```clojure\n(gen-fn! :my-add #'my-add output-file :reset? true)\n```\n\n#### 0.1.30 - 2022-05-24\nFirst publicly announced release.\n\n## License\n\nCopyright © 2022 Ivar Refsdal\n\nThis program and the accompanying materials are made available under the\nterms of the Eclipse Public License 2.0 which is available at\nhttp://www.eclipse.org/legal/epl-2.0.\n\nThis Source Code may also be made available under the following Secondary\nLicenses when the conditions for such availability set forth in the Eclipse\nPublic License, v. 2.0 are satisfied: GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or (at your\noption) any later version, with the GNU Classpath Exception which is available\nat https://www.gnu.org/software/classpath/license.html.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivarref%2Fgen-fn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivarref%2Fgen-fn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivarref%2Fgen-fn/lists"}