{"id":13760347,"url":"https://github.com/jimmythompson/halboy","last_synced_at":"2025-02-27T21:41:05.436Z","repository":{"id":22479378,"uuid":"96367949","full_name":"jimmythompson/halboy","owner":"jimmythompson","description":"A library for generating, and interacting with, Hypertext Application Language","archived":false,"fork":false,"pushed_at":"2023-07-19T09:12:47.000Z","size":151,"stargazers_count":27,"open_issues_count":1,"forks_count":17,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-16T17:41:07.691Z","etag":null,"topics":["clojure","hal","hypermedia","hypermedia-client","json"],"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/jimmythompson.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,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-05T23:04:10.000Z","updated_at":"2023-05-26T15:34:23.000Z","dependencies_parsed_at":"2024-01-15T03:45:56.906Z","dependency_job_id":"35f6acc2-2019-4f3a-9e73-1d56e30180ee","html_url":"https://github.com/jimmythompson/halboy","commit_stats":{"total_commits":173,"total_committers":10,"mean_commits":17.3,"dds":"0.21965317919075145","last_synced_commit":"7e0b1fcb072851520917476ade692f7dbf6f9962"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimmythompson%2Fhalboy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimmythompson%2Fhalboy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimmythompson%2Fhalboy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jimmythompson%2Fhalboy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jimmythompson","download_url":"https://codeload.github.com/jimmythompson/halboy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241062289,"owners_count":19902865,"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","hal","hypermedia","hypermedia-client","json"],"created_at":"2024-08-03T13:01:08.357Z","updated_at":"2025-02-27T21:41:05.393Z","avatar_url":"https://github.com/jimmythompson.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"# halboy\n\n[![Current Version](https://clojars.org/halboy/latest-version.svg)](https://clojars.org/halboy)\n\nA Clojure library for all things hypermedia.\n\n* Create hypermedia resources\n* Marshal to and from JSON, or a map\n* Navigate JSON+HAL APIs\n\n## New in version 6\n\n* Thanks to a great contribution from @danielzurawski, we now support clj-http. In version 6, this is now the default.\n\n## API\n\n### Resources\n\nWith Halboy you can create resources, and pull information from them.\n\n```clojure\n(require '[halboy.resource :as hal])\n\n(def my-resource\n    (-\u003e (hal/new-resource \"/orders/123\")\n        (hal/add-link :creator \"/users/rob\")\n        (hal/add-resource :discount (-\u003e (hal/new-resource \"/discounts/1256\")\n                                         (hal/add-property :discount-percentage 10)))\n        (hal/add-resource :items [(-\u003e (hal/new-resource \"/items/534\")\n                                     (hal/add-property :price 25.48))])\n        (hal/add-property :state :dispatching)))\n\n(hal/get-link my-resource :self)\n; { :href \"/orders/123\" }\n\n(hal/get-href my-resource :creator)\n; \"/users/rob\"\n\n(hal/get-property my-resource :state)\n; :dispatching\n\n(-\u003e (hal/get-resource my-resource :discount)\n    (hal/get-property :discount-percentage))\n; 10\n\n(-\u003e (hal/get-resource my-resource :items)\n    (first)\n    (hal/get-property :price))\n; 25.48\n```\n\n### Marshalling\n\nYou can also marshal your hal resources to and from maps, or JSON.\n\n```clojure\n(require '[halboy.resource :as hal])\n(require '[halboy.json :as haljson])\n\n(def my-resource\n    (-\u003e (hal/new-resource \"/orders/123\")\n        (hal/add-link :creator \"/users/rob\")\n        (hal/add-resource :items (-\u003e (hal/new-resource \"/items/534\")\n                                     (hal/add-property :price 25.48)))\n        (hal/add-property :state :dispatching)))\n\n(haljson/resource-\u003emap my-resource)\n; {:_links    {:self {:href \"/orders/123\"},\n;              :creator {:href \"/users/rob\"}},\n;  :_embedded {:items {:_links {:self {:href \"/items/534\"}},\n;                      :price  25.48}},\n;   :state    :dispatching}\n\n(haljson/resource-\u003ejson my-resource)\n; Formatted in these docs only.\n;\n; {\n;   \\\"_links\\\": {\n;     \\\"self\\\": {\n;       \\\"href\\\": \\\"/orders/123\\\"\n;     },\n;     \\\"creator\\\": {\n;       \\\"href\\\": \\\"/users/rob\\\"\n;     }\n;   },\n;   \\\"_embedded\\\": {\n;     \\\"items\\\": {\n;       \\\"_links\\\": {\n;         \\\"self\\\": {\n;           \\\"href\\\": \\\"/items/534\\\"\n;         }\n;       },\n;       \\\"price\\\": 25.48\n;     }\n;   },\n;   \\\"state\\\": \\\"dispatching\\\"\n; }\n\n(-\u003e (haljson/resource-\u003ejson my-resource)\n    (haljson/json-\u003eresource)\n    (hal/get-href :self))\n; \"/orders/123\"\n```\n\n### Navigation\n\nProvided you're calling a HAL+JSON API, you can discover the API and navigate\nthrough its links. When you've found what you want, you call\n`navigator/resource` and you get a plain old HAL resource, which you can inspect\nusing any of the methods above.\n\n```clojure\n(require '[halboy.resource :as hal])\n(require '[halboy.navigator :as navigator])\n\n; GET / - 200 OK\n; {\n;  \"_links\": {\n;    \"self\": {\n;      \"href\": \"/\"\n;    },\n;    \"users\": {\n;      \"href\": \"/users\"\n;    },\n;    \"user\": {\n;      \"href\": \"/users/{id}\",\n;      \"templated\": true\n;    }\n;  }\n;}\n\n(def users-result\n     (-\u003e (navigator/discover \"https://api.example.com/\")\n         (navigator/get :users))\n\n(navigator/status users-result)\n; 200\n\n(navigator/location users-result)\n; \"https://api.example.com/users\"\n\n(-\u003e (navigator/discover \"https://api.example.com/\")\n    (navigator/get :user {:id \"rob\"})\n    (navigator/location))\n; \"https://api.example.com/users/rob\"\n\n(def sue-result\n     (-\u003e (navigator/discover \"https://api.example.com/\")\n         (navigator/post :users {:id \"sue\" :name \"Sue\" :title \"Dev\"}))\n\n(navigator/location sue-result)\n; \"https://api.example.com/users/sue\"\n\n(-\u003e (navigator/resource sue-result)\n    (hal/get-property :title))\n; \"Dev\"\n```\n\n### Customisation\n\n### Custom HTTP clients\n\nHalboy offers an out-of-the-box HTTP client which, as of 6.0.0, uses clj-http. You can pass\na HTTP client into Halboy using the `:client` key of the settings. It must\nadhere to the `halboy.http.protocol.HttpClient` protocol.\n\n### HTTP Kit\n\nThere is an alternative http client, which uses HTTPKit. You can use it by passing it into the navigator settings:\n\n```clojure\n(navigator/discover \"https://api.example.com\"\n                    {:client           (halboy.http.http-kit/new-http-client)\n                     :follow-redirects true\n                     :http             {:headers {}}})\n```\n\n#### CachableHttpClient\nHalboy also offers an HTTP client with caching support which is an in-memory \ncache. It uses `clojure.core.cache TTLCache` for caching with the default TTL of \n2000 miliseconds which can be overridden to a different type of cache of choice\nor a different TTL. \n```clojure\n; default cachable client\n(navigator/discover \"https://api.example.com\"\n {:client           (halboy.http.cachable/new-http-client)\n  :follow-redirects true\n  :http             {:headers {}}})\n  \n; cachable client with an hour TTL\n(require '[clojure.core.cache :as cache])\n\n(def in-memory-cache (atom (cache/ttl-cache-factory {} :ttl 3600000)))\n  (navigator/discover \"https://api.example.com\"\n     {:client           (halboy.http.cachable/new-http-client in-memory-cache)\n      :follow-redirects true\n      :http             {:headers {}}})\n```\n#### HTTP settings\n\nAll settings under the `:http` key are passed into the HTTP client. These are\n_deep_ merged into each request, with keys on the request taking priority.\n\nThe request will always fill in the keys `:method`, `:url`, `:body`, and\n`:query-params`.\n\nHeaders specified in HTTP settings will be merged with headers defined by\n`set-header`. If they share the same key, the `set-header` call wins.\n\n## Contributing\n\nI'm happy to receive and go through feedback, bug reports, and pull requests.\n\nIf you need to contact me, my email is jimmy[at]jimmythompson.co.uk.\n\n### Development \nTo run the tests:\n\n```sh\n$ lein eftest\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimmythompson%2Fhalboy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjimmythompson%2Fhalboy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjimmythompson%2Fhalboy/lists"}