@xsc xsc released this Sep 21, 2016 · 172 commits to master since this release

Assets 2

I'm very proud to announce the latest release of claro, a library to streamline your data access, providing powerful optimisations and abstractions along the way.

Check out the Basic Resolution guide or the Quickstart README section for an introduction!

Infinity ...

This release sees claro's increased support for infinite trees, causing a significant shift in focus. We move away from inline transformations that wrap a Resolvable and are run once a concrete value is available – instead, we can now rely on an abstract description on how to convert an infinite tree to a finite one, a so-called projection.

Previously, similar to muse or urania, we built an abstract resolvable tree, inlining transformation logic close to the affected nodes:

(-> (->Person 1)
    (data/then
      (fn [person]
        (-> person
            (select-keys [:id :name :friends])
            (update :friends #(data/map :name %))))))

Considering claro supports infinite trees we have to be very careful to only apply the transformation once all necessary data is available – but, then again, we can't wait for a value to be fully resolved since we don't have any guarantees that that'll ever happen. This resulted in the introduction of conditional composition, guarding transformations with predicates asserting the shape of a subtree before function application.

Needless to say, there were surprises and extended README sections on what to especially look out for. We can do better.

Consider the following projection which'll do the same thing as the piece of code above:

(def person-with-friend-names
  {:id      projection/leaf
   :name    projection/leaf
   :friends [(projection/extract :name)]})

(-> (->Person 1)
    (projection/apply person-with-friend-names))

Projections are queries and schemas at once. They are declarative, composable and powerful, and they'll let you cleanly separate external transformation logic from your data access (which can now seamlessly produce dumb, rich, infinite trees). Moreover, together with claro's improved mocking capabilities projections are independently testable.

All in all, I firmly believe that this is a big step forward. If you agree, disagree or just want to chat don't hesitate to open an issue or contact me on Twitter.

To infinity!

... and Beyond!

The idea of projections is heavily inspired by GraphQL. It seems thus only natural that there should be a GraphQL layer on top of claro – which I expect to be a rather straightforward endeavour, seeing as projections are a superset of GraphQL queries.

If you're also interested in this – or have ideas/drive/code towards a concrete implementation – feel free to contact me. This is, in my opinion, the next logical step.

Release Notes

Auto-Generated Documentation | Guides

Breaking Changes

  • data manipulation functions have been moved out of claro.data into claro.data.ops,
  • wrap-select has been removed – use selectors instead,
  • wrap-resolve has been removed – use wrap instead,
  • the override and overrides middlewares have been removed – use the ones in claro.middleware.mock instead,
  • the trace and trace-stats middlewares have been removed – use the ones in claro.middleware.observe instead.

Features

  • resolve-batch! can now either return an in-order seq of results or a map of resolvable/result pairs,
  • claro.data/Transform has been added to separate pure and impure resolvable logic,
  • new projections & capabilities:
    • alias: renames a key,
    • bind/let: uses a partial result to generate the actual projection,
    • case: dispatches on the Resolvable type,
    • conditional: dispatches on a partial Resolvable result,
    • default/maybe: replaces or accepts nil values,
    • extract: reads a leaf value from a subtree,
    • parameters: injects values into Resolvable records,
    • prepare: transforms Resolvable records before resolution,
    • transform: transforms Resolvable results,
    • union/conditional-union: merges projection results,
    • value: injects concrete values (which, themselves, can be Resolvable records).
  • better error messages for projections,
  • introduces adapters (claro.engine.adapter),
  • introduces selectors (claro.engine.selector), allowing stateful resolution strategies,
  • adds middlewares:
    • claro.middleware.observe: engine introspection middlewares,
    • claro.middleware.transform: resolution result transformation middlewares,
    • claro.middleware.mock: data source mocking middlewares,
    • claro.middleware.deferred: deferred value transformation middlewares.

Artifact Coordinates

[claro "0.2.0]