Matchbox is archived and incompatible with current Firebase APIs.
For new projects:
- ClojureScript: use the official Firebase JavaScript SDK.
- Clojure: use the official Firebase Admin Java SDK, or consider fire for Realtime Database access.
Matchbox is unlikely to be worth reviving:
- The client and Admin SDKs no longer have enough in common for one wrapper.
- The newer JavaScript SDK's function-based API works well directly from ClojureScript.
[matchbox "0.0.9"]The rest of this README documents the final release.
Matchbox offers more than just bindings:
- Atom-, zipper-, and cursor-like abstractions over Firebase references
- Conversion to and from Clojure data
- A uniform API for Clojure and ClojureScript
- Optional sequence abstraction—work with lists rather than sorted maps
- Optional core.async-based API
- Multiplexed child-event channels and callbacks
- Listener registry for scoped or global cleanup
Here's a quick taste:
(require '[matchbox.core :as m])
(def root (m/connect "https://<app>.firebaseio.com"))
(m/auth-anon root)
(m/listen-children
root [:users :mike :friends]
(fn [[event-type data]] (prn data)))
(def mikes-friends (m/get-in root [:users :mike :friends]))
(m/reset! mikes-friends [{:name "Kid A"} {:name "Kid B"}])
(m/conj! mikes-friends {:name "Jean"})
(m/deref
mikes-friends
(fn [key value]
(m/reset-in! root [:users :mike :num-friends]
(count value))))
(m/unauth root)See the quick introduction for a more complete tour.
For brevity, this table compares Matchbox only with the JavaScript Firebase API.
Notes:
- Almost all functions accept callbacks. Matchbox intercepts them so they receive hydrated data.
- This table is not exhaustive. Matchbox also wraps connectivity, queries, authentication, logging, and more.
| Matchbox | Firebase.js | Differences |
|---|---|---|
connect |
new Firebase |
Accepts a key or key path as an optional second argument |
get-in |
.child |
Accepts a string, symbol, keyword, or key path |
parent |
.parent |
— |
deref |
.once |
Always uses a value subscription |
deref-list |
.once |
Returns ordered values rather than a map; compatible with queries |
reset! |
.set |
Automatically serializes data |
reset-with-priority! |
.setWithPriority |
— |
merge! |
.update |
— |
conj! |
.push |
— |
swap! |
.transaction |
Applies locally by default and accepts additional arguments |
dissoc! or remove! |
.remove |
— |
set-priority! |
.setPriority |
— |
listen-to |
.on |
Stored in the registry for easy unsubscription |
listen-list |
.on |
Like deref-list for listen-to |
listen-children |
.on |
Multiplexes all child events through one callback |
Additionally, there are up to three variations of most functions:
*-invariants accept a key or key path as their second argument to refer directly to a child. These exist for all functions ending in!, as well asderefandderef-list.*<variants return a channel of results instead of taking a callback. These exist for all functions that would take a callback.*-in<variants combine the first two forms where applicable.
The latter two, when available, are defined in matchbox.async, allowing Matchbox to be used
without a core.async dependency.
There are ClojureScript demos for Reagent and
Om in the examples directory. Run boot dev from that directory,
then open http://localhost:3000 in a browser.
-
swap!takes its callback in a non-standard positionSince we support passing additional arguments to an update function, we can't use an optional argument for the callback.
Our solution draws inspiration from keyword-argument-style signatures, such as
(my-function :has "keyword" :style "arguments").For
swap!, pass:callback callback-fnat the end of the argument list:(m/swap! r f) ;; call (f <val>), no callback (m/swap! r f b c) ;; call (f <val> b c), no callback (m/swap! r f :callback cb) ;; call (f <val>), with callback `cb` (m/swap! r f b c :callback cb) ;; call (f <val> b c), with callback `cb`
:callbackmust appear as the second-last argument. -
JVM callbacks may run on another thread
Depending on your environment and Firebase configuration, callbacks may be triggered on another thread.
This can be confusing when debugging with
prn, as*out*may not point to the REPL's writer.matchbox.utils/prnensures that output is visible. -
Serialization
Data Storage Reads back as it writes? {}, nameable keysJSON Not unless all keys are keywords (rest are coerced) {}, richer keysNot supported N/A []JSON with numeric keys Yes #{}JSON with numeric keys No, reads back as a vector "string"string Yes :a/keyword":a/keyword"Yes Number Number Mostly; some java.math.*types differdefrecordinstanceJSON No, reads back as a vanilla map deftypeinstanceJSON No, reads back as a vanilla map Other Depends on platform Expect useless strings (JS) or serious downcasting (JVM) See the serialization documentation for more information.
Matchbox began as a fork of Pani.
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.
