spark.spec-tacular
A database-agnostic DSL for data specifications
check-component!
(check-component! spec k v)
Checks the field name k
and its value v
against the given spec, errors if v
is not of the correct type. Returns true
otherwise.
coerce
(coerce spec kw val)
Coerces val
into something that could be put in the kw
field of spec
. Returns nil
only if the value is coerced to nil
; i.e. if val
does not have a coercion
the val
is returned unharmed.
database-coercion
multimethod
Returns the coercion function that maps an object of a known database type (currently datomic.query.EntityMap
) to a map that can be used in a spec constructor. Defaults to nil
if the object does not come from a known database type.
defenum
macro
added in 0.6.0
(defenum & stx)
Defines an enumeration of values under a parent name.
(defenum Name docstring? symbol ...)
The resulting enumeration recognizes :Name/<symbol>
keywords, which answer true
to name?
. Enumerations do not define constructors as keywords are already Clojure values.
defspec
macro
(defspec & stx)
Defines a spec-tacular spec type.
(defspec Name
docstring?
[field-name arity type option ...]
...)
creates the spec :Name
; where arity is either :is-a
or :is-many
and type is either another spec name or a primitive type keyword. The docstring is optional; if given, it will become the :doc metadata on the spec var.
spec-tacular supports base types :keyword
, :string
, :boolean
, :long
, :bigint
, :float
, :double
, :bigdec
, :instant
, :calendarday
, :uuid
, :uri
, and :bytes
.
Fields are allowed to have the following options:
:unique
and:identity
, meaning only one entity can have a given value for this attribute in the database:link
, meaning the instance is always passed by-reference:component
, mutually exclusive with:link
, means the instance only exists when tied to it’s parent:required
, a required field
A core.typed alias Name
is created, as well as a constructor name
and a predicate name?
.
Spec instances that are created from a database may also contain the :db-ref
field which, in the case of Datomic, contains a map {:eid
database-id}
containing the entity’s :db/id
.
defunion
macro
(defunion & stx)
Defines a spec-tacular union.
(defunion Name docstring? :SpecName ...)
where each SpecName is another spec to be added to the union.
diff
(diff sp1 sp2)
Takes two spec instances and returns a vector of three maps created by calling clojure.data/diff
on each item of the spec.
Only well defined when sp1
and sp2
share the same spec.
For :is-many
fields, expect to see sets of similarities or differences in the result, as order should not matter.
get-spec
multimethod
Acts like the identity function if sent an actual spec object.
Otherwise, if there is a spec (defined with defspec, defunion, or defenum) such that:
- the spec has the
:name
denoted by the argument, when given a keyword; - the spec instance is an instance of that spec, when given an actual spec instance;
then returns that spec, otherwise returns nil
.
get-type
multimethod
Returns a SpecType record containing the name
of the type, the class type
, a symbol type-symbol
that would eval to the type
(useful for macros), and possibly a coercion
function.
identical-keys
(identical-keys si)
Takes any number of spec instances and returns the keys for fields with values they all share in common. Useful for error messages.
namespace->specs
(namespace->specs namespace)
Returns a sequence containing every spec in the given namespace.
primitive?
(primitive? spec-name)
Returns true
if the given spec name is primitive (i.e. is part of the base type environment and not defined as a spec), false
otherwise. See defspec for a list of primitive type keywords.
Note that all specs defined by defenum
are considered primitive, since their values are all keywords.
recursiveness
(recursiveness {[_ t] :type})
Returns :non-rec
if the given item is primitive?, :rec
otherwise.
refless
added in 0.5.0
(refless si)
Returns a version of the given spec instance with no :db-ref
s on any sub-instance.
refless=
added in 0.5.0
(refless= x y)
Given any walkable collection, returns true
if the two collections would be =
if no spec instances had :db-ref
s.
Contains a fast path if both x
and y
have specs, otherwise expect bad asymptotics as each collection must be rebuilt without :db-ref
s.
spec-meta
added in 0.6.0
(spec-meta ns spec spec-meta)
Parses the meta-data that can be placed on a namespace containing spec definitions, or on spec definitions themselves. Not intended to be called directly.
Currently, it is possible to override the constructor, predicate, and type aliases name for a single spec or all specs in a namespace.
(ns my-ns
{....
:spec-tacular
{:ctor-name-fn (fn [s] ....)
:huh-name-fn (fn [s] ....)
:alias-name-fn (fn [s] ....)}
....}
(:require ....))
Each function should expect a string and return a string. The function is currently eval
uated in the spark.spec-tacular namespace, so plan accordingly.
These names can be overriden on a per-spec basis in a similar fashion.
(defspec
^{:ctor-name "mk-my-spec"
:huh-name false ;; fall back to default
:alias-name "my-spec-type"}
my-spec
....)
The spec-level meta-data has higher priority than the namespace-level meta-data.
SpecInstance
The broadest type for a spec instance, but it is preferable to use an alias defined via defspec.