From 2a111024c640f5c2e1d11ed7a6a36cedf9b99730 Mon Sep 17 00:00:00 2001 From: Sergey Alekhnovich Date: Wed, 24 Feb 2021 12:01:24 -0800 Subject: [PATCH 1/3] docstringify the specter koans to match patterns --- src/koans/28_specter.clj | 73 +++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/src/koans/28_specter.clj b/src/koans/28_specter.clj index 0322b4f..241c5b5 100644 --- a/src/koans/28_specter.clj +++ b/src/koans/28_specter.clj @@ -40,87 +40,83 @@ (meditations - ;; Imagine you wanted to select the keys for all of the brands of cars you own - ;; Try doing this without specter + "Imagine you wanted to select the keys for all of the brands of cars you own. + Try doing this without specter" (= [:ford :toyota :latacara] (__ (get car-inventory __))) - ;; Now try with specter! + "Now try with specter!" (= [:ford :toyota :latacara] (sr/select [:cars sr/MAP-KEYS] __)) - ;; Try to just get the types of cars from latacara + "Try to just get the types of cars from latacara" (= [:prime :prime-zero] (sr/select [:cars __ __] car-inventory)) - ;; Let's try to get some Values instead of keys - ;; Let's start with selecting the vector of the two ford Focus cars we have at the dealership + "Let's try to get some Values instead of keys + Let's start with selecting the vector of the two ford Focus cars we have at the dealership" (= [[{:doors 2 :color "red"} {:doors 2 :color "blue"}]] (sr/select [:cars __ __] car-inventory)) - ;; That was great, but what if we actually just want the maps instead of the vector - ;; (notice that the previous test is a vector inside of a vector) + "That was great, but what if we actually just want the maps instead of the vector + (notice that the previous test is a vector inside of a vector)" (= [{:doors 2 :color "red"} {:doors 2 :color "blue"}] (___ [:cars :ford __ sr/ALL] __)) - ;; Let's get all of the cars from both ford types, the focus and the expedition + "Let's get all of the cars from both ford types, the focus and the expedition" (= [{:doors 2 :color "red"} {:doors 2 :color "blue"} {:doors 4 :color "red"} {:doors 4 :color "black"}] (sr/select [:cars :ford (sr/multi-path :focus :expedition) __] car-inventory)) - ;; multi-path was cool, but imagine if you had 100 keys in the multi-path, that would be terrible - ;; let's see if we can get there with MAP-VALS + "multi-path was cool, but imagine if you had 100 keys in the multi-path, that would be terrible + let's see if we can get there with MAP-VALS" (= [{:doors 2 :color "red"} {:doors 2 :color "blue"} {:doors 4 :color "red"} {:doors 4 :color "black"}] (sr/select [__ __ sr/MAP-VALS __] car-inventory)) - ;; You can also pass a function to the end of a select to filter results - ;; Let's dig down into the maps values for all of the cars and then just get the results of the cars with 4 doors + "You can also pass a function to the end of a select to filter results + Let's dig down into the maps values for all of the cars and then just get the results of the cars with 4 doors" (= [{:doors 4, :color "red"} {:doors 4, :color "black"} {:doors 4, :color "tan"} {:doors 4, :color "blue"} {:doors 4, :color "blue"}] (sr/select [:cars ___ ___ sr/ALL #(= (:doors %) 4)] car-inventory)) - ;; Great, so we can now traverse a map, going either by the absolute path or using some generalized special functions to get where we need to go - ;; So what is Eidolon? - ;; Eidolon is a latacora built specter library - Let's check it out - - ;; eidolon TREE-KEYS will provide all of the keys below whatever point in the map it is called + "Great, so we can now traverse a map, going either by the absolute path or using some generalized special functions to get where we need to go + So what is Eidolon? + Eidolon is a latacora built specter library - Let's check it out + eidolon TREE-KEYS will provide all of the keys below whatever point in the map it is called" (= [:doors :color :doors :color :doors :color :doors :color :focus :expedition] (sr/select [:cars :ford e/TREE-KEYS] __)) - ;; What if instead of TREE-KEYS we wanted the TREE-LEAVES? + "What if instead of TREE-KEYS we wanted the TREE-LEAVES?" (= [2 "red" 2 "blue" 4 "red" 4 "black"] (sr/select [:cars :ford ___] car-inventory)) - ;; Another eidolon feature is collecting a value and continuing down to the items - ;; this will return both the collected value and the items in their own vector + "Another eidolon feature is collecting a value and continuing down to the items + this will return both the collected value and the items in their own vector" (= [[:focus {:doors 2, :color "red"}] [:focus {:doors 2, :color "blue"}] [:expedition {:doors 4, :color "red"}] [:expedition {:doors 4, :color "black"}]] (sr/select [:cars __ e/INDEXED __] car-inventory)) ;; TODO figure out a good use for INDEXED-SEQ :) - ;; Setval and transform - ;; So part of specter is collecting data, but another aspect is changing that data - ;; Let's scope these examples down a tad with car-inventory-ford-focus - - ;; imagine that someone came and painted all of your ford focuses orange. + "Setval and transform + So part of specter is collecting data, but another aspect is changing that data + imagine that someone came and painted all of your ford focuses orange." (= {:cars {:ford {:focus [{:doors 2, :color "orange"} {:doors 2, :color "orange"}], :expedition [{:doors 4, :color "red"} {:doors 4, :color "black"}]}}} (sr/setval [:cars :ford :focus sr/ALL __] __ car-inventory-ford)) - ;; The new orange ford focuses sell like hot cakes! now that we have sold 2 lets remove them from our inventory + "The new orange ford focuses sell like hot cakes! now that we have sold 2 lets remove them from our inventory" (= {:cars {:ford {:expedition [{:doors 4, :color "red"} {:doors 4, :color "black"}]}}} (sr/setval [:cars __ __] sr/NONE car-inventory-ford)) - ;; setval takes a map and returns a map. - ;; Let's use a thread to remove both of our sold ford focuses and change the color of the expedition to orange to try to sell that faster as well + "setval takes a map and returns a map. + Let's use a thread to remove both of our sold ford focuses and change the color of the expedition to orange to try to sell that faster as well" (= {:cars {:ford {:expedition [{:doors 4, :color "orange"}]}}} (___ car-inventory-ford (sr/setval [:cars :ford :focus] ___) (sr/setval [:cars :ford :expedition sr/ALL :color] __))) - ;; Transform also takes a map and returns a map - ;; The format of transform is (sr/transform path function map) - ;; Generally I use transform when I need to do more complex functions to decide what to change things to - - ;; Let's change any blue ford focus from out car-inventory-ford map to orange, our new favorite color + "Transform also takes a map and returns a map + The format of transform is (sr/transform path function map) + Generally I use transform when I need to do more complex functions to decide what to change things to + Let's change any blue ford focus from out car-inventory-ford map to orange, our new favorite color" (= {:cars {:ford {:focus [{:doors 2, :color "red"} {:doors 2, :color "orange"}], :expedition [{:doors 4, :color "red"}]}}} (___ [:cars :ford :focus sr/ALL :color] #(if (= % "blue") @@ -136,11 +132,10 @@ ;; color)) ;; car-inventory-ford) - ;; For this final example let's scope back up to car-inventory, just for fun :) - - ;; Let's say that the government has outlawed all cars with an even amount of doors. To be good to the people, let's just give them an extra one - ;; latacara is ahead of the curve on this one, so we will specifically exclude them from this transform - ;; We are going to use eidolon's INDEXED function to collect the name of the brand, and then continue to dig down into the door count. From there, we can check if its even using an even? function and if its true increase the count by one +" For this final example let's scope back up to car-inventory, just for fun :) + Let's say that the government has outlawed all cars with an even amount of doors. To be good to the people, let's just give them an extra one + latacara is ahead of the curve on this one, so we will specifically exclude them from this transform + We are going to use eidolon's INDEXED function to collect the name of the brand, and then continue to dig down into the door count. From there, we can check if its even using an even? function and if its true increase the count by one" (= {:cars {:ford {:focus [{:doors 3, :color "red"} {:doors 3, :color "blue"}], :expedition [{:doors 5, :color "red"} {:doors 5, :color "black"}]}, :toyota {:camry [{:doors 3, :color "red"} {:doors 5, :color "tan"}], :rav4 [{:doors 5, :color "blue"} {:doors 5, :color "blue"}]}, :latacara {:prime [{:doors 11, :color "clear"}], :prime-three [{:doors 3, :color "red"} {:doors 3, :color "green"}]}}} (sr/transform [:cars ___ ___ sr/ALL :doors] (fn [brand doors] From 9b4ec774cfa4d02ffe09835bd790d02f5dc083b2 Mon Sep 17 00:00:00 2001 From: Sergey Alekhnovich Date: Wed, 24 Feb 2021 13:01:51 -0800 Subject: [PATCH 2/3] add docstrings to make the koans runnable and fix up some of the assertions --- src/koans/28_specter.clj | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/koans/28_specter.clj b/src/koans/28_specter.clj index 241c5b5..3d29dd3 100644 --- a/src/koans/28_specter.clj +++ b/src/koans/28_specter.clj @@ -34,7 +34,9 @@ {:doors 2 :color "blue"}] :expedition [{:doors 4 - :color "red"}]}}}) + :color "red"} + {:doors 4 + :color "black"}]}}}) ;; The hope of this exercise is to get you more familiar with specter and eidolon @@ -60,6 +62,7 @@ "That was great, but what if we actually just want the maps instead of the vector (notice that the previous test is a vector inside of a vector)" + ;; kinda confusing that you have to fill in sr/select (maybe would add a hint for sr/ALL?) (= [{:doors 2 :color "red"} {:doors 2 :color "blue"}] (___ [:cars :ford __ sr/ALL] __)) @@ -69,6 +72,7 @@ "multi-path was cool, but imagine if you had 100 keys in the multi-path, that would be terrible let's see if we can get there with MAP-VALS" + let's see if we can get all the :ford cars with MAP-VALS" (= [{:doors 2 :color "red"} {:doors 2 :color "blue"} {:doors 4 :color "red"} {:doors 4 :color "black"}] (sr/select [__ __ sr/MAP-VALS __] car-inventory)) @@ -80,7 +84,7 @@ "Great, so we can now traverse a map, going either by the absolute path or using some generalized special functions to get where we need to go So what is Eidolon? - Eidolon is a latacora built specter library - Let's check it out + Eidolon is a latacora-built specter library - Let's check it out eidolon TREE-KEYS will provide all of the keys below whatever point in the map it is called" (= [:doors :color :doors :color :doors :color :doors :color :focus :expedition] (sr/select [:cars :ford e/TREE-KEYS] __)) @@ -90,7 +94,8 @@ (sr/select [:cars :ford ___] car-inventory)) "Another eidolon feature is collecting a value and continuing down to the items - this will return both the collected value and the items in their own vector" + this will return both the collected value and the items in their own vector. + Complete the below query to get a list of model descriptions for ford " (= [[:focus {:doors 2, :color "red"}] [:focus {:doors 2, :color "blue"}] [:expedition {:doors 4, :color "red"}] [:expedition {:doors 4, :color "black"}]] (sr/select [:cars __ e/INDEXED __] car-inventory)) @@ -102,16 +107,19 @@ (= {:cars {:ford {:focus [{:doors 2, :color "orange"} {:doors 2, :color "orange"}], :expedition [{:doors 4, :color "red"} {:doors 4, :color "black"}]}}} (sr/setval [:cars :ford :focus sr/ALL __] __ car-inventory-ford)) - "The new orange ford focuses sell like hot cakes! now that we have sold 2 lets remove them from our inventory" + "The new orange ford focuses sell like hot cakes! now that we have sold 2 lets remove them from our inventory + with sr/NONE" (= {:cars {:ford {:expedition [{:doors 4, :color "red"} {:doors 4, :color "black"}]}}} (sr/setval [:cars __ __] sr/NONE car-inventory-ford)) "setval takes a map and returns a map. - Let's use a thread to remove both of our sold ford focuses and change the color of the expedition to orange to try to sell that faster as well" - (= {:cars {:ford {:expedition [{:doors 4, :color "orange"}]}}} - (___ car-inventory-ford - (sr/setval [:cars :ford :focus] ___) - (sr/setval [:cars :ford :expedition sr/ALL :color] __))) + Let's use a thread to do the following: + 1. remove both of our sold ford focuses + 2. change the color of the expedition to orange to try to sell that faster as well" + ;; TODO using placeholder for the thread macro doesn't play nice w/ the meditate macro (can this be fixed?) + ;; TODO: add two entries to the expected vec + (= {:cars {:ford {:expedition [{:doors 4, :color "orange"} {:doors 4, :color "orange"}]}}} + (->> car-inventory-ford "Transform also takes a map and returns a map The format of transform is (sr/transform path function map) @@ -133,9 +141,13 @@ ;; car-inventory-ford) " For this final example let's scope back up to car-inventory, just for fun :) - Let's say that the government has outlawed all cars with an even amount of doors. To be good to the people, let's just give them an extra one - latacara is ahead of the curve on this one, so we will specifically exclude them from this transform - We are going to use eidolon's INDEXED function to collect the name of the brand, and then continue to dig down into the door count. From there, we can check if its even using an even? function and if its true increase the count by one" + Let's say that the government has outlawed all cars with an even amount of doors. + To be good to the people, let's just give them an extra one. latacara is ahead of + the curve on this one, so we will specifically exclude them from this transform + We are going to use eidolon's INDEXED function to collect the name of the brand, + and then continue to dig down into the door count. From there, we can check if + its even using an even? function and if its true increase the count by one +" (= {:cars {:ford {:focus [{:doors 3, :color "red"} {:doors 3, :color "blue"}], :expedition [{:doors 5, :color "red"} {:doors 5, :color "black"}]}, :toyota {:camry [{:doors 3, :color "red"} {:doors 5, :color "tan"}], :rav4 [{:doors 5, :color "blue"} {:doors 5, :color "blue"}]}, :latacara {:prime [{:doors 11, :color "clear"}], :prime-three [{:doors 3, :color "red"} {:doors 3, :color "green"}]}}} (sr/transform [:cars ___ ___ sr/ALL :doors] (fn [brand doors] @@ -154,6 +166,7 @@ ;; (inc %) ;; %) car-inventory) + ;; TODO exercise to remove maps that contain keys w/ specific strings ) From 57f4af6c1872053e60f683c8b209eab549f2a618 Mon Sep 17 00:00:00 2001 From: Sergey Alekhnovich Date: Wed, 24 Feb 2021 13:02:40 -0800 Subject: [PATCH 3/3] DEV-ONLY add specter_koans to run 1st in config list this can probably be removed/configured as needed in the future --- resources/koans.clj | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/koans.clj b/resources/koans.clj index 8f9bb10..bc5857b 100644 --- a/resources/koans.clj +++ b/resources/koans.clj @@ -1,4 +1,16 @@ -[["01_equalities" {"__" [true +[["28_specter" {"__" [true + 2 + 5 + true + false + true + true + false + "hello" + "hello" + nil + 3]}] + ["01_equalities" {"__" [true 2 5 true