• Stars
    star
    206
  • Rank 190,504 (Top 4 %)
  • Language
    Clojure
  • License
    MIT License
  • Created over 7 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Coerce by leveraging your specs

spec-coerce Clojars Project Test cljdoc badge

A Clojure(script) library designed to leverage your specs to coerce your information into correct types.

Spec Coerce will remain in alpha while clojure.spec still in alpha.

Usage

Learn by example:

(ns spec-coerce.example
  (:require
    [clojure.spec.alpha :as s]
    [spec-coerce.core :as sc]))
    
; Define a spec as usual
(s/def ::number int?)

; Call the coerce method passing the spec and the value to be coerced
(sc/coerce ::number "42") ; => 42

; Like spec generators, when using `and` it will use the first item as the inference source
(s/def ::odd-number (s/and int? odd?))
(sc/coerce ::odd-number "5") ; => 5

; When inferring the coercion, it tries to resolve the upmost spec in the definition
(s/def ::extended (s/and ::odd-number #(> % 10)))
(sc/coerce ::extended "11") ; => 11

; Nilables are considered
(s/def ::nilable (s/nilable ::number))
(sc/coerce ::nilable "42") ; => 42
(sc/coerce ::nilable "nil") ; => nil
(sc/coerce ::nilable "foo") ; => "foo"

; The coercion can even be automatically inferred from specs given explicitly as sets of a homogeneous type
(s/def ::enum #{:a :b :c})
(sc/coerce ::enum ":a") ; => :a

; If you wanna play around or use a specific coercion, you can pass the predicate symbol directly
(sc/coerce `int? "40") ; => 40

; Parsers are written to be safe to call, when unable to coerce they will return the original value
(sc/coerce `int? "40.2") ; => "40.2" 
(sc/coerce `inst? "date") ; => "date" 

; To leverage map keys and coerce a composed structure, use coerce-structure
(sc/coerce-structure {::number      "42"
                      ::not-defined "bla"
                      :sub          {::odd-number "45"}})
; => {::number      42
;     ::not-defined "bla"
;     :sub          {::odd-number 45}}

; coerce-structure supports overrides, so you can set a custom coercer for a specific context, and can be also a point
; to set coercer for unqualified keys
(sc/coerce-structure {::number      "42"
                      ::not-defined "bla"
                      :unqualified  "12"
                      :sub          {::odd-number "45"}}
                     {::sc/overrides {::not-defined `keyword?
                                      :unqualified  ::number}})
; => {::number      42
;     ::not-defined :bla
;     :unqualified  12
;     :sub          {::odd-number 45}}

; If you want to set a custom coercer for a given spec, use the spec-coerce registry
(defrecord SomeClass [x])
(s/def ::my-custom-attr #(instance? SomeClass %))
(sc/def ::my-custom-attr #(map->SomeClass {:x %}))

; Custom registered keywords always takes precedence over inference
(sc/coerce ::my-custom-attr "Z") ; => #user.SomeClass{:x "Z"}

; Coercers in the registry can be overriden within a specific context
(binding [sc/*overrides* {::my-custom-attr keyword}]
  (sc/coerce ::my-custom-attr "Z")) ; => :Z

Examples from predicate to coerced value:

; Numbers
(sc/coerce `number? "42")                                   ; => 42.0
(sc/coerce `integer? "42")                                  ; => 42
(sc/coerce `int? "42")                                      ; => 42
(sc/coerce `pos-int? "42")                                  ; => 42
(sc/coerce `neg-int? "-42")                                 ; => -42
(sc/coerce `nat-int? "10")                                  ; => 10
(sc/coerce `even? "10")                                     ; => 10
(sc/coerce `odd? "9")                                       ; => 9
(sc/coerce `float? "42.42")                                 ; => 42.42
(sc/coerce `double? "42.42")                                ; => 42.42
(sc/coerce `zero? "0")                                      ; => 0

; Numbers on CLJS
(sc/coerce `int? "NaN")                                     ; => js/NaN
(sc/coerce `double? "NaN")                                  ; => js/NaN

; Booleans
(sc/coerce `boolean? "true")                                ; => true
(sc/coerce `boolean? "false")                               ; => false
(sc/coerce `true? "true")                                   ; => true
(sc/coerce `false? "false")                                 ; => false

; Idents
(sc/coerce `ident? ":foo/bar")                              ; => :foo/bar
(sc/coerce `ident? "foo/bar")                               ; => 'foo/bar
(sc/coerce `simple-ident? ":foo")                           ; => :foo
(sc/coerce `qualified-ident? ":foo/baz")                    ; => :foo/baz
(sc/coerce `keyword? "keyword")                             ; => :keyword
(sc/coerce `keyword? ":keyword")                            ; => :keyword
(sc/coerce `simple-keyword? ":simple-keyword")              ; => :simple-keyword
(sc/coerce `qualified-keyword? ":qualified/keyword")        ; => :qualified/keyword
(sc/coerce `symbol? "sym")                                  ; => 'sym
(sc/coerce `simple-symbol? "simple-sym")                    ; => 'simple-sym
(sc/coerce `qualified-symbol? "qualified/sym")              ; => 'qualified/sym

; Collections
(sc/coerce `(s/coll-of int?) ["5" "11" "42"])               ; => [5 11 42]
(sc/coerce `(s/coll-of int?) ["5" "11.3" "42"])             ; => [5 "11.3" 42]
(sc/coerce `(s/map-of keyword? int?) {"foo" "42" "bar" "31"})
; => {:foo 42 :bar 31}

; Branching
; tests are realized in order
(sc/coerce `(s/or :int int? :bool boolean?) "40")           ; 40
(sc/coerce `(s/or :int int? :bool boolean?) "true")         ; true
; returns original value when no options can handle
(sc/coerce `(s/or :int int? :bool boolean?) "nil")          ; "nil"

; Tuple
(sc/coerce `(s/tuple int? string?) ["0" 1])                 ; => [0 "1"]

; Others
(sc/coerce `uuid? "d6e73cc5-95bc-496a-951c-87f11af0d839")   ; => #uuid "d6e73cc5-95bc-496a-951c-87f11af0d839"
(sc/coerce `inst? "2017-07-21")                             ; => #inst "2017-07-21T00:00:00.000000000-00:00"
(sc/coerce `nil? "nil")                                     ; => nil
(sc/coerce `nil? "null")                                    ; => nil

;; Clojure only:
(sc/coerce `uri? "http://site.com") ; => (URI. "http://site.com")
(sc/coerce `decimal? "42.42") ; => 42.42M
(sc/coerce `decimal? "42.42M") ; => 42.42M

;; Throw exception when coercion fails
(sc/coerce! `int? "abc") ; => throws (ex-info "Failed to coerce value" {:spec `int? :value "abc"})
(sc/coerce! :simple-keyword "abc") ; => "abc", coerce! doesn't do anything on simple keywords

;; Conform the result after coerce
(sc/conform `(s/or :int int? :bool boolean?) "40")          ; [:int 40]

;; Throw on coerce structure
(sc/coerce-structure {::number "42"} {::sc/op sc/coerce!})

;; Conform on coerce structure
(sc/coerce-structure {::number "42"} {::sc/op sc/conform})

License

Copyright Β© 2017 Wilker LΓΊcio

Distributed under the MIT License.

More Repositories

1

pathom

Pathom is a Clojure(script) engine for processing EQL requests.
Clojure
617
star
2

pathom3

A library for navigating data.
Clojure
369
star
3

mongoid_taggable

Mongoid taggable behaviour
Ruby
196
star
4

pathom-viz

Visualization tools for Pathom
Clojure
87
star
5

wsscode-async

Core.async utilities package.
Clojure
78
star
6

edn-json

Tools to convert back and forth between EDN and JSON, optimised for storage and JSON tooling friendliness.
Clojure
63
star
7

huffman_js

Huffman Algorithm in Javascript
JavaScript
57
star
8

tailwind-garden

Port of Tailwind CSS library to Garden format in Clojure
Clojure
56
star
9

lein-node-webkit-build

Build node-webkit applications with Leiningen
Clojure
51
star
10

pathom-datomic

Pathom Connect integration with Datomic
Clojure
39
star
11

js-data-interop

Clojurescript <-> JS data interop using specialized data structures.
Clojure
22
star
12

jcheck_rails

wilkerlucio
Ruby
21
star
13

jquery-autoscroll

Creates one automatic scroll system that moves with user mouse movements
JavaScript
21
star
14

jcheck

Javascript validations done right
JavaScript
19
star
15

pathom3-graphql

GraphQL Integration for Pathom 3
Clojure
18
star
16

jquery-multiselect

jQuery multiselect implementation
JavaScript
18
star
17

oge

Om Graph Explorer
Clojure
17
star
18

transito

Helpers for common Clojure transit operations
Clojure
17
star
19

subdb

Ruby client and API for SubDB download and upload
Ruby
16
star
20

pathom3-docs

Documentation and blog site for Pathom 3
Clojure
16
star
21

media-looper

Clojure
14
star
22

bow_and_arrow

The old bow and arrow windows game recreated with Ruby and Shoes
Ruby
12
star
23

aidmock

Safe mocking
Ruby
12
star
24

pathom-viz-connector

Clojure
11
star
25

dragonfly_mongoid_extensions

Add support for Mongoid in Dragonfly
Ruby
10
star
26

presentation-data-navigation-with-pathom3

Sources of demo for the presentation: Data Navigation with Pathom 3
Clojure
10
star
27

pathom-connect-youtube

Clojure
9
star
28

conj2018demo

Code used in Scaling Full Stack Applications presentation
Clojure
9
star
29

pathom3-datomic

Clojure
8
star
30

ember-youtube-manager

Ember demo implementing Youtube new interface for video manager.
JavaScript
8
star
31

multi-timer

Clojure
7
star
32

mazes

My studies on the Mazes for Programmers book
Clojure
7
star
33

youtube-gmail-queue-plugin

Clojure
7
star
34

barrier

A test framework that embraces promises for real
JavaScript
7
star
35

patterny

Automatically extract minimum pattern of an image
Clojure
7
star
36

limber

Limber PHP MVC Framework
PHP
6
star
37

clojure-days-clojure-graph-presentation

Clojure
6
star
38

om-cookbook

Receipt tutorials for Om library.
Clojure
6
star
39

pathom-connect-spacex

Clojure
6
star
40

cljc-misc

Clojure
6
star
41

calendar_iterator

Calendar Iterator is a simple API to iterate of a calendar days of a month
Ruby
5
star
42

dart-stylus

Dart
5
star
43

chunked-transfer

Stream transfer of clojure data structures
Clojure
5
star
44

pathom-book

Documentation for Pathom
5
star
45

limber-haml

PHP Implementation of Haml
PHP
5
star
46

pathom3-integrant

Clojure
5
star
47

cocoa_simple_multipart

Objective-C
4
star
48

pencil-draw

Pencil draw experiment
JavaScript
4
star
49

scripted_css

Make your CSS's more powerful than ever!
CoffeeScript
4
star
50

fishy-framework

Prototype of php web framework
PHP
3
star
51

promesa-bridges

Clojure
3
star
52

limber-spec

PHP BDD Library created for Limber Framework
PHP
3
star
53

future_goodies

Future goodies for Dart
Dart
3
star
54

fulcro-graphql

Clojure
3
star
55

collabsubtitles-extension

Clojure
3
star
56

dotfiles

My dotfiles
Vim Script
3
star
57

jena-clj

Clojure
3
star
58

fuzzy-cljs

JavaScript
2
star
59

pathom3-viz

Clojure
2
star
60

pathom3-demo-gcf

Clojure
2
star
61

spotify-pathom

Source code for tutorial on pathom spotify
Clojure
2
star
62

pathom-connect-ynab

Clojure
2
star
63

pathom-cljs-demo

Simple repository with a demo setup for Pathom in CLJS with Shadow
Clojure
2
star
64

fishy-framework-bundles

Textmate Bundles for Fishy Framework
1
star
65

wilker-tmbundles

My Custom general bundles
1
star
66

spec-inspec

Helpers to navigate Clojure specs.
Clojure
1
star
67

cljs-compilation-fail

Clojure
1
star
68

alfred_ex_chrome_bookmarks

Elixir
1
star
69

match-comb-shadow-repro

Clojure
1
star
70

fishy-framework-base

Basic directory structure for using with Fishy Framework
PHP
1
star
71

remember-mobile

Clojure
1
star
72

ws.squares

WebSockets experiment with Node
JavaScript
1
star
73

d3-in-action-storybook

HTML
1
star
74

simplehttp

Simple HTTP import from rubyforge
Ruby
1
star
75

mower

Clojure
1
star
76

staticfy

Ruby
1
star
77

inject-it

Simple Javascript injector
CoffeeScript
1
star
78

subdb-sync

SubDB GUI
Ruby
1
star
79

todomvc-cljs

HTML
1
star
80

wilkerlucio.github.com

HTML
1
star
81

clj-subdb

Clojure SubDB API
Clojure
1
star
82

icontact

modified copy of nate63179-icontact
Ruby
1
star
83

rabbit-forms

CRUD related library for CodeIgniter
1
star
84

html-om

HTML to Om converter
JavaScript
1
star
85

absin

A crazy programming language
CoffeeScript
1
star
86

cooktimer

Clojure
1
star