Повторное использование кода определения структуры из спецификации/ключей clojure

У меня есть определение спецификации, которое проверяет содержимое входящих данных. Поскольку данные представляют собой карту полей, я использую spec/keys для ее проверки. Например:

(def person-data {:name "Jon Doe", :age 30})
(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person-info (s/keys :req-un [::name ::age])
...
;validate data via spec and make sure no additional keys are included
(s/valid? ::person-spec some-input)

Но у меня есть дополнительная потребность убедиться, что входящие данные содержат только те ключи, которые мне нужны. (в данном случае только клавиши :name и :age. Для этого я делаю что-то вроде:

(def permitted-keys [:age :name])
(select-keys some-input permitted-keys)

, гарантируя, что будут отфильтрованы только эти ключи.

Есть ли способ повторно использовать некоторый код между моим определением спецификации для структуры карты (s/keys) и этим дополнительным шагом, который я предпринимаю для фильтрации разрешенных ключей (permitted-keys)?

Возможно, либо извлекая список ключей из определения s/keys, либо передавая существующий вектор ключей в s/keys?


person Yonathan W'Gebriel    schedule 12.12.2017    source источник
comment
Ознакомьтесь с этим макросом. .   -  person Taylor Wood    schedule 12.12.2017


Ответы (1)


Я рекомендую посмотреть этот макрос, так как он охватывает все основы и предоставляет генератор, но вот еще один подход, который предполагает, что ваши карты только используют ключи без имен:

(defmacro keys-strict
  [& args]
  (let [{:keys [req opt req-un opt-un]} args
        ks (into #{} (->> (concat req opt req-un opt-un)
                          (map #(keyword (name %)))))] ;; strip namespaces from keywords
    `(s/and (s/keys ~@args) (s/map-of ~ks any?))))

Единственная уловка здесь для повторного использования одного и того же источника правды для ключей заключается в том, что ваши ключевые спецификации будут иметь пространство имен, а ваши ключи карты — нет. Вы можете сделать то же самое без макроса, вы просто s/and свою спецификацию s/keys со спецификацией s/map-of или какой-либо другой спецификацией, которая ограничивает разрешенные ключи.

Есть ли способ повторно использовать некоторый код между моим определением спецификации для структуры карты (s/keys) и этим дополнительным шагом, который я предпринимаю для фильтрации разрешенных ключей (разрешенные ключи)?

Да, в приведенном выше примере это обрабатывается путем сращивания args с вызовом s/keys и делается аналогичным образом в этом более полном макросе gfredericks/schpec.clj#L13" rel="nofollow noreferrer">здесь.

Примечание. Могут быть ситуации, когда вам действительно нужно ограничить, какие ключи вы будете принимать, но я думаю, что обычно рекомендуется определять спецификации карты, которые открыты для последующего расширения.

person Taylor Wood    schedule 12.12.2017
comment
Потрясающий. Это определенно помогает специфицировать строгие карты. Кроме того, хотя вы не указали это явно, вы ответили на часть моего вопроса, как я могу передать существующий вектор ключей в определение s/keys в вашем ~@args использовании сплайсинга в определении макроса. Это стоит указать как часть вашего ответа. - person Yonathan W'Gebriel; 15.12.2017