Advertisement
Guest User

Untitled

a guest
Feb 2nd, 2017
139
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.52 KB | None | 0 0
  1. (ns clj-spec-playground
  2. (:require [clojure.string :as str]
  3. [clojure.spec :as s]
  4. [clojure.test.check.generators :as gen]))
  5.  
  6. ;;; examples of clojure.spec being used like a gradual/dependently typed system.
  7.  
  8. (defn make-user
  9. "Create a map of inputs after splitting name."
  10. ([name email]
  11. (let [[first-name last-name] (str/split name #"\ +")]
  12. {::first-name first-name
  13. ::last-name last-name
  14. ::email email}))
  15. ([name email phone]
  16. (assoc (make-user name email) ::phone (Long/parseLong phone))))
  17.  
  18.  
  19. (defn cleanup-user
  20. "Fix names, generate username and id for user."
  21. [u]
  22. (let [{:keys [::first-name ::last-name]} u
  23. [lf-name ll-name] (map (comp str/capitalize str/lower-case)
  24. [first-name last-name])]
  25. (assoc u
  26. ::first-name lf-name
  27. ::last-name ll-name
  28. ::uuid (java.util.UUID/randomUUID)
  29. ::username (str/lower-case (str "@" ll-name)))))
  30.  
  31. ;;; and now for something completely different!
  32. ;;; specs!
  33.  
  34. ;;; Do NOT use this regexp in production!
  35. (def ^:private email-re #"(?i)[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}")
  36.  
  37. (defn ^:private ^:dynamic valid-email?
  38. [e]
  39. (re-matches email-re e))
  40.  
  41. (defn ^:private valid-phone?
  42. [n]
  43. ;; lame. do NOT copy
  44. (<= 1000000000 n 9999999999))
  45.  
  46. ;;; map specs
  47. (s/def ::first-name (s/and string? #(<= (count %) 20)))
  48. (s/def ::last-name (s/and string? #(<= (count %) 30)))
  49. (s/def ::email (s/and string? valid-email?))
  50. (s/def ::phone (s/and number? valid-phone?))
  51.  
  52. (def user-spec (s/keys :req [::first-name ::last-name ::email]
  53. :opt [::phone]))
  54.  
  55. ;;; play with the spec rightaway...
  56. ;;; conform can be used for parsing input, eg. in macros
  57. (s/conform user-spec {::first-name "anthony"
  58. ::last-name "gOnsalves"
  59. ::email "a.gonsalves@GMAIL.com"
  60. ::phone 9820740784})
  61.  
  62.  
  63. ;;; sequence specs
  64.  
  65. (s/def ::name (s/and string? #(< (count %) 45)))
  66. (s/def ::phone-str (s/and string? #(= (count %) 10)))
  67.  
  68. (def form-spec (s/cat :name ::name
  69. :email ::email
  70. :phone (s/? ::phone-str)))
  71.  
  72. ;;; Specify make-user
  73. (s/fdef make-user
  74. :args (s/cat :u form-spec)
  75. :ret #(s/valid? user-spec %)
  76. ;; useful to map inputs to outputs. kinda dependent typing.
  77. ;; here we're asserting that the input and output emails must match
  78. :fn #(= (-> % :args :u :email) (-> % :ret ::email)))
  79.  
  80.  
  81. ;;; more specs
  82. (s/def ::uuid #(instance? java.util.UUID %))
  83. (s/def ::username (s/and string? #(= % (str/lower-case %))))
  84.  
  85. ;;; gladly reusing previous specs
  86. ;;; is there a better way to compose specs?
  87. (def enriched-user-spec (s/keys :req [::first-name ::last-name ::email
  88. ::uuid ::username]
  89. :opt [::phone]))
  90.  
  91. ;;; Specify cleanup-user
  92. (s/fdef cleanup-user
  93. :args (s/cat :u user-spec)
  94. :ret #(s/valid? enriched-user-spec %))
  95.  
  96.  
  97.  
  98. ;;; try these inputs
  99. (def good-inputs [["ANthony Gonsalves" "anthony@gmail.com"]
  100. ["ANthony Gonsalves" "anthony@gmail.com" "1234567890"]])
  101.  
  102. (def bad-inputs [["ANthony Gonsalves" "anthony@gmail"]
  103. ["ANthony Gonsalves" "anthony@gmail.com" "12367890"]
  104. ["ANthony Gonsalves" "anthony@gmail.com" 1234567890]])
  105.  
  106. ;;; switch instrumentation on/off
  107.  
  108. ;; (do (s/instrument #'make-user)
  109. ;; (s/instrument #'cleanup-user))
  110.  
  111.  
  112. ;; (do (s/unstrument #'make-user)
  113. ;; (s/unstrument #'cleanup-user))
  114.  
  115. ;;; if you're working on the REPL, expect to reset instrumentation multiple
  116. ;;; times.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement