Clojure:从Map创建记录时确保数据完整性?

时间:2017-08-08 21:55:13

标签: clojure

我正在学习Clojure并享受它,但发现记录中的不一致让我感到困惑:为什么默认的地图构造函数(map-> Whatever)在创建新记录时检查数据完整性?例如:

user=> (defrecord Person [first-name last-name])
#<Class@46ffda99 user.Person>
user=> (map->Person {:first-name "Rich" :last-name "Hickey"})
#user.Person {:first-name "Rich" :last-name "Hickey"}
user=> (map->Person {:first-game "Rich" :last-name "Hickey"})
#user.Person {:first-game "Rich" :first-name nil :last-name "Hickey"}

我认为Map不需要定义Record定义中的所有字段,也允许包含不属于Record定义的额外字段。另外我理解我可以定义自己的构造函数来包装默认构造函数,然后我认为可以使用:post条件来检查正确(和全面)的记录创建(没有成功地使其工作)

我的问题是:是否有一种惯用的Clojure方法可以在Map构建记录期间验证数据?而且,这里有关于唱片的东西吗?

谢谢。

2 个答案:

答案 0 :(得分:5)

我认为你的综合性要求已经非常具体,所以我所知道的任何内置都无法涵盖这一点。

现在你可以做的一件事就是使用clojure.spec为你的构造函数提供一个s/fdef(然后对它进行检测)。

(require '[clojure.spec.alpha :as s]
         '[clojure.spec.test.alpha :as stest])

(defrecord Person [first-name last-name])

(s/fdef map->Person
  :args (s/cat :map (s/keys :req-un [::first-name ::last-name])))

(stest/instrument `map->Person)

(map->Person {:first-name "Rich", :last-name "Hickey"})
(map->Person {:first-game "Rich", :last-name "Hickey"})  ; now fails

(如果为::first-name::last-name定义了规范,那么也会对其进行检查。)

答案 1 :(得分:1)

另一个选项是to use Plumatic Schema来创建一个包装器&#34;构造函数&#34;指定允许键的函数。例如:

{ :foo "hello"  :bar :some-kw }

第一行定义了一个只接受以下地图的模式:

(def NameMap {(s/required-key :first-name) s/Str (s/required-key :last-name) s/Str})

(s/defn safe->person 
  [name-map :- NameMap]
  (map->Person name-map))

你的包装器构造函数看起来像:

(s/defn safe->person-2
  [name-map]
  (assert (= #{:first-name :last-name} (set (keys name-map))))
  (map->Person name-map))

  plugins: [
    new CopyWebpackPlugin([
      { from: 'app/assets', to: 'assets' }
    ])
  ]