{"id":13801331,"url":"https://github.com/tatut/tuck","last_synced_at":"2025-03-21T21:31:03.279Z","repository":{"id":62435174,"uuid":"66100853","full_name":"tatut/tuck","owner":"tatut","description":"Tuck: a micro framework for Reagent apps","archived":false,"fork":false,"pushed_at":"2020-08-28T07:47:43.000Z","size":130,"stargazers_count":29,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-01T10:03:35.556Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tatut.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}},"created_at":"2016-08-19T17:40:25.000Z","updated_at":"2024-04-15T17:50:39.000Z","dependencies_parsed_at":"2022-11-01T21:16:15.610Z","dependency_job_id":null,"html_url":"https://github.com/tatut/tuck","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatut%2Ftuck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatut%2Ftuck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatut%2Ftuck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tatut%2Ftuck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tatut","download_url":"https://codeload.github.com/tatut/tuck/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244160042,"owners_count":20408020,"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":[],"created_at":"2024-08-04T00:01:21.688Z","updated_at":"2025-03-21T21:31:02.895Z","avatar_url":"https://github.com/tatut.png","language":"Clojure","readme":"# ![Tuck logo](https://github.com/tatut/tuck/blob/master/tucklogo.png?raw=true) Tuck\n\n[![Clojars Project](https://img.shields.io/clojars/v/webjure/tuck.svg)](https://clojars.org/webjure/tuck)\n[![CircleCI](https://circleci.com/gh/tatut/tuck.svg?style=svg)](https://circleci.com/gh/tatut/tuck)\n\nTuck is a micro framework for building Reagent apps that have a clean separation of view code and\nevent processing code. View components in tuck are just pure functions without any magic.\n\nTuck defines a protocol for events and how they are processed and provides a simple way to send\nevents from UI code to be processed.\n\nTuck can be used at any level (not just at the app root): simply pass tuck a reagent atom and a component.\n\nTuck is heavily inspired by [Petrol](https://github.com/krisajenkins/petrol) but is even lighter and has no dependencies (aside form Reagent itself).\n\n## Documentation\n\nSee the codox generated [API documentation](https://tatut.github.io/tuck/codox/index.html).\n\n\u003cimg src=\"https://raw.github.com/tatut/tuck/master/docs/tuck-concepts.svg?sanitize=true\"\u003e\n\nThe entrypoint to tuck is the `tuck.core/tuck` reagent component which takes your app state (a ratom) and your\nroot component as arguments. Tuck will create a control handle and call your root component with the it and the current\ndereferenced value of the app state. The control handle is used to dispatch events for processing and is typically\ncalled `e!`. All changes to app state are done by event processing and must be sent through the control handle.\n\n### Minimal example\n\nThis is a minimal example that shows how to use tuck. Normally you would want to separate your view code and your event\ndefinitions to separate name spaces.\n\n```clojure\n(ns tuck-example.core\n  (:require [reagent.core :as r]\n            [tuck.core :as t :refer-macros [define-event]]))\n\n(define-event UpdateName [new-name]\n  {:path [:name]}\n  new-name)\n\n(defn my-root-component [e! {:keys [name :as app]]\n  [:input {:value name\n           :on-change #(e! (-\u003eUpdateName (-\u003e % .-target .-value)))}])\n\n(def app-state (r/atom {}))\n\n(defn main []\n  (r/render [t/tuck app-state my-root-component]\n            (.getElementById js/document \"app\")))\n```\n\n### Defining events\n\nEvents are anything that implement the `tuck.core/Event` protocol. The protocol defines a single\nmethod called `process-event` that takes the event and the current value of the app state and\nproduces a new app state.\n\nYou can define your events as records and define the implementation separately with\n`extend-protocol` or use the convenience macro `define-event` which defines the event record and the\nprocessing code. The `define-event` macro has an option called `:path` which is a vector defining a\npath in the app state where the update should take place. If no path is defined, the root app state\nis passed.\n\nSometimes events just need to assoc a value somewhere in the app state, there is a further\nconvenience macro called `define-assoc-events` which takes alternating names and app state paths.\n\n### Asynchronous events\n\n**NOTE:** There is a newer (easier to test) way to do side effects, see \"Returning effects\" below.\n\nAn application will most likely need some asynchronous events to communicate with servers or set\ntimeouts. This can be done by using `tuck.core/send-async!` which must be called within event\nprocessing code. The `send-async!` function takes an event constructor and optional arguments\nand returns a callback that will create and and apply the event when called.\n\nYou can use `send-async!` to create callbacks to pass to XHR calls.\n\n```clojure\n;; Simple async example\n(define-event FetchThings\n  {}\n  ;; Launch an XHR call and set events events as callbacks\n  (GET! \"/fetch-things\" {:on-success (t/send-async! -\u003eFetchThingsResponse)\n                         :on-failure (t/send-async! -\u003eServerError)})\n  ;; Return new app state\n  (assoc app :fetching-things? true))\n```\n\n### Returning effects\n\nA `process-event` implementation may also need to fire off effects like HTTP calls or\nother side effects. This can be done by returning an effect descriptor with the `fx` function.\n\nThe `fx` function takes the new app state and one (or more) effects that should be run after\nthe `process-event` is done. An effect may be a function with one argument (the `e!` control handle)\nor a map with a `:tuck.effect/type` keyword describing the effect to process.\n\nFor function effects, they are simply called and can send further events by calling the `e!` parameter.\n\nFor maps, they are run with `tuck.effect/process-effect` multimethod. To create new types of effects,\nsimply add implementations for that method.\n\n```clojure\n;; Same simple event, as in the above send-async! example\n(define-event FetchThings\n  {}\n  (fx\n    ;; Return new app state\n    (assoc app :fetching-things? true)\n\n    ;; Launch an XHR call by an effect descriptor\n    {:tuck.effect/type :http\n     :url \"/fetch-things\"\n     :on-success -\u003eFetchThingsResponse}))\n```\n\nTuck does not provide any effect types out of the box. You must provide an implementation of the\n`:http` effect type in order to use it.\n\n\n## Changes\n\n### Version 20181204\n\n* New effect system that is easier to test (see `fx` function and `tuck.effect` namespace)\n\n### Version 20180722\n\n* Changed: improved docstrings (no code changes)\n\n### Version 20180721\n\n* Added: `define-assoc-events` convenience macro\n\n### Version 20180327\n\n* Added: debugger supports watches\n* Fixed: debugger state handling improvements\n\n### Version 0.4.4 (2018-03-01)\n\n* Added: new (somewhat) experimental `define-event` macro for defining the record and implementation in one go\n\n### Version 0.4.3 (2017-10-20)\n\n* Performance improvement: Evaluate options once during component creation.\n\n### Version 0.4.2 (2017-10-09)\n\n* Bugfix: Fix binding of *current-send-function*\n\n### Version 0.4.1 (2017-06-28)\n\nMinor fix release\n\n* change clojure.spec namespaces to .alpha suffixed\n* allow 0 or more args to send-async! fn (#4)\n\n### Version 0.4 (2017-04-19)\n\nSupport clojure.spec validation of app state.\nAdd options map as 3rd argument that can specify new options:\n\n* :spec\n* :on-invalid-state\n\n\n## Usage\n\nClone this repo and run \"lein figwheel dev\" in the examples folder.\n","funding_links":[],"categories":["Awesome ClojureScript"],"sub_categories":["State Management"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftatut%2Ftuck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftatut%2Ftuck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftatut%2Ftuck/lists"}