{"id":22862741,"url":"https://github.com/nedap/utils.modular","last_synced_at":"2025-04-30T21:52:48.077Z","repository":{"id":39241085,"uuid":"170934665","full_name":"nedap/utils.modular","owner":"nedap","description":"Utilities for creating modular Clojure systems.","archived":false,"fork":false,"pushed_at":"2023-01-11T08:53:03.000Z","size":128,"stargazers_count":6,"open_issues_count":3,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-30T21:52:38.693Z","etag":null,"topics":["clojure"],"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/nedap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-15T22:07:06.000Z","updated_at":"2023-04-02T07:59:15.000Z","dependencies_parsed_at":"2023-02-09T02:31:36.495Z","dependency_job_id":null,"html_url":"https://github.com/nedap/utils.modular","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nedap%2Futils.modular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nedap%2Futils.modular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nedap%2Futils.modular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nedap%2Futils.modular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nedap","download_url":"https://codeload.github.com/nedap/utils.modular/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251789310,"owners_count":21644081,"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"],"created_at":"2024-12-13T10:14:40.978Z","updated_at":"2025-04-30T21:52:48.057Z","avatar_url":"https://github.com/nedap.png","language":"Clojure","readme":"# utils.modular\n\nUtilities for creating modular systems: functions related to [Component](https://github.com/stuartsierra/component), or protocols.\n\n## Installation\n\n```clojure\n[com.nedap.staffing-solutions/utils.modular \"2.2.1\"]\n````\n\n## Synopsis\n\n#### `nedap.utils.modular.api/implement`\n\n`implement` is a safer layer over raw metadata-based protocol extension.\n\n\u003e Metadata-based protocol extension has recently proven to be a reliable solution against [this](https://github.com/clojure/tools.namespace/tree/bb9d7a1e98cc5a1ff53107966c96af6886eb0f5b#warnings-for-protocols) problem.\n\nIn plain Clojure you might be tempted to do:\n\n```clojure\n(defn start [this] ...)\n\n(defn stop [this] ...)\n\n(def my-component\n  \"A com.stuartsierra/component implementing some functionality\"\n  ^{`component/start start\n    `component/stop stop}\n  {})\n```\n\nHowever, several things might go wrong:\n\n* What if the protocol lacks a `:extend-via-metadata` directive?\n* What if you're running Clojure \u003c 1.10?\n* What if the `component/stop` quoted symbol does not get expanded to its fully-qualified name?\n  * Would happen if you forget the `:require` in your `(ns ...)` declaration\n* What if `component/start` does not resolve to a protocol function?\n  * e.g. to a function of not emitted by `defprotocol`\n* What if `start` does not evaluate to a function?\n  * i.e. any other kind of value\n\n`implement` guards you against all of those, preventing the lack of errors (or opaque errors, at best) that you'd get otherwise.\n\nIt also provides some sugar (symbols don't have to be quoted) and enforcement (you cannot pass anything other than a symbol; this aims to avoid deeply nested, non-reusable code).\n\nThis is how it looks like:\n\n```clojure\n(implement {}\n  component/start start\n  component/stop  stop)\n```\n\n#### `nedap.utils.modular.api/add-method`\n\n`clojure.core/defmethod` does the following:\n\n\u003e Creates and installs a new method of multimethod associated with dispatch-value.\n\n`add-method` does the same exact thing, but skipping the `Creates` part. i.e., it merely associates an existing function to a multimethod.\n\nThis has multiple advantages:\n\n* One can code with plain defns, making things more homogeneous\n  * And decoupled, reusable\n* Said defns can be [speced.def](https://github.com/nedap/speced.def) ones\n* Importantly, one should understand that `defmethod` is a side-effect, and as such should be controlled.\n  * Better to `add-method` in a [Component](https://github.com/stuartsierra/component) `start` definition.\n  \n#### `nedap.utils.modular.api/dependent`\n\nHelper fn for `com.stuartsierra.component/using` which takes a dependency collection and optionally\na map with renames. Note that the dependencies can be passed as a vector or a map.\n\n```clojure\n(dependent (my-component/new)\n           :on my-component/dependencies\n           :renames {:internal ::my-component/external})\n```\n\nThis allows the user to keep using the `my-component/dependencies`-def while maintaining the flexibility to rename\nsome keys.\n\n#### `nedap.utils.modular.api/omit-this [f]`\n\nCreates a replacement for `f` which drops the first argument, presumed to be of \\\"this\\\" type.\nApt for protocol extensions, when `f` is an arbitrary function which may not participate in our protocols at all.\n\nRefer to its test for an example.\n\n## ClojureScript compatibility\n\nAll the offered API is compatible with vanilla ClojureScript (i.e. non-self-compiled).\n\nHowever, `implement` offers weaker guarantees in its cljs version, since cljs has fewer introspection capabilities, particularly at macroexpansion time.\n\nAt the same time, as long as your cljs code is defined as a .cljc file _and_ it is compiled for the two possible targets (JVM, js),\nthen the JVM target will provide the guarantees that cljs cannot provide. i.e. cross-compilation can act as a \"linter\",\neven if only using in production just a single target. \n\n## ns organisation\n\nThere is exactly 1 namespace meant for public consumption:\n\n* `nedap.utils.modular.api`\n\nBy convention, `api` namespaces are deliberately thin so you can browse them comfortably.\n\n## Documentation\n\nPlease browse the public namespaces, which are documented, speced and tested.\n\n## License\n\nCopyright © Nedap\n\nThis program and the accompanying materials are made available under the terms of the [Eclipse Public License 2.0](https://www.eclipse.org/legal/epl-2.0).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnedap%2Futils.modular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnedap%2Futils.modular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnedap%2Futils.modular/lists"}