Skip to content

Instantly share code, notes, and snippets.

@camsaul
Created September 1, 2023 01:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save camsaul/c80a72ac0bf81475f0e33599ae9e1e06 to your computer and use it in GitHub Desktop.
Save camsaul/c80a72ac0bf81475f0e33599ae9e1e06 to your computer and use it in GitHub Desktop.
Kebab Case Key Hating Map
(ns metabase.util.kebab-hating-map
"This is a map type that catches attempts to get `:kebab-case` values from it. In prod, it logs a warning and gets the
value for the equivalent `snake_case` key; in tests and dev it throws an Exception.
This is here so we can catch driver code is still supposed to be using Toucan instances rather than MLv2 Metadata.
This is intended to be only temporary; after everything in the QP is converted to MLv2 we can remove this."
(:require
[clojure.string :as str]
[metabase.config :as config]
[metabase.driver :as driver]
[metabase.util :as u]
[metabase.util.log :as log]
[potemkin :as p]
[pretty.core :as pretty]
[toucan2.core :as t2]
[toucan2.instance :as t2.instance]))
(defn- kebab-cased-key? [k]
(str/includes? k "-"))
(defn- warn-about-using-kebab-case [k]
(let [e (ex-info (format "Driver %s is accessing legacy metadata using :kebab-case key %s. This is not supported yet. Use %s instead."
(or driver/*driver* "")
(pr-str k)
(pr-str (u/->snake_case_en k)))
{:driver driver/*driver*, :k k})]
(if config/is-prod?
(log/warn e)
(throw e))))
(defn- normalize-key [k]
(if (kebab-cased-key? k)
(do
(warn-about-using-kebab-case k)
(u/->snake_case_en k))
k))
(declare ->KebabHatingMap)
(p/def-map-type KebabHatingMap [m]
(get [_this k default-value]
(get m (normalize-key k) default-value))
(assoc [this k v]
(let [m' (assoc m (normalize-key k) v)]
(if (identical? m m')
this
(->KebabHatingMap m'))))
(dissoc [this k]
(let [m' (dissoc m k)]
(if (identical? m m')
this
(->KebabHatingMap m'))))
(keys [_this]
(keys m))
(meta [_this]
(meta m))
(with-meta [this metta]
(let [m' (with-meta m metta)]
(if (identical? m m')
this
(->KebabHatingMap m'))))
pretty/PrettyPrintable
(pretty [_this]
(list `kebab-hating-map m)))
(defn kebab-hating-map
"Create a new map that handles either `snake_case` or `kebab-case` keys, but warns is you use `kebab-case` keys (in
prod) or throws an Exception (in dev and tests). This is here so we can catch code that needs to be updated to use
MLv2 metadata in 48+."
([]
(->KebabHatingMap {}))
([m]
(if (t2/instance? m)
(t2.instance/update-current m ->KebabHatingMap)
(->KebabHatingMap (or m {}))))
([k v & more]
(->KebabHatingMap (into {} (cons [k v] (partition-all 2 more))))))
(defn kebab-hating-map? [m]
(instance? KebabHatingMap m))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment