{"id":32182005,"url":"https://github.com/metametadata/carry","last_synced_at":"2025-10-21T22:57:18.488Z","repository":{"id":57713320,"uuid":"52026575","full_name":"metametadata/carry","owner":"metametadata","description":"ClojureScript application framework.","archived":false,"fork":false,"pushed_at":"2019-06-21T07:16:55.000Z","size":33201,"stargazers_count":149,"open_issues_count":0,"forks_count":6,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-09-26T21:31:11.637Z","etag":null,"topics":["debugger","framework","gui","mvc","react","reagent"],"latest_commit_sha":null,"homepage":"https://metametadata.github.io/carry/","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/metametadata.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-18T17:50:32.000Z","updated_at":"2025-08-14T00:25:59.000Z","dependencies_parsed_at":"2022-09-06T02:11:02.092Z","dependency_job_id":null,"html_url":"https://github.com/metametadata/carry","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/metametadata/carry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metametadata%2Fcarry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metametadata%2Fcarry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metametadata%2Fcarry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metametadata%2Fcarry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metametadata","download_url":"https://codeload.github.com/metametadata/carry/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metametadata%2Fcarry/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279614957,"owners_count":26199734,"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","status":"online","status_checked_at":"2025-10-18T02:00:06.492Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["debugger","framework","gui","mvc","react","reagent"],"created_at":"2025-10-21T22:57:16.270Z","updated_at":"2025-10-21T22:57:18.483Z","avatar_url":"https://github.com/metametadata.png","language":"Clojure","readme":"# Carry\n\nClojureScript single-page application framework inspired by\n[re-frame](https://github.com/Day8/re-frame),\n[Elm Architecture](https://guide.elm-lang.org/architecture/),\n[Redux](https://github.com/reactjs/redux/) and\n[Cerebral](https://github.com/cerebral/cerebral).\n\nCarry provides a structure for making GUI application code easier to modify, debug, test and be worked on by multiple programmers.\n\nThe core of the framework is a simple state management library. \nUI bindings, routing, debugger, etc. are implemented as separate optional [packages](#packages).\n\n[![Clojars Project](https://img.shields.io/clojars/v/carry.svg)](https://clojars.org/carry)\n[![Gitter](https://img.shields.io/gitter/room/metametadata/carry.svg?maxAge=2592000?style=plastic)](https://gitter.im/metametadata/carry)\n[![Slack](https://img.shields.io/badge/chat-on_slack-brightgreen.svg?style=flat)](https://clojurians.slack.com/messages/carry/)\n\n## Status\n\nStable. Was used in production.\n\nI now focus on the successor [Aide](https://github.com/metametadata/aide) framework instead.\n\n## Features\n\n* Functional API without globals and macros.\n* Agnostic to UI layer: can be effectively used with [Reagent](https://github.com/reagent-project/reagent)\n(via [carry-reagent](https://github.com/metametadata/carry/tree/master/contrib/reagent/) package) or\nany other view layer that is able to re-render UI in response to app model changes.\n* [Time traveling debugger](https://github.com/metametadata/carry/tree/master/contrib/debugger)\ninspired by [Redux DevTools](https://github.com/gaearon/redux-devtools) and [Cerebral Debugger](http://www.cerebraljs.com/documentation/the_debugger).\n* Live code editing using [Figwheel](https://github.com/bhauman/lein-figwheel) and debugger's replay mode.\n* Can work with [Devcards](https://github.com/bhauman/devcards).\n* Core library can be also used in Clojure projects.\n\n## Pattern\nCarry enforces:\n\n* Separation of presentation code.\n* Events as first-class citizens.\n* Splitting event handling code into side-effectful and \"pure\" model updating phases.\n* Storing model in a single observable atom.\n\nIt also advises to decouple view and view model code in the presentation layer:\n\n![pattern](http://metametadata.github.io/carry/graphs/pattern.svg)\n\n* An app is defined by its initial model value, signal handler and action handler.\n* All app state is stored inside a single model atom.\n* Anyone can read model value at any given time and subscribe to its changes.\n* Signal handler performs side effects and dispatches actions.\n* Anyone can dispatch a new signal: signal handler, views, timers, etc.\n* Typically UI layer dispatches signals on UI events and subscribes to model changes to redraw the GUI when needed.\n* Model can be modified only by dispatching actions.\n* Only signal handler can dispatch actions.\n* Action handler is a pure function which returns a new model value based on an incoming action.  \n\n## Example (counter app)\n\n[Demo](https://metametadata.github.com/carry/examples/counter),\n[Source code](https://github.com/metametadata/carry/tree/master/examples/counter)\n\nHTML:\n\n```html\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003e\n    \u003ctitle\u003eCarry • Counter\u003c/title\u003e\n    \u003clink rel=\"icon\" href=\"data:;base64,iVBORw0KGgo=\"\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cdiv id=\"root\"\u003e\u003c/div\u003e\n    \u003cscript src=\"js/compiled/frontend.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nMain file:\n\n```clj\n(ns app.core\n  (:require [counter.core :as counter]\n            [carry.core :as carry]\n            [carry-reagent.core :as carry-reagent]\n            [reagent.core :as r]))\n\n(let [app (carry/app counter/blueprint)\n      [_ app-view] (carry-reagent/connect app counter/view-model counter/view)]\n    (r/render app-view (.getElementById js/document \"root\"))\n    ((:dispatch-signal app) :on-start))\n```\n\nUI (using [Reagent](https://github.com/reagent-project/reagent) and [carry-reagent](https://github.com/metametadata/carry/tree/master/contrib/reagent/)):\n\n```clj\n(ns counter.core\n  (:require [cljs.core.match :refer-macros [match]]\n            [reagent.ratom :refer [reaction]]))\n\n(defn view-model\n  [model]\n  {:counter (reaction (str \"#\" (:val @model)))})\n\n(defn view\n  [{:keys [counter] :as _view-model} dispatch]\n  [:p\n   @counter \" \"\n   [:button {:on-click #(dispatch :on-increment)} \"+\"] \" \"\n   [:button {:on-click #(dispatch :on-decrement)} \"-\"] \" \"\n   [:button {:on-click #(dispatch :on-increment-if-odd)} \"Increment if odd\"] \" \"\n   [:button {:on-click #(dispatch :on-increment-async)} \"Increment async\"]])\n```\n\nBlueprint:\n\n```clj\n(def -initial-model {:val 0})\n\n(defn -on-signal\n  [model signal _dispatch-signal dispatch-action]\n  (match signal\n         :on-start nil\n         :on-stop nil\n\n         :on-increment\n         (dispatch-action :increment)\n\n         :on-decrement\n         (dispatch-action :decrement)\n\n         :on-increment-if-odd\n         (when (odd? (:val @model))\n           (dispatch-action :increment))\n\n         :on-increment-async\n         (.setTimeout js/window #(dispatch-action :increment) 1000)))\n\n(defn -on-action\n  [model action]\n  (match action\n         :increment (update model :val inc)\n         :decrement (update model :val dec)))\n\n(def blueprint {:initial-model -initial-model\n                :on-signal     -on-signal\n                :on-action     -on-action})\n```\n\n## Packages\n\n### UI Bindings\n\n* [carry-reagent](https://github.com/metametadata/carry/tree/master/contrib/reagent/)\n(bindings for [Reagent](https://github.com/reagent-project/reagent))\n\n### Middleware\n\n* [carry-atom-sync](https://github.com/metametadata/carry/tree/master/contrib/atom-sync)\n(creating a bidirectional sync between an app model and a specified atom-like reference)\n* [carry-debugger](https://github.com/metametadata/carry/tree/master/contrib/debugger)\n(time traveling debugger)\n* [carry-history](https://github.com/metametadata/carry/tree/master/contrib/history)\n(working with browser history)\n* [carry-logging](https://github.com/metametadata/carry/tree/master/contrib/logging)\n(console logging)\n* [carry-persistence](https://github.com/metametadata/carry/tree/master/contrib/persistence)\n(automatic model saving/loading using browser storage)\n* [carry-schema](https://github.com/metametadata/carry/tree/master/contrib/schema)\n(model validation using [Schema](https://github.com/plumatic/schema))\n\n## Documentation\n\nMore information can be found at [the project site](http://metametadata.github.io/carry/):\n\n* [User Guide](http://metametadata.github.io/carry/user-guide/)\n* [Examples](http://metametadata.github.io/carry/examples/)\n* [API Reference](http://metametadata.github.io/carry/api/)\n* [FAQ](http://metametadata.github.io/carry/faq/)\n* [Developer Guide](http://metametadata.github.io/carry/dev-guide/)\n\n## License\nCopyright © 2016 Yuri Govorushchenko.\n\nReleased under an MIT license.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetametadata%2Fcarry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetametadata%2Fcarry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetametadata%2Fcarry/lists"}