A metosin/malli schema implementation for clj-commons/ring-buffer, providing validation, generation, and transformation capabilities for ring buffers in Clojure and ClojureScript.
Alpha
There is decent test coverage, but this hasn't yet been tested in production. Use at your own risk. Issues and PRs are welcome!
Latest version: 0.0.1
This library is currently not hosted on clojars, and must be required as a git dep in deps.edn
:
psyclyx/malli-amalloy-ring-buffer {:git/url "https://github.com/psyclyx/malli-amalloy-ring-buffer"
:git/tag "0.0.1"
:git/sha "10b290287d8f"}
Note: This library doesn't declare metosin/malli
or clj-commons/ring-buffer
as dependencies, but does require namespaces from both.
Snippets in this section assume the following requires:
(require
'[amalloy.ring-buffer :as rb]
'[malli.core :as m]
'[malli.error :as me]
'[malli.generator :as mg]
'[malli.registry :as mr]
'[malli.transform :as mt]
'[psyclyx.malli.amalloy.ring-buffer :as malli.rb])
Make the schema accessible as :amalloy/ring-buffer
by adding it to the default registry:
(mr/set-default-registry!
(mr/composite-registry
(m/default-schemas)
malli.rb/registry))
(m/validate [:amalloy/ring-buffer :int]
(into (rb/ring-buffer 3) [1 2 3])) ; => true
(m/validate [:amalloy/ring-buffer :int]
[1 2 3]) ; => false
Ring buffer capacity can be constrained using schema properties:
:capacity
- exact capacity requirement:min-capacity
- minimum capacity bound:max-capacity
- maximum capacity boundNote that :capacity
cannot be combined with min/max bounds.
(m/validate [:amalloy/ring-buffer {:capacity 5} :int]
(rb/ring-buffer 5)) ; => true
(m/validate [:amalloy/ring-buffer {:capacity 5} :int]
(rb/ring-buffer 10)) ; => false
(m/validate [:amalloy/ring-buffer {:min-capacity 5} :int]
(rb/ring-buffer 5)) ; => true
(m/validate [:amalloy/ring-buffer {:min-capacity 5} :int]
(rb/ring-buffer 3)) ; => false
(m/validate [:amalloy/ring-buffer {:max-capacity 5} :int]
(rb/ring-buffer 3)) ; => true
(m/validate [:amalloy/ring-buffer {:max-capacity 5} :int]
(rb/ring-buffer 10)) ; => false
Explanations can be humanized to meaningful error messages in English. Localization PRs are welcome!
(->> [1 2 3]
(m/explain [:amalloy/ring-buffer :int])
me/humanize) ; => ["should be a ring buffer"]
(->> (into (rb/ring-buffer 3) [1 2 3])
(m/explain [:amalloy/ring-buffer {:capacity 5} :int])
me/humanize) ; => ["should have capacity 5"]
(->> (into (rb/ring-buffer 3) [1 2 3])
(m/explain [:amalloy/ring-buffer {:min-capacity 4} :int])
me/humanize) ; => ["should have capacity >= 4"]
(->> (into (rb/ring-buffer 3) [1 2 3])
(m/explain [:amalloy/ring-buffer {:min-capacity 1 :max-capacity 2} :int])
me/humanize) ; => ["should have capacity between 1 and 2"]
(->> (into (rb/ring-buffer 3) [1 2 3])
(m/explain [:amalloy/ring-buffer :string])
(me/humanize)) ; => [["should be a string"] ["should be a string"] ["should be a string"]]
Ring buffers can be generated from schemas.
(mg/generate [:amalloy/ring-buffer :int]) ; => #amalloy/ring-buffer [59 (-32326 -19097 1154648 94 -1 -67 -72204790)]
Capacity constraints are respected.
(mg/generate [:amalloy/ring-buffer {:capacity 3} :int]) ; => #amalloy/ring-buffer [3 (3838072 503345 7160549)]
(mg/generate [:amalloy/ring-buffer {:min-capacity 3} :int]) ; => #amalloy/ring-buffer [11 (1707179 -1 217 -3 -814 -71 644 -2 -561 0 118606)]
:amalloy/ring-buffer
schemas support encoding/decoding with transformers.
Ships with ring-buffer-transformer
, which transforms between ring buffers and sequentials.
(m/encode [:amalloy/ring-buffer :int]
(into (rb/ring-buffer 5) [1 2 3])
(malli.rb/ring-buffer-transformer)) ; => [1 2 3]
(m/decode [:amalloy/ring-buffer :int]
[1 2 3]
(malli.rb/ring-buffer-transformer)) ; => #amalloy/ring-buffer [3 (1 2 3)]
When decoding collections without an exact :capacity
, it will be inferred from the sequential being decoded and any capacity bounds.
(m/decode [:amalloy/ring-buffer {:min-capacity 3} :int]
[1 2 3 4 5]
(malli.rb/ring-buffer-transformer)) ; => #amalloy/ring-buffer [5 (1 2 3 4 5)]
When decoding sequences shorter than the required capacity, the ring buffer is created with the specified capacity but only partially filled:
(m/decode [:amalloy/ring-buffer {:min-capacity 3} :int]
[1 2]
(malli.rb/ring-buffer-transformer)) ; => #amalloy/ring-buffer [3 (1 2)]
This applies to both :capacity
and :min-capacity
constraints.
When the sequence being decoded exceeds :capacity
or
:max-capacity
, decoding will fail by default. This is to prevent
seemingly successful decodes from unintentionally dropping data.
(m/decode [:amalloy/ring-buffer {:capacity 3} :int]
(range 5)
(malli.rb/ring-buffer-transformer)) ; => (0 1 2 3 4)
(m/decode [:amalloy/ring-buffer {:max-capacity 3} :int]
(range 5)
(malli.rb/ring-buffer-transformer)) ; => (0 1 2 3 4)
This behavior can be disabled with :overflow
.
(m/decode [:amalloy/ring-buffer {:max-capacity 3} :int]
(range 5)
(malli.rb/ring-buffer-transformer {:overflow true})) ; => #amalloy/ring-buffer [3 (2 3 4)]
Of course, malli transformers become much more useful when composed and used to transform larger structures.
(m/encode
[:map [:foo/xs [:amalloy/ring-buffer :int]]]
{:foo/xs (into (rb/ring-buffer 5) [1 2 3])}
(mt/transformer
(mt/key-transformer {:encode name})
(malli.rb/ring-buffer-transformer)
(mt/string-transformer))) ; => {"xs" ["1" "2" "3"]}
(m/decode
[:map [:foo/xs [:amalloy/ring-buffer {:capacity 5} :int]]]
{"xs" ["1" "2" "3"]}
(mt/transformer
(mt/key-transformer {:decode {"xs" :foo/xs}})
(malli.rb/ring-buffer-transformer)
(mt/string-transformer))) ; => {:foo/xs #amalloy/ring-buffer [5 (1 2 3)]}
There's a .dir-locals.el
that should make CLJ and CLJS repls work
OOTB on Emacs + CIDER. Just cider-jack-in-clj
or
cider-jack-in-cljs
from a clojure-mode buffer.
For other editors, you'll need to make sure your editor is including
the :libs
and :dev
aliases, and is injecting whatever additional
middleware it needs. The :cider
and :nrepl
aliases may be useful
here. When in doubt, consult your editor's documentation.
As an alternative, starting an nREPL with cider support from the CLI is likely sufficient to enable connecting from your editor.
clj -M:libs:dev:nrepl:cider
This starts an nREPL that's usable from the CLI and accepts connections from many editors.
To switch to a CLJS repl, require repl
and evaluate (repl/start-cljs)
.
The ClojureScript test runner requires an npm dependency, which can be installed with
npm i
clj -M:libs:test
clj -M:libs:test --focus :clj
clj -M:libs:test --focus :cljs
clj -T:build clean
clj -T:build jar
Requires a previously-built jar.
clj -T:build install
clj -T:build ci
CLOJARS_USERNAME
and CLOJARS_TOKEN
must be set.
clj -T:build ci && clj clj -T:build deploy
clj -M:outdated
clj -M:outdated --upgrade
clj -M:cljstyle check
clj -M:cljstyle fix
Issues and pull requests welcome!
Copyright (c) 2025 Alice Burns
Released under the MIT license.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close