From 931023de0947b2212adc2a7a26908f0c9a12d549 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 8 Jul 2020 11:52:22 -0700 Subject: [PATCH] Address #121 by adding conditional builders/options for CSK --- deps.edn | 4 +++- src/next/jdbc.clj | 23 +++++++++++++++++++++++ src/next/jdbc/result_set.clj | 21 +++++++++++++++++++++ test/next/jdbc/result_set_test.clj | 19 +++++++++++++++++++ test/next/jdbc/sql_test.clj | 6 +++--- test/next/jdbc_test.clj | 12 +++++++++++- 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/deps.edn b/deps.edn index 6cc599e..4f81a80 100644 --- a/deps.edn +++ b/deps.edn @@ -32,7 +32,9 @@ org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.13.3"} org.apache.logging.log4j/log4j-jcl {:mvn/version "2.13.3"} org.apache.logging.log4j/log4j-jul {:mvn/version "2.13.3"} - org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.13.3"}} + org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.13.3"} + ;; test auto-added snake/kebab builders and options + camel-snake-kebab {:mvn/version "0.4.1"}} :jvm-opts ["-Dlog4j2.configurationFile=log4j2-info.properties"]} :runner {:extra-deps {com.cognitect/test-runner diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index 9b04e92..20bd3f6 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -279,3 +279,26 @@ wrapper object that can be used in its place." [connectable opts] (opts/->DefaultOptions connectable opts)) + +(defmacro ^:private def-snake-kebab [] + (try + (let [kebab-case (requiring-resolve 'camel-snake-kebab.core/->kebab-case) + snake-case (requiring-resolve 'camel-snake-kebab.core/->snake_case)] + `(do + (def snake-kebab-opts + "A hash map of options that will convert Clojure identifiers to + snake_case SQL entities (`:table-fn`, `:column-fn`), and will convert + SQL entities to qualified kebab-case Clojure identifiers (`:builder-fn`)." + {:column-fn ~snake-case :table-fn ~snake-case + :label-fn ~kebab-case :qualifier-fn ~kebab-case + :builder-fn (resolve 'next.jdbc.result-set/as-kebab-maps)}) + (def unqualified-snake-kebab-opts + "A hash map of options that will convert Clojure identifiers to + snake_case SQL entities (`:table-fn`, `:column-fn`), and will convert + SQL entities to unqualified kebab-case Clojure identifiers (`:builder-fn`)." + {:column-fn ~snake-case :table-fn ~snake-case + :label-fn ~kebab-case :qualifier-fn ~kebab-case + :builder-fn (resolve 'next.jdbc.result-set/as-unqualified-kebab-maps)}))) + (catch Throwable _))) + +(def-snake-kebab) diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj index c2a0c20..8c41ae2 100644 --- a/src/next/jdbc/result_set.clj +++ b/src/next/jdbc/result_set.clj @@ -256,6 +256,27 @@ [rs opts] (as-unqualified-modified-maps rs (assoc opts :label-fn lower-case))) +(defmacro ^:private def-snake-kebab [] + (try + (let [kebab-case (requiring-resolve 'camel-snake-kebab.core/->kebab-case)] + `(do + (defn ~'as-kebab-maps + {:doc "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` + that produces bare vectors of hash map rows, with kebab-case keys." + :arglists '([~'rs ~'opts])} + [rs# opts#] + (as-modified-maps rs# (assoc opts# + :qualifier-fn ~kebab-case + :label-fn ~kebab-case))) + (defn ~'as-unqualified-kebab-maps + {:doc "Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder` + that produces bare vectors of hash map rows, with simple, kebab-case keys." + :arglists '([~'rs ~'opts])} + [rs# opts#] + (as-unqualified-modified-maps rs# (assoc opts# :label-fn ~kebab-case))))) + (catch Throwable _))) +(def-snake-kebab) + (defn as-maps-adapter "Given a map builder function (e.g., `as-lower-maps`) and a column reading function, return a new builder function that uses that column reading diff --git a/test/next/jdbc/result_set_test.clj b/test/next/jdbc/result_set_test.clj index cb8ae5c..fbcb197 100644 --- a/test/next/jdbc/result_set_test.clj +++ b/test/next/jdbc/result_set_test.clj @@ -134,6 +134,25 @@ (is (map? row)) (is (= 4 (:id row))) (is (= "Orange" (:name row))))) + (testing "kebab-case row builder" + (let [row (p/-execute-one (ds) + ["select id,name,appearance as looks_like from fruit where id = ?" 3] + (assoc (default-options) + :builder-fn rs/as-kebab-maps))] + (is (map? row)) + (is (contains? row :fruit/looks-like)) + (is (nil? (:fruit/looks-like row))) + (is (= 3 (:fruit/id row))) + (is (= "Peach" (:fruit/name row))))) + (testing "unqualified kebab-case row builder" + (let [row (p/-execute-one (ds) + ["select id,name,appearance as looks_like from fruit where id = ?" 4] + {:builder-fn rs/as-unqualified-kebab-maps})] + (is (map? row)) + (is (contains? row :looks-like)) + (is (= "juicy" (:looks-like row))) + (is (= 4 (:id row))) + (is (= "Orange" (:name row))))) (testing "custom row builder 1" (let [row (p/-execute-one (ds) ["select fruit.*, id + 100 as newid from fruit where id = ?" 3] diff --git a/test/next/jdbc/sql_test.clj b/test/next/jdbc/sql_test.clj index d19a879..bb05885 100644 --- a/test/next/jdbc/sql_test.clj +++ b/test/next/jdbc/sql_test.clj @@ -8,7 +8,6 @@ [next.jdbc.sql :as sql] [next.jdbc.test-fixtures :refer [with-test-db ds column default-options - db derby? jtds? maria? mssql? mysql? postgres? sqlite?]] [next.jdbc.types :refer [as-other as-real as-varchar]])) @@ -183,5 +182,6 @@ (deftest enum-pg (when (postgres?) - (let [r (sql/insert! (ds) :lang_test {:lang (as-other "fr")})] - (is (= {:lang_test/lang "fr"} r))))) + (let [r (sql/insert! (ds) :lang-test {:lang (as-other "fr")} + jdbc/snake-kebab-opts)] + (is (= {:lang-test/lang "fr"} r))))) diff --git a/test/next/jdbc_test.clj b/test/next/jdbc_test.clj index 4870b27..cb5d505 100644 --- a/test/next/jdbc_test.clj +++ b/test/next/jdbc_test.clj @@ -46,7 +46,17 @@ (is (= "Apple" ((column :FRUIT/NAME) (jdbc/execute-one! ds-opts - ["select * from fruit where appearance = ?" "red"]))))) + ["select * from fruit where appearance = ?" "red"])))) + (is (= "red" (:fruit/looks-like + (jdbc/execute-one! + ds-opts + ["select appearance as looks_like from fruit where id = ?" 1] + jdbc/snake-kebab-opts)))) + (is (= "red" (:looks-like + (jdbc/execute-one! + ds-opts + ["select appearance as looks_like from fruit where id = ?" 1] + jdbc/unqualified-snake-kebab-opts))))) (testing "execute!" (let [rs (jdbc/execute! ds-opts