{"id":17134337,"url":"https://github.com/brianium/yoose","last_synced_at":"2025-04-13T08:55:24.769Z","repository":{"id":57713285,"uuid":"100531510","full_name":"brianium/yoose","owner":"brianium","description":":construction_worker: A Clojure library for use case driven development","archived":false,"fork":false,"pushed_at":"2017-12-25T17:31:06.000Z","size":239,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T22:46:54.530Z","etag":null,"topics":["async","clojure","usecase"],"latest_commit_sha":null,"homepage":"","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/brianium.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":"2017-08-16T20:50:50.000Z","updated_at":"2022-02-06T17:57:35.000Z","dependencies_parsed_at":"2022-09-06T02:10:58.362Z","dependency_job_id":null,"html_url":"https://github.com/brianium/yoose","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/brianium%2Fyoose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fyoose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fyoose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianium%2Fyoose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brianium","download_url":"https://codeload.github.com/brianium/yoose/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248688544,"owners_count":21145763,"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":["async","clojure","usecase"],"created_at":"2024-10-14T19:44:39.347Z","updated_at":"2025-04-13T08:55:24.746Z","avatar_url":"https://github.com/brianium.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# yoose\n\n[![Clojars Project](https://img.shields.io/clojars/v/brianium/yoose.svg)](https://clojars.org/brianium/yoose)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.github.com/brianium/yoose/master/yussss.gif\" alt=\"Finn Says Yussssss\" /\u003e\n\u003c/p\u003e\n\nYoose attempts to encourage use case centered applications.\n\n*What is a use case?*\n\n\u003e In software and systems engineering, a use case is a list of actions or event steps typically defining the interactions between a role and a system to achieve a goal. The actor can be a human or other external system.\n\nYoose attempts to define a use case as a process created with input and output ports (currently via core.async channels). A use case should be able to gather input from it's input port to accomplish it's goal, and send output to it's output port to notify interested parties. Interested parties you say??!?!? An interested party might be an HTTP handler, a CLI application, a robotic arm? - the list goes on and on.\n\n## Usage\n\nThe following contrived example is inspired by the [clean todos](https://github.com/brianium/clean-todos) project.\n\n```clojure\n(ns yoose.todos\n  (:require [brianium.yoose.async :refer :all]))\n  \n(defusecase create-todo [this db]\n  (let [entity (\u003cin this)]\n    (-\u003e\u003e entity\n         (save db)\n         (create-message :todo/create)\n         (\u003eout this))))\n```\n\nThe `defusecase` is an optional bit of syntactic sugar, but one I find useful. `defusecase` defines a function\nthat is used for creating use cases. A `use-case-factory` if you will. These factories expect to be called\nwith an input and output channel a la `core.async`, and any other dependencies needed to do the job. Or in `clojure.spec` parlance:\n\n```clojure\n(s/def ::use-case-factory\n  (s/fspec :args (s/cat\n                   :in   ::in\n                   :out  ::out\n                   :deps (s/* any?))\n           :ret  yoose.spec/use-case))\n```\n\nAnd so one might actually create an instance of a use case like so:\n\n```clojure\n(def input (chan))\n\n(def output (chan))\n\n(def db (create-a-db))\n\n(def use-case (create-todo input output db))\n```\n\nNotice how dependencies defined after `this` in the `defusecase` example are passed in after `input` and `output`.\n\nAny number of additional arguments can be defined this way:\n\n```clojure\n(defusecase create-todo [this db arg2 arg3] ...)\n```\n\nWhile this is possible, I would recommend a single dependency map. This is super convenient when passing\naround dependencies - say with a super cool library like [mount](https://github.com/tolitius/mount) - and destructuring is always your friend.\n\n```clojure\n(defusecase create-todo [this {:keys [db arg2 arg3]}] ...)\n```\n\n**Note: use cases are currently built with `core.async` in mind, but it may not always be the case. It's possible\nthat some day `yoose.manifold` or `yoose.queue` might burst onto the scene and define use case factories with different expectations. When in doubt - refer to the specs ;)\n\n\n## Documentation\n\n### brianium.yoose\n\n`brianium.yoose` defines the api for exercising use cases. A use case is something that implements the `brianium.yoose/UseCase`\nprotocol. Most of the functions in `brianium.yoose` just implement the functions defined by the protocol.\n\n\n**push!**\n\nPlaces a value into the use case input port\n\n```clojure\n(push! use-case \"some value!\")\n```\n\n**pull!**\n\nCalls the given function with the next value taken from the output port\n\n```clojure\n(pull! use-case #(println %))\n```\n\n**pull!!**\n\nTakes a value from the output port and returns it. Blocks until output is received\n\n```clojure\n(def response (pull!! use-case))\n```\n\n**\u003cin**\n\nTakes a value from the input port. The use of this function is encouraged only in the context of defining a use case\n\n```clojure\n(let [input (\u003cin use-case)])\n```\n\n**\u003eout**\n\nPuts a value into the output port. The use of this function is encouraged only in the context of defining a use case\n\n```clojure\n(\u003eout use-case \"an output message\")\n```\n\n**in**\n\nReturns the input port of the use case\n\n```clojure\n(let [port (in use-case)])\n```\n\n**out**\n\nReturns the output port of the use case\n\n```clojure\n(let [port (out use-case)])\n```\n\n**close!**\n\nCloses input and output ports\n\n```clojure\n(close! use-case)\n```\n\n**trade!!**\n\nPushes a values into the use case and blocks until output is available.\n\n```clojure\n(def result (trade!! use-case \"hello\"))\n```\n\n**use-case?**\n\nCheck if the given value implements the `brianium.yoose/UseCase` protocol\n\n```clojure\n(use-case? value)\n```\n\nFor more information - see the [spec](https://github.com/brianium/yoose/blob/master/src/brianium/yoose/spec.clj)\n\n## brianium.yoose.async\n\nProvides a `core.async` implementation of the `brianium.yoose/UseCase` protocol.\n\n**make-use-case**\n\nCreates a new use case backed by `core.async`\n\n```clojure\n(require '[clojure.core.async :refer [chan]])\n\n(def input (chan))\n\n(def output (chan))\n\n(def use-case (make-use-case input output))\n```\n\n**\u003cin**\n\n`brianium.yoose/\u003cin` redefined as a macro. Since `go` macro translation stops at function creation boundaries - `brianium.yoose/\u003cin` can't opt into a wrapping `go` block. Using `brianium.yoose.async/\u003cin` circumvents this problem.\n\n```clojure\n;;; BAD - throws error for using \u003c! outside of go block\n(go\n  (let [input (brianium.yoose/\u003cin use-case)]))\n  \n;;; OK\n(go\n  (let [input (brianium.yoose.async/\u003cin use-case)]))\n```\n\n**\u003eout**\n\n`brianium.yoose/\u003eout` redefined as a macro. See `brianium.yoose/\u003cin` rationale above.\n\n\n**defusecase**\n\nDefines an async use case. A use case is really just a function\nthat executes it's body in the context of a go loop.\n\n```clojure\n(defusecase create-todo [this db]\n  (let [entity (\u003cin this)]\n    (-\u003e\u003e entity\n         (save db)\n         (create-message :todo/create)\n         (\u003eout this))))\n\t\t \n;; is equivalent to\n\n(defn create-todo [in out db]\n  (let [use-case (make-use-case in out)]\n    (go-loop []\n      (let [entity (\u003cin this)]\n        (-\u003e\u003e entity\n             (save db)\n             (create-message :todo/create)\n             (\u003eout this)))\n    (recur))\n  use-case))\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Fyoose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrianium%2Fyoose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianium%2Fyoose/lists"}