Skip to content

Commit c62e042

Browse files
committed
Lesson 12 has been added
1 parent 2b093b7 commit c62e042

File tree

7 files changed

+948
-0
lines changed

7 files changed

+948
-0
lines changed

otus-12/project.clj

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
(defproject otus-12 "0.1.0-SNAPSHOT"
2+
:description "FIXME: write description"
3+
:url "http://example.com/FIXME"
4+
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
5+
:url "https://www.eclipse.org/legal/epl-2.0/"}
6+
:dependencies [[camel-snake-kebab "0.4.3"]
7+
[org.clojure/clojure "1.11.1"]
8+
[org.clojure/data.json "2.4.0"]
9+
[org.clojure/test.check "1.1.1"]]
10+
:main ^:skip-aot otus-12.core
11+
:target-path "target/%s"
12+
:profiles {:dev
13+
{:jvm-opts ["-Ddev=true"
14+
"-Dclojure.spec.check-asserts=true"]}
15+
16+
:uberjar
17+
{:aot :all
18+
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

otus-12/src/otus_12/conforming.clj

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
(ns otus-12.conforming
2+
(:require [camel-snake-kebab.core :as csk]
3+
[clojure.data.json :as json]
4+
[clojure.instant :refer [read-instant-date]]
5+
[clojure.spec.alpha :as s]
6+
[clojure.string :as string]
7+
[otus-12.util :as u]))
8+
9+
;;
10+
;; # Приведение
11+
;;
12+
13+
(s/def ::->int
14+
(s/conformer (fn [value]
15+
(try (Long/valueOf value)
16+
(catch Exception _
17+
::s/invalid)))
18+
str))
19+
20+
(s/def ::int
21+
(s/or :int int?
22+
:string ::->int))
23+
24+
(s/conform ::int 2019)
25+
(s/conform ::int "2019")
26+
27+
(->> "2019"
28+
(s/conform ::int)
29+
(s/unform ::int))
30+
31+
(s/def ::->date
32+
(s/conformer (fn [value]
33+
(try (read-instant-date value)
34+
(catch Exception _
35+
::s/invalid)))
36+
(fn [^java.util.Date x]
37+
(str (.toInstant x)))))
38+
39+
(s/def ::date
40+
(s/or :date (partial instance? java.util.Date)
41+
:string ::->date))
42+
43+
(s/conform ::date #inst "2019")
44+
(s/conform ::date "2019")
45+
46+
(->> "2019"
47+
(s/conform ::date)
48+
(s/unform ::date))
49+
50+
;; Можно заметить повторяющиеся паттерны. Давайте введём ->conformer
51+
52+
(s/def ::->int
53+
(u/->conformer (fn [value]
54+
(Long/valueOf value))))
55+
56+
(s/def ::int
57+
(s/or :int int?
58+
:string ::->int))
59+
60+
(s/def ::->date
61+
(u/->conformer read-instant-date))
62+
63+
(s/def ::date
64+
(s/or :date (partial instance? java.util.Date)
65+
:string ::->date))
66+
67+
;; Конформеры можно объединять.
68+
69+
(s/def ::->boolean
70+
(s/and (u/->conformer string/lower-case)
71+
(u/->conformer (fn [value]
72+
(case value
73+
("true" "1" "on" "yes") true
74+
("false" "0" "off" "no") false
75+
;; Можно опустить, потмоу что case кидает исключение,
76+
;; когда ни одно значение не подошло.
77+
::s/invalid)))))
78+
79+
(s/def ::boolean
80+
(s/or :boolean boolean?
81+
:string ::->boolean))
82+
83+
(s/conform ::boolean false)
84+
(s/conform ::boolean "TRUE")
85+
(s/conform ::boolean "true2")
86+
87+
;; Парсинг с использованием conform и unform. См. parse-spec в util.
88+
89+
(u/parse-spec ::int "2019")
90+
(u/parse-spec ::date "2019")
91+
(u/parse-spec ::boolean "TRUE")
92+
93+
94+
(s/def ::exec-time ::date)
95+
(s/def ::port ::int)
96+
97+
(s/def ::config
98+
(s/keys :req-un [::exec-time
99+
::port]))
100+
101+
(let [json-config "{\"execTime\": \"2022-01-01\", \"port\": \"8080\"}"
102+
raw-config (json/read-str json-config :key-fn csk/->kebab-case-keyword)]
103+
(s/conform ::config raw-config)
104+
#_(u/parse-spec ::config raw-config))
105+
106+
;; Если обойтись без s/or, то parse-spec нам не понадобится.
107+
(s/def ::exec-time ::->date)
108+
(s/def ::port ::->int)
109+
110+
111+
(comment
112+
;; Случайно наткнулся на такую ошибку при компиляции.
113+
;; Связана она с тем, что на многие стандартные макросы в core библиотеке
114+
;; написано спеки, проверяющие структуру переданного значения.
115+
;; При проверке второй ветви в if-let в данном примере спека думает,
116+
;; что проверка завершилась ошибкой.
117+
(if-let [value true]
118+
value
119+
::s/invalid)
120+
121+
(if-let [value true]
122+
value
123+
'::s/invalid)
124+
125+
(s/valid? any? ::s/invalid)
126+
)

0 commit comments

Comments
 (0)