使用Taoensso Truss验证地图中可选键下的内容

时间:2017-05-10 15:33:29

标签: clojure

(require '[taoensso.truss :as truss])

假设我们有一张描述我圈子的地图。圆总是有一个中心,但可以通过直径或半径来描述:

{:center [1, 2] :diameter 10}
{:center [1, 2] :radius   5}

上述两个圈子都描述了相同的圈子。

所以说我们有一个期望圆形图作为输入的函数,我们怎样才能最好地用truss来断言呢?代码的开头可能如下所示:

(defn circle-tosser
  [circle-map]
  (truss/have map? circle-map)
  (truss/have number? :in (:center circle-map))
  (str "Tossing " circle-map))

(circle-tosser {:center [1, 2] :radius 5})
;; => "Tossing {:center [1 2], :radius 5}"

问题当然是我们不能直接断言这些密钥的值具有certine属性,因为它们不存在neccicerily。例如,以下声明要求两个键同时出现:

(defn circle-tosser
  [circle-map]
  (truss/have map? circle-map)
  (truss/have number? :in (:center circle-map))
  (truss/have number? (:diameter circle-map))
  (truss/have number? (:radius   circle-map))
  (str "Tossing " circle-map))

(circle-tosser {:center [1, 2] :diameter 10}) ; Unhandled Exception
(circle-tosser {:center [1, 2] :diameter 10 :radius 5})
;; => "Tossing {:center [1 2], :diameter 10, :radius 5}"

然后你可以开始编写像

这样的东西
(defn circle-tosser
  [circle-map]
  (truss/have map? circle-map)
  (truss/have number? :in (:center circle-map))
  (when (contains? circle-map :diameter)
    (truss/have number? (:diameter circle-map)))
  (when (contains? circle-map :radius)
    (truss/have number? (:radius   circle-map)))
  (str "Tossing " circle-map))

(circle-tosser {:center [1, 2] :diameter 10})
;; => "Tossing {:center [1 2], :diameter 10}"

但是这开始变得过于冗长且无法阅读,这是不幸的,因为提供可访问的文档(以代码的形式),是桁架目的的一部分。

也许你可以想出一个改进的方法来解决这个问题?

2 个答案:

答案 0 :(得分:1)

有两种方法可以解决您的问题。

(1)始终生成radius而不是diameter

的圈子

(2)如果你不能做(1),那么每个对圆的引用都需要包含在如下的转换函数中:

(defn normalize [c]
  (if (contains? c :diameter)
    (-> c
      (assoc :radius (/ (:diameter c) 2))
      (dissoc :diameter))
    c))

,您的代码看起来像

(defn circle-tosser
  [circle-map]
  (let [c (normalize circle-map) ]
    (truss/have map? c)
    (truss/have number? :in (:center c))
    (str "Tossing " c)))

我总是喜欢解决方案(1),但有时候(2)无法避免。

答案 1 :(得分:0)

你可以做的一件事是定义一个隐藏重复代码的函数,比如

<Grid DataContext="{Binding ElementName=lb_Configuration, Path=SelectedItem}">
                <StackPanel HorizontalAlignment="Left">
                    <TextBlock Text="Basic_Param" Style="{StaticResource Heading2}" Margin="0,0,0,5" />
                    <StackPanel HorizontalAlignment="Left">
                        <DataGrid Name="dgBasicInfo" DataContext="{Binding ElementName=lb_Configuration, Path=SelectedItem}" AutoGenerateColumns="True" Margin="0,0,0,15" BorderThickness="2">
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="ModelName"  Binding="{Binding Path=ModelName}"/>
                                <DataGridTextColumn Header="CategoryName" Binding="{Binding Path=CategoryName}" />
                                <DataGridTextColumn Header="InputAddress" Binding="{Binding Path=IntputAddress}" />
                                <DataGridTextColumn Header="OutputAddress" Binding="{Binding Path=OutputAddress}" />
                                <DataGridTextColumn Header="DiagAddress" Binding="{Binding Path=DiagAddress}" />
                                <DataGridTextColumn Header="Description" Binding="{Binding Path=Description}" />
                            </DataGrid.Columns>
                        </DataGrid>
                    </StackPanel>
<TextBox Margin="5" Grid.Column="1" Text="{Binding Path=ModelName}" /></Grid>

当然,对于这个例子,我们要引入一个互斥的概念。但是,正如Alan Thompson建议的那样,将数据标准化可能会更好。