From 800baf30856038195df1c49e352feb3fcddf79ff Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 2 May 2021 19:46:56 -0700 Subject: [PATCH] Fixes #164 by making CSK dependency direct --- CHANGELOG.md | 1 + deps.edn | 8 ++++---- doc/friendly-sql-functions.md | 2 +- doc/getting-started.md | 4 +--- doc/result-set-builders.md | 2 +- src/next/jdbc.clj | 32 +++++++++++++------------------- src/next/jdbc/result_set.clj | 32 +++++++++++++------------------- 7 files changed, 34 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e4eb07..64d7fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Only accretive/fixative changes will be made from now on. * 1.1.next in progress + * Address #164 by making `clj-commons/camel-snake-kebab` an unconditional dependency. _[Being a conditional dependency that could be brought in at runtime caused problems with GraalVM-based native compilation as well as with multi-project monorepos]_ * Add **Tips & Tricks** section about working with PostgreSQL "interval" types (via PR #163 from @snorremd). * Address #162 by adding GraalVM to the test matrix (thank you @DeLaGuardo). * Update several dependency versions. diff --git a/deps.edn b/deps.edn index 5dc6bcd..14990a7 100644 --- a/deps.edn +++ b/deps.edn @@ -1,6 +1,8 @@ {:paths ["src"] :deps {org.clojure/clojure {:mvn/version "1.10.3"} - org.clojure/java.data {:mvn/version "1.0.86"}} + org.clojure/java.data {:mvn/version "1.0.86"} + + camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.2"}} :aliases {:test {:extra-paths ["test"] :extra-deps {org.clojure/test.check {:mvn/version "1.1.0"} @@ -32,9 +34,7 @@ org.apache.logging.log4j/log4j-1.2-api {:mvn/version "2.14.1"} org.apache.logging.log4j/log4j-jcl {:mvn/version "2.14.1"} org.apache.logging.log4j/log4j-jul {:mvn/version "2.14.1"} - org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.14.1"} - ;; test auto-added snake/kebab builders and options - camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.2"}} + org.apache.logging.log4j/log4j-slf4j-impl {:mvn/version "2.14.1"}} :jvm-opts ["-Dlog4j2.configurationFile=log4j2-info.properties"]} :runner {:extra-deps {com.cognitect/test-runner diff --git a/doc/friendly-sql-functions.md b/doc/friendly-sql-functions.md index 8c99e46..40352dd 100644 --- a/doc/friendly-sql-functions.md +++ b/doc/friendly-sql-functions.md @@ -196,7 +196,7 @@ These quoting functions can be provided to any of the friendly SQL functions abo (sql/insert! ds :my-table {:some "data"} {:table-fn snake-case}) ``` -If you have [camel-snake-kebab](https://clj-commons.org/camel-snake-kebab/) on your classpath, `next.jdbc` will contain `snake-kebab-opts` and `unqualified-snake-kebab-opts` which are hash maps containing `:column-fn` and `:table-fn` that use the `->snake_case` function from that library which performs a more sophisticated transformation. +`next.jdbc` provides `snake-kebab-opts` and `unqualified-snake-kebab-opts` which are hash maps containing `:column-fn` and `:table-fn` that use the `->snake_case` function from the [camel-snake-kebab library](https://github.com/clj-commons/camel-snake-kebab/) which performs a more sophisticated transformation. > Note: The entity naming function is passed a string, the result of calling `name` on the keyword passed in. Also note that the default quoting functions do not handle schema-qualified names, such as `dbo.table_name` -- `sql-server` would produce `[dbo.table_name]` from that. Use the `schema` function to wrap the quoting function if you need that behavior, e.g,. `{:table-fn (schema sql-server)}` which would produce `[dbo].[table_name]`. diff --git a/doc/getting-started.md b/doc/getting-started.md index 371cf68..7ba78dc 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -150,12 +150,10 @@ user=> > Note: See the `next.jdbc/with-option` examples in the [**Datasources, Connections & Transactions**](#datasources-connections--transactions) below for some caveats around using this function. -If you have [camel-snake-kebab](https://clj-commons.org/camel-snake-kebab/) on your classpath, two pre-built option hash maps are available in `next.jdbc`: +In addition, two pre-built option hash maps are available in `next.jdbc`, that leverage the [camel-snake-kebab library](https://github.com/clj-commons/camel-snake-kebab/): * `snake-kebab-opts` -- provides `:column-fn`, `:table-fn`, `:label-fn`, `:qualifier-fn`, and `:builder-fn` that will convert Clojure identifiers in `:kebab-case` to SQL entities in `snake_case` and will produce result sets with qualified `:kebab-case` names from SQL entities that use `snake_case`, * `unqualified-snake-kebab-opts` -- provides `:column-fn`, `:table-fn`, `:label-fn`, `:qualifier-fn`, and `:builder-fn` that will convert Clojure identifiers in `:kebab-case` to SQL entities in `snake_case` and will produce result sets with _unqualified_ `:kebab-case` names from SQL entities that use `snake_case`. -In addition, `next.jdbc.result-set` will have `as-kebab-maps` and `as-unqualified-kebab-maps` defined. - > Note: Using `camel-snake-kebab` might also be helpful if your database has `camelCase` table and column names, although you'll have to provide `:column-fn` and `:table-fn` yourself as `->camelCase` from that library. Either way, consider relying on the _default_ result set builder first and avoid converting column and table names (see [Advantages of 'snake case': portability and ubiquity](https://vvvvalvalval.github.io/posts/clojure-key-namespacing-convention-considered-harmful.html#advantages_of_'snake_case':_portability_and_ubiquity) for an interesting discussion on kebab-case vs snake_case -- I do not agree with all of the author's points in that article, particularly his position against qualified keywords, but his argument for retaining snake_case around system boundaries is compelling). diff --git a/doc/result-set-builders.md b/doc/result-set-builders.md index bf3ced7..bfc72ef 100644 --- a/doc/result-set-builders.md +++ b/doc/result-set-builders.md @@ -34,7 +34,7 @@ An example builder that converts `snake_case` database table/column names to `ke (result-set/as-modified-maps rs (assoc opts :qualifier-fn kebab :label-fn kebab)))) ``` -If you have [camel-snake-kebab](https://clj-commons.org/camel-snake-kebab/) on your classpath, two additional builders will be available: `as-kebab-maps` and `as-unqualified-kebab-maps` which use the `->kebab-case` function from that library with `as-modified-maps` and `as-unqualified-modified-maps` respectively. +However, a version of `as-kebab-maps` is built-in, as is `as-unqualified-kebab-maps`, which both use the `->kebab-case` function from the [camel-snake-kebab library](https://github.com/clj-commons/camel-snake-kebab/) with `as-modified-maps` and `as-unqualified-modified-maps` respectively. And finally there are two styles of adapters for the existing builders that let you override the default way that columns are read from result sets. The first style takes a `column-reader` function, which is called with the `ResultSet`, the `ResultSetMetaData`, and the column index, and is expected to read the raw column value from the result set and return it. The result is then passed through `read-column-by-index` (from `ReadableColumn`, which may be implemented directly via protocol extension or via metadata on the result of the `column-reader` function): diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index ac4c73d..6ba4102 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -61,7 +61,8 @@ In addition, wherever a `PreparedStatement` is created, you may specify: * `:return-keys` -- either `true` or a vector of key names to return." - (:require [next.jdbc.connection] + (:require [camel-snake-kebab.core :refer [->kebab-case ->snake_case]] + [next.jdbc.connection] [next.jdbc.default-options :as opts] [next.jdbc.prepare :as prepare] [next.jdbc.protocols :as p] @@ -367,25 +368,18 @@ [connectable opts] (opts/->DefaultOptions connectable opts)) -(defmacro ^:private def-snake-kebab [] - (try - (require 'camel-snake-kebab.core) - `(let [kebab-case# (requiring-resolve 'camel-snake-kebab.core/->kebab-case) - snake-case# (requiring-resolve 'camel-snake-kebab.core/->snake_case)] - (def snake-kebab-opts - "A hash map of options that will convert Clojure identifiers to +(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 + {:column-fn ->snake_case :table-fn ->snake_case + :label-fn ->kebab-case :qualifier-fn ->kebab-case + :builder-fn rs/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) + {:column-fn ->snake_case :table-fn ->snake_case + :label-fn ->kebab-case :qualifier-fn ->kebab-case + :builder-fn rs/as-unqualified-kebab-maps}) diff --git a/src/next/jdbc/result_set.clj b/src/next/jdbc/result_set.clj index ade7722..2bb42fe 100644 --- a/src/next/jdbc/result_set.clj +++ b/src/next/jdbc/result_set.clj @@ -17,7 +17,8 @@ See also https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/api/next.jdbc.date-time for implementations of `ReadableColumn` that provide automatic conversion of some SQL data types to Java Time objects." - (:require [clojure.core.protocols :as core-p] + (:require [camel-snake-kebab.core :refer [->kebab-case]] + [clojure.core.protocols :as core-p] [clojure.core.reducers :as r] [clojure.datafy :as d] [next.jdbc.prepare :as prepare] @@ -256,26 +257,19 @@ [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` +(defn as-kebab-maps + "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` + [rs opts] + (as-modified-maps rs (assoc opts + :qualifier-fn ->kebab-case + :label-fn ->kebab-case))) + +(defn as-unqualified-kebab-maps + "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) + [rs opts] + (as-unqualified-modified-maps rs (assoc opts :label-fn ->kebab-case))) (defn as-maps-adapter "Given a map builder function (e.g., `as-lower-maps`) and a column reading