From 3ae62314cef48cbf649274bac555f00a05b4a1f1 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Thu, 23 Feb 2023 03:40:19 +0000 Subject: [PATCH 01/11] Add color unit namespace --- src/brewtility/calculations.cljc | 4 +- src/brewtility/color.cljc | 143 --------- src/brewtility/precision.cljc | 15 +- src/brewtility/units.cljc | 295 +++++++++++------- src/brewtility/units/color.cljc | 395 +++++++++++++++++++++++++ src/brewtility/units/options.cljc | 389 ++++++++++++++++++++++++ test/brewtility/data/equipment.cljc | 4 +- test/brewtility/data/fermentables.cljc | 8 +- test/brewtility/data/hops.cljc | 8 +- test/brewtility/data/mash.cljc | 10 +- test/brewtility/data/miscs.cljc | 8 +- test/brewtility/data/recipes.cljc | 8 +- test/brewtility/data/styles.cljc | 8 +- test/brewtility/data/waters.cljc | 8 +- test/brewtility/data/yeasts.cljc | 8 +- test/brewtility/units/color_test.cljc | 94 ++++++ 16 files changed, 1116 insertions(+), 289 deletions(-) delete mode 100644 src/brewtility/color.cljc create mode 100644 src/brewtility/units/color.cljc create mode 100644 src/brewtility/units/options.cljc create mode 100644 test/brewtility/units/color_test.cljc diff --git a/src/brewtility/calculations.cljc b/src/brewtility/calculations.cljc index 894841f..ebb0db2 100644 --- a/src/brewtility/calculations.cljc +++ b/src/brewtility/calculations.cljc @@ -113,14 +113,14 @@ (gravity-points->potential-gravity total-gravity-points batch-size))) -(def ^:const gravity->abv-multiplier +(def gravity->abv-multiplier "The multiplier used to convert gravity to ABV. This is a constant, and is not configurable." 0.00135) -(def ^:const default-attenuation +(def default-attenuation "A default attenuation for yeast if none is provided. This represents a common level home brewers should expect from their yeast." 0.75) diff --git a/src/brewtility/color.cljc b/src/brewtility/color.cljc deleted file mode 100644 index 5fbfe95..0000000 --- a/src/brewtility/color.cljc +++ /dev/null @@ -1,143 +0,0 @@ -(ns brewtility.color - "Namespace for calculating beer colors") - - -;; -;; SRM Beer Colors (https://en.wikipedia.org/wiki/Standard_Reference_Method) -;; -(def ^:const srm-1 "rgba(255,230,153,1)") -(def ^:const srm-2 "rgba(255,216,120,1)") -(def ^:const srm-3 "rgba(255,202,90,1)") -(def ^:const srm-4 "rgba(255,191,66,1)") -(def ^:const srm-5 "rgba(251,177,35,1)") -(def ^:const srm-6 "rgba(248,166,0,1)") -(def ^:const srm-7 "rgba(243,156,0,1)") -(def ^:const srm-8 "rgba(234,143,0,1)") -(def ^:const srm-9 "rgba(229,133,0,1)") -(def ^:const srm-10 "rgba(222,124,0,1)") -(def ^:const srm-11 "rgba(215,114,0,1)") -(def ^:const srm-12 "rgba(207,105,0,1)") -(def ^:const srm-13 "rgba(203,98,0,1)") -(def ^:const srm-14 "rgba(195,89,0,1)") -(def ^:const srm-15 "rgba(187,81,0,1)") -(def ^:const srm-16 "rgba(181,76,0,1)") -(def ^:const srm-17 "rgba(176,69,0,1)") -(def ^:const srm-18 "rgba(166,62,0,1)") -(def ^:const srm-19 "rgba(161,55,0,1)") -(def ^:const srm-20 "rgba(155,50,0,1)") -(def ^:const srm-21 "rgba(149,45,0,1)") -(def ^:const srm-22 "rgba(142,41,0,1)") -(def ^:const srm-23 "rgba(136,35,0,1)") -(def ^:const srm-24 "rgba(130,30,0,1)") -(def ^:const srm-25 "rgba(123,26,0,1)") -(def ^:const srm-26 "rgba(119,25,0,1)") -(def ^:const srm-27 "rgba(112,20,0,1)") -(def ^:const srm-28 "rgba(106,14,0,1)") -(def ^:const srm-29 "rgba(102,13,0,1)") -(def ^:const srm-30 "rgba(94,11,0,1)") -(def ^:const srm-31 "rgba(90,10,2,1)") -(def ^:const srm-32 "rgba(96,9,3,1)") -(def ^:const srm-33 "rgba(82,9,7,1)") -(def ^:const srm-34 "rgba(76,5,5,1)") -(def ^:const srm-35 "rgba(71,6,6,1)") -(def ^:const srm-36 "rgba(68,6,7,1)") -(def ^:const srm-37 "rgba(63,7,8,1)") -(def ^:const srm-38 "rgba(59,6,7,1)") -(def ^:const srm-39 "rgba(58,7,11,1)") -(def ^:const srm-40 "rgba(3,4,3,1)") - - -(def srm-color-map - {1 srm-1 - 2 srm-2 - 3 srm-3 - 4 srm-4 - 5 srm-5 - 6 srm-6 - 7 srm-7 - 8 srm-8 - 9 srm-9 - 10 srm-10 - 11 srm-11 - 12 srm-12 - 13 srm-13 - 14 srm-14 - 15 srm-15 - 16 srm-16 - 17 srm-17 - 18 srm-18 - 19 srm-19 - 20 srm-20 - 21 srm-21 - 22 srm-22 - 23 srm-23 - 24 srm-24 - 25 srm-25 - 26 srm-26 - 27 srm-27 - 28 srm-28 - 29 srm-29 - 30 srm-30 - 31 srm-31 - 32 srm-32 - 33 srm-33 - 34 srm-34 - 35 srm-35 - 36 srm-36 - 37 srm-37 - 38 srm-38 - 39 srm-39 - 40 srm-40}) - - -(defn lovibond->srm - "Convert the color described in degrees `lovibond` to the equivalent SRM color" - [lovibond] - (- (* lovibond 1.3546) 0.76)) - - -(defn srm->ebc - "Convert the color described by the `srm` to the equivalent EBC color" - [srm] - (* srm 1.97)) - - -(defn ebc->srm - "Convert the color described by the `ebc` to the equivalent SRM color" - [ebc] - (* ebc 0.508)) - - -(defn srm->lovibond - "Convert the color described in 'srm` to the equivalent degrees Lovibond" - [srm] - (/ (+ srm 0.76) 1.3546)) - - -(def lovibond->ebc - "Convert the color described in degrees 'lovibond` to the equivalent EBC color" - (comp srm->ebc lovibond->srm)) - - -(def ebc->lovibond - "Convert the color described in 'ebc` to the equivalent degrees Lovibond" - (comp srm->lovibond ebc->srm)) - - -(defn srm->rgba - "Given `srm-number`, return the closest, bounded applicable RGBA color string. - OPINIONATED: The provided `srm-number` will be bound to the common range from 1 to 40 - Decimal-like values are trimmed, not rounded." - [srm-number] - (let [srm-color (min (max (int srm-number) 1) 40)] - (get srm-color-map srm-color))) - - -(def lovibond->rgba - "Given `lovibond-number`, return the closest, bounded applicable RGBA color string." - (comp srm->rgba lovibond->srm)) - - -(def ebc->rgba - "Given `ebc-number`, return the closest, bounded applicable RGBA color string." - (comp srm->rgba ebc->srm)) diff --git a/src/brewtility/precision.cljc b/src/brewtility/precision.cljc index cc4a1af..703a48f 100644 --- a/src/brewtility/precision.cljc +++ b/src/brewtility/precision.cljc @@ -1,10 +1,12 @@ (ns brewtility.precision - "Namespace for handling numeric precision" + "Namespace for handling numeric precision, rounding, and approximation." + {:added "1.0"} #?(:clj (:import [java.math RoundingMode]))) (defn approximates? - "Determine if `n2` approximates `n1` within `variance` percent" + "Determine if `n2` approximates `n1` within `variance` percent." + {:added "1.0"} [n1 n2 variance] (let [upper-bound (* n1 (+ 1.0 variance)) lower-bound (* n1 (- 1.0 variance))] @@ -12,7 +14,8 @@ (defn ->precision - "Given a decimal `x` and the number of decimal places, returns that number rounded to `num-decimals` precision" + "Given a decimal `x` and the number of decimal places, returns that number rounded to `num-decimals` precision." + {:added "1.0"} [^double x ^long num-decimals] (double #?(:clj (.setScale (bigdec x) num-decimals RoundingMode/HALF_UP) @@ -23,17 +26,23 @@ (defn ->1dp "Given a decimal `x`, returns that number rounded to one decimal place." + {:added "1.0" + :see-also ["->precision" "->2dp" "->3dp"]} [^double x] (->precision x 1)) (defn ->2dp "Given a decimal `x`, returns that number rounded to two decimal places." + {:added "1.0" + :see-also ["->precision" "->1dp" "->3dp"]} [^double x] (->precision x 2)) (defn ->3dp "Given a decimal `x`, returns that number rounded to three decimal places." + {:added "1.0" + :see-also ["->precision" "->1dp" "->2dp"]} [^double x] (->precision x 3)) diff --git a/src/brewtility/units.cljc b/src/brewtility/units.cljc index 832e790..1fa1ccf 100644 --- a/src/brewtility/units.cljc +++ b/src/brewtility/units.cljc @@ -1,107 +1,190 @@ (ns brewtility.units - "Namespace for converting between different units of measure") - - -(def ^:const volume-measurement->litre - {:teaspoon 0.00492892 - :tablespoon 0.0147868 - :imperial-fluid-ounce 0.0284131 - :american-fluid-ounce 0.0295735 - :cup 0.236588 - :imperial-pint 0.568261 - :american-pint 0.473176 - :imperial-quart 1.13652 - :american-quart 0.946353 - :imperial-gallon 4.54609 - :american-gallon 3.78541 - :litre 1.0 - :liter 1.0 ; To support regional spelling - :millilitre 0.001 - :milliliter 0.001}) ; To support regional spelling - -(defn liter->volume-measurement - [measurement-name] - (/ 1.0 (get volume-measurement->litre measurement-name))) - - -(defn convert-volume - "Given a `volume` in `source-measurement`, convert it to the `target-measurement`." - [volume source-measurement target-measurement] - (if (= source-measurement target-measurement) - volume - (let [source->litre-multiplier (volume-measurement->litre source-measurement) - litre->target-multiplier (liter->volume-measurement target-measurement)] - (* volume source->litre-multiplier litre->target-multiplier)))) - - -(def ^:const weight-measurement->kilogram - {:ounce 0.02834952 - :pound 0.45359237 - :milligram 0.000001 - :gram 0.001 - :kilogram 1.0}) - - -(defn kilogram->weight-measurement - [measurement-name] - (/ 1.0 (get weight-measurement->kilogram measurement-name))) - - -(defn convert-weight - "Given a `weight` in `source-measurement`, convert it to the `target-measurement`." - [weight source-measurement target-measurement] - (if (= source-measurement target-measurement) - weight - (let [source->kilogram-multiplier (weight-measurement->kilogram source-measurement) - kilogram->target-multiplier (kilogram->weight-measurement target-measurement)] - (* weight source->kilogram-multiplier kilogram->target-multiplier)))) - - -(defn celsius->fahrenheit - [temp] - (+ 32 (* 1.8 temp))) - - -(defn celsius->kelvin - [temp] - (+ temp 273.15)) - - -(defn fahrenheit->celsius - [temp] - (/ (* (- temp 32) 5) 9.0)) - - -(defn kelvin->celsius - [temp] - (- temp 273.15)) - - -(def ^:const temperature-measurement->celsius - {:celsius identity - :c identity - :centigrade identity - :fahrenheit fahrenheit->celsius - :f fahrenheit->celsius - :kelvin kelvin->celsius - :k kelvin->celsius}) - - -(def ^:const celsius->temperature-measurement - {:celsius identity - :c identity - :centigrade identity - :fahrenheit celsius->fahrenheit - :f celsius->fahrenheit - :kelvin celsius->kelvin - :k celsius->kelvin}) - - -(defn convert-temperature - "Given a `temp` in `source-measurement`, convert it to the `target-measurement`." - [temp source-measurement target-measurement] - (if (= source-measurement target-measurement) - temp - (let [source->celsius-fn (temperature-measurement->celsius source-measurement) - celsius->target-fn (celsius->temperature-measurement target-measurement)] - (-> temp source->celsius-fn celsius->target-fn)))) + "Namespace for converting between different units of measure. + + Currently, brewtility supports the following types of measurements: + + - [Color](https://en.wikipedia.org/wiki/Beer_measurement#Colour) + - [Pressure](https://en.wikipedia.org/wiki/Pressure) + - [Specific Gravity](https://en.wikipedia.org/wiki/Relative_density) + - [Temperature](https://en.wikipedia.org/wiki/Temperature) + - [Time](https://en.wikipedia.org/wiki/Time) + - [Volume](https://en.wikipedia.org/wiki/Volume) + - [Weight](https://en.wikipedia.org/wiki/Mass) + + These types of measurement cover four systems of measure: + + - [The British Imperial System](https://en.wikipedia.org/wiki/Imperial_units) + - [The Metric System](https://en.wikipedia.org/wiki/Metric_system) + - [The International System](https://en.wikipedia.org/wiki/International_System_of_Units) + - [The US Customary Scale](https://en.wikipedia.org/wiki/United_States_customary_units)" + {:added "1.0" + :changed "2.0"} + (:require [brewtility.units.color :as color] + [brewtility.units.pressure :as pressure] + [brewtility.units.specific-gravity :as specific-gravity] + [brewtility.units.temperature :as temperature] + [brewtility.units.time :as time] + [brewtility.units.volume :as volume] + [brewtility.units.weight :as weight])) + +(def system-of-measure + "The system of measure used in the recipe or for a given unit. + + Brewility supports the following systems of measure: + - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) + - [Metric system](https://en.wikipedia.org/wiki/Metric_system) + - [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) + - [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units)" + :system-of-measure) + + +(def imperial + "The [British imperial](https://en.wikipedia.org/wiki/Imperial_units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial) + + +(def metric + "The [metric system](https://en.wikipedia.org/wiki/Metric_system) of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :metric) + + +(def us-customary + "The [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions.." + :us) + + +(def international-system + "The [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :si) + +(def systems-of-meaure + "The systems of measure available across brewtility. + + Brewility supports the following systems of measure: + - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) + - [Metric system](https://en.wikipedia.org/wiki/Metric_system) + - [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) + - [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units)" + #{imperial + international-system + metric + us-customary}) + +(def suffix + "The type of suffix to add on to displayed units of measure. + + Brewility supports the following suffix types: + - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") + - `:full` - The full name as a suffix (e.g. \"teaspoon\")" + :suffix) + + +(def short + "A short suffix. + (e.g. \"tsp\" instead of \"teaspoon\")" + :short) + + +(def full + "The full name as a suffix. + (e.g. \"teaspoon\")" + :full) + + +(def supported-suffixes + "A set of supported suffix types. + + Brewility supports the following suffix types: + - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") + - `:full` - The full name as a suffix (e.g. \"teaspoon\")" + #{short full}) + +(def measurement-types + "The measurement types available across brewtility." + #{:color + :pressure + :specific-gravity + :temperature + :time + :volume + :weight}) + +(defn convert + "Given a `measurement` in `source-units`, convert it to the `target-units` in a given `measurement-type`. + + For example: + + ```clj + (convert :weight 1.5 :kg :lb) ;; => 3.31 + ``` + + Supported values for `source-units` and `target-units` are enumerated in each unit system namespace. + This function will throw an exception if unsupported unit values are passed." + {:added "2.0" + :see-also ["brewtility.units.color/convert" + "brewtility.units.pressure/convert" + "brewtility.units.specific-gravity/convert" + "brewtility.units.temperature/convert" + "brewtility.units.time/convert" + "brewtility.units.volume/convert" + "brewtility.units.weight/convert"]} + [measurement-type measurement source-units target-units] + (case measurement-type + :color (color/convert measurement source-units target-units) + :pressure (pressure/convert measurement source-units target-units) + :specific-gravity (specific-gravity/convert measurement source-units target-units) + :temperature (temperature/convert measurement source-units target-units) + :time (time/convert measurement source-units target-units) + :volume (volume/convert measurement source-units target-units) + :weight (weight/convert measurement source-units target-units) + :else (throw (ex-info "Unsupported unit system" + {:measurement-type measurement-type + :allowed-values measurement-types + :measurement measurement})))) + + +(defn display + "A function to render a human-readable `measurement` in `source-units` for a `measurement-type`. + + For example, + ```clj + (display :weight 1.5 :pound) ;; => \"1.5 lb\" + ``` + This function accepts an option map as an optional fourth argument. The following options are available: + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0" + :see-also ["brewtility.units.color/display" + "brewtility.units.pressure/display" + "brewtility.units.specific-gravity/display" + "brewtility.units.temperature/display" + "brewtility.units.time/display" + "brewtility.units.volume/display" + "brewtility.units.weight/display"]} + ([measurement-type measurement source-units] + (display measurement source-units measurement-type {})) + ([measurement-type measurement source-units opts] + + (let [options (merge {:precision 2 suffix short} opts)] + (case measurement-type + :color (color/display measurement source-units options) + :pressure (pressure/display measurement source-units options) + :specific-gravity (specific-gravity/display measurement source-units options) + :temperature (temperature/display measurement source-units options) + :time (time/display measurement source-units options) + :volume (volume/display measurement source-units options) + :weight (weight/display measurement source-units options) + :else (throw (ex-info "Unsupported unit system" + {:measurement-type measurement-type + :allowed-values measurement-types + :measurement measurement})))))) diff --git a/src/brewtility/units/color.cljc b/src/brewtility/units/color.cljc new file mode 100644 index 0000000..c6b4a46 --- /dev/null +++ b/src/brewtility/units/color.cljc @@ -0,0 +1,395 @@ +(ns brewtility.units.color + "A namespace for converting between different units of color. + + By the BeerXML spec, color is measured in different units in different contexts. + This namespace provides a way to convert between SRM and other units of color." + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + + +(def measurements + "The color systems available across brewtility." + #{opts/srm + opts/ebc + opts/lovibond + opts/rgba}) + + +(def measurements->display-name + "A map from color system names to their full and short unit names" + {opts/srm {opts/full "standard reference method" + opts/short "srm"} + opts/ebc {opts/full "ebc" + opts/short "ebc"} + opts/lovibond {opts/full "degrees lovibond" + opts/short "°L"} + opts/rgba {opts/full "" + opts/short ""}}) + + +;; +;; SRM Beer Colors (https://en.wikipedia.org/wiki/Standard_Reference_Method) +;; +(def srm-1 + "An SRM of 1 mapped to an RGBa color code." + + "rgba(255,230,153,1)") + + +(def srm-2 + "An SRM of 2 mapped to an RGBa color code." + + "rgba(255,216,120,1)") + + +(def srm-3 + "An SRM of 3 mapped to an RGBa color code." + + "rgba(255,202,90,1)") + + +(def srm-4 + "An SRM of 4 mapped to an RGBa color code." + "rgba(255,191,66,1)") + + +(def srm-5 + "An SRM of 5 mapped to an RGBa color code." + "rgba(251,177,35,1)") + + +(def srm-6 + "An SRM of 6 mapped to an RGBa color code." + "rgba(248,166,0,1)") + + +(def srm-7 + "An SRM of 7 mapped to an RGBa color code." + "rgba(243,156,0,1)") + + +(def srm-8 + "An SRM of 8 mapped to an RGBa color code." + "rgba(234,143,0,1)") + + +(def srm-9 + "An SRM of 9 mapped to an RGBa color code." + "rgba(229,133,0,1)") + + +(def srm-10 + "An SRM of 10 mapped to an RGBa color code." + "rgba(222,124,0,1)") + + +(def srm-11 + "An SRM of 11 mapped to an RGBa color code." + "rgba(215,114,0,1)") + + +(def srm-12 + "An SRM of 12 mapped to an RGBa color code." + "rgba(207,105,0,1)") + + +(def srm-13 + "An SRM of 13 mapped to an RGBa color code." + "rgba(203,98,0,1)") + + +(def srm-14 + "An SRM of 14 mapped to an RGBa color code." + "rgba(195,89,0,1)") + + +(def srm-15 + "An SRM of 15 mapped to an RGBa color code." + "rgba(187,81,0,1)") + + +(def srm-16 + "An SRM of 16 mapped to an RGBa color code." + "rgba(181,76,0,1)") + + +(def srm-17 + "An SRM of 17 mapped to an RGBa color code." + "rgba(176,69,0,1)") + + +(def srm-18 + "An SRM of 18 mapped to an RGBa color code." + "rgba(166,62,0,1)") + + +(def srm-19 + "An SRM of 19 mapped to an RGBa color code." + "rgba(161,55,0,1)") + + +(def srm-20 + "An SRM of 20 mapped to an RGBa color code." + "rgba(155,50,0,1)") + + +(def srm-21 + "An SRM of 21 mapped to an RGBa color code." + "rgba(149,45,0,1)") + + +(def srm-22 + "An SRM of 22 mapped to an RGBa color code." + "rgba(142,41,0,1)") + + +(def srm-23 + "An SRM of 23 mapped to an RGBa color code." + "rgba(136,35,0,1)") + + +(def srm-24 + "An SRM of 24 mapped to an RGBa color code." + "rgba(130,30,0,1)") + + +(def srm-25 + "An SRM of 25 mapped to an RGBa color code." + "rgba(123,26,0,1)") + + +(def srm-26 + "An SRM of 26 mapped to an RGBa color code." + "rgba(119,25,0,1)") + + +(def srm-27 + "An SRM of 27 mapped to an RGBa color code." + "rgba(112,20,0,1)") + + +(def srm-28 + "An SRM of 28 mapped to an RGBa color code." + "rgba(106,14,0,1)") + + +(def srm-29 + "An SRM of 29 mapped to an RGBa color code." + "rgba(102,13,0,1)") + + +(def srm-30 + "An SRM of 30 mapped to an RGBa color code." + "rgba(94,11,0,1)") + + +(def srm-31 + "An SRM of 31 mapped to an RGBa color code." + "rgba(90,10,2,1)") + + +(def srm-32 + "An SRM of 32 mapped to an RGBa color code." + "rgba(96,9,3,1)") + + +(def srm-33 + "An SRM of 33 mapped to an RGBa color code." + "rgba(82,9,7,1)") + + +(def srm-34 + "An SRM of 34 mapped to an RGBa color code." + "rgba(76,5,5,1)") + + +(def srm-35 + "An SRM of 35 mapped to an RGBa color code." + "rgba(71,6,6,1)") + + +(def srm-36 + "An SRM of 36 mapped to an RGBa color code." + "rgba(68,6,7,1)") + + +(def srm-37 + "An SRM of 37 mapped to an RGBa color code." + "rgba(63,7,8,1)") + + +(def srm-38 + "An SRM of 38 mapped to an RGBa color code." + "rgba(59,6,7,1)") + + +(def srm-39 + "An SRM of 39 mapped to an RGBa color code." + "rgba(58,7,11,1)") + + +(def srm-40 + "An SRM of 40 mapped to an RGBa color code." + "rgba(3,4,3,1)") + + +(def srm-color-map + "A map of integer values to their closest SRM value as an RGBa color code." + {1 srm-1 + 2 srm-2 + 3 srm-3 + 4 srm-4 + 5 srm-5 + 6 srm-6 + 7 srm-7 + 8 srm-8 + 9 srm-9 + 10 srm-10 + 11 srm-11 + 12 srm-12 + 13 srm-13 + 14 srm-14 + 15 srm-15 + 16 srm-16 + 17 srm-17 + 18 srm-18 + 19 srm-19 + 20 srm-20 + 21 srm-21 + 22 srm-22 + 23 srm-23 + 24 srm-24 + 25 srm-25 + 26 srm-26 + 27 srm-27 + 28 srm-28 + 29 srm-29 + 30 srm-30 + 31 srm-31 + 32 srm-32 + 33 srm-33 + 34 srm-34 + 35 srm-35 + 36 srm-36 + 37 srm-37 + 38 srm-38 + 39 srm-39 + 40 srm-40}) + + +(defn- lovibond->srm + "Convert the color described in degrees `lovibond` to the equivalent SRM color." + {:added "2.0"} + [degrees-lovibond] + (- (* degrees-lovibond 1.3546) 0.76)) + + +(defn- srm->ebc + "Convert the color described by the `srm` to the equivalent EBC color." + {:added "2.0"} + [srm-value] + (* srm-value 1.97)) + + +(defn- ebc->srm + "Convert the color described by the `ebc` to the equivalent SRM color." + {:added "2.0"} + [ebc-value] + (* ebc-value 0.508)) + + +(defn- srm->lovibond + "Convert the color described in 'srm` to the equivalent degrees Lovibond." + {:added "2.0"} + [srm-value] + (/ (+ srm-value 0.76) 1.3546)) + + +(defn- srm->rgba + "Given `srm-number`, return the closest, bounded applicable RGBA color string. + OPINIONATED: The provided `srm-number` will be bound to the common range from 1 to 40. + Decimal-like values are trimmed, not rounded." + {:added "2.0"} + [srm-number] + (let [srm-color (min (max (int srm-number) 1) 40)] + (get srm-color-map srm-color))) + + +(def measurement->srm + "A map from color systems to the implementation function that converts to SRM. + + Note: RGBa is not included because it is not a color system, but a color representation." + {opts/ebc ebc->srm + opts/lovibond lovibond->srm + opts/srm identity}) + + +(def srm->measurement + "A map from color systems to the implementation function that converts from SRM" + {opts/ebc srm->ebc + opts/lovibond srm->lovibond + opts/srm identity + opts/rgba srm->rgba}) + + +(defn convert + "Given a `color-val` in `source-system`, convert it to the `target-system`. + + Supported values for `source-system` and `target-system` are enumerated in [[color-systems]]. + This function will throw an exception if unsupported measurement values are passed." + {:added "2.0"} + [color-val source-system target-system] + (if (and (contains? measurement->srm source-system) + (contains? measurements target-system) + (number? color-val)) + (if (= source-system target-system) + color-val + (let [source->srm-fn (measurement->srm source-system) + srm->target-fn (srm->measurement target-system)] + (-> color-val + source->srm-fn + srm->target-fn))) + (throw (ex-info "Unsupported color conversion units" + {:source-system source-system + :target-system target-system + :allowed-values (-> measurement->srm keys set) + :color color-val})))) + + +(defn display + "A function to render a human-readable `color` in `source-units`. + + For example, + ```clj + (display 1.5 :lovibond) ;; => \"1.5 °L\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"°L\"` for `\"Degrees Lovibond\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([color source-units] + (display color source-units {})) + ([color source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? color) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (if (= source-units :rgba) + color + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> color + (precision/->precision precision) + (str " " display-name)))) + (throw (ex-info "Unsupported color display options" + {:source-units source-units + :allowed-values measurements + :color color + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/src/brewtility/units/options.cljc b/src/brewtility/units/options.cljc new file mode 100644 index 0000000..245f63b --- /dev/null +++ b/src/brewtility/units/options.cljc @@ -0,0 +1,389 @@ +(ns brewtility.units.options + "A namespace of static symbolic keywords and values used in the option maps throughout brewtility. + + Implemented as part of the Symbolic Keyword pattern." + {:added "2.0"} + (:refer-clojure :exclude [short second])) + +;; Defaults + +(def default-precision + "The default precision to use in `->displayable` functions" + 3) + + +;; Systems of Measure + +(def system-of-measure + "The system of measure used in the recipe or for a given unit. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Brewility supports the following systems of measure: + - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) + - [Metric system](https://en.wikipedia.org/wiki/Metric_system) + - [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) + - [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units)" + :system-of-measure) + + +(def imperial + "The [British imperial](https://en.wikipedia.org/wiki/Imperial_units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial) + + +(def metric + "The [metric system](https://en.wikipedia.org/wiki/Metric_system) of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :metric) + + +(def us-customary + "The [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions.." + :us) + + +(def international-system + "The [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units) system of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :si) + + +;; Enricher Setting Keys + + +(def precision + "The number of decimal places to which a value is rounded. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :precision) + + +(def suffix + "The type of suffix to add on to displayed units of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Brewility supports the following suffix types: + - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") + - `:full` - The full name as a suffix (e.g. \"teaspoon\")" + :suffix) + + +(def short + "A short suffix. + (e.g. \"tsp\" instead of \"teaspoon\") + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :short) + + +(def full + "The full name as a suffix. + (e.g. \"teaspoon\") + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :full) + + +(def supported-suffixes + "A set of supported suffix types." + #{short full}) + + +;; Color Systems + +(def srm + "The [Standard Reference Method](https://en.wikipedia.org/wiki/Standard_Reference_Method) color system. + + Commonly used with `brewtility.color` and in argument maps for enricher functions." + :srm) + + +(def ebc + "The [European Brewery Convention](https://en.wikipedia.org/wiki/European_Brewery_Convention) color system. + + Commonly used with `brewtility.color` and in argument maps for enricher functions." + :ebc) + + +(def lovibond + "The [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) color system. + + Commonly used with `brewtility.color` and in argument maps for enricher functions." + :lovibond) + + +(def rgba + "The [RGBA](https://en.wikipedia.org/wiki/RGBA_color_space) color system. + + Commonly used with `brewtility.color` and in argument maps for enricher functions." + :rgba) + + +;; Volume Units + +(def american-fluid-ounce + "The [American fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce#American_fluid_ounce) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :american-fluid-ounce) + + +(def american-gallon + "The [American gallon](https://en.wikipedia.org/wiki/Gallon#Us_liquid_gallon) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :american-gallon) + + +(def american-pint + "The [American pint](https://en.wikipedia.org/wiki/Pint#US_liquid_pint) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :american-pint) + + +(def american-quart + "The [American quart](https://en.wikipedia.org/wiki/Quart#US_liquid_quart) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :american-quart) + + +(def cup + "The [cup](https://en.wikipedia.org/wiki/Cup_(unit)) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :cup) + + +(def imperial-fluid-ounce + "The [Imperial fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce#Imperial_fluid_ounce) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial-fluid-ounce) + + +(def imperial-gallon + "The [Imperial gallon](https://en.wikipedia.org/wiki/Gallon#Imperial_gallon) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial-gallon) + + +(def imperial-pint + "The [Imperial pint](https://en.wikipedia.org/wiki/Pint#Imperial_pint) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial-pint) + + +(def imperial-quart + "The [Imperial quart](https://en.wikipedia.org/wiki/Quart#Imperial_quart) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :imperial-quart) + + +(def liter + "The [liter](https://en.wikipedia.org/wiki/Liter) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: Both `litre` and `liter` are valid keys throughout brewtility to support regional spelling." + :liter) + + +(def litre + "The [liter](https://en.wikipedia.org/wiki/Litre) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: Both `litre` and `liter` are valid keys throughout brewtility to support regional spelling." + :litre) + + +(def milliliter + "The [milliliter](https://en.wikipedia.org/wiki/Milliliter) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: Both `millilitre` and `milliliter` are valid keys throughout brewtility to support regional spelling." + :milliliter) + + +(def millilitre + "The [milliliter](https://en.wikipedia.org/wiki/Millilitre) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: Both `millilitre` and `milliliter` are valid keys throughout brewtility to support regional spelling." + :millilitre) + + +(def teaspoon + "The [teaspoon](https://en.wikipedia.org/wiki/Teaspoon) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :teaspoon) + + +(def tablespoon + "The [tablespoon](https://en.wikipedia.org/wiki/Tablespoon) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :tablespoon) + + +;; Weight/Mass Units + +(def gram + "The [gram](https://en.wikipedia.org/wiki/Gram) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :gram) + + +(def kilogram + "The [kilogram](https://en.wikipedia.org/wiki/Kilogram) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :kilogram) + + +(def milligram + "The [milligram](https://en.wikipedia.org/wiki/Milligram) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :milligram) + + +(def ounce + "The [ounce](https://en.wikipedia.org/wiki/Ounce#International_avoirdupois_ounce) unit of measure; specifically, the International avoirdupois ounce. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :ounce) + + +(def pound + "The [pound](https://en.wikipedia.org/wiki/Pound_(mass)) unit of measure + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :pound) + + +;; Temperature Units + +(def celsius + "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." + :celsius) + + +(def c + "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." + :c) + + +(def centigrade + "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." + :centigrade) + + +(def fahrenheit + "The [Fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `fahrenheit` and `f` are all valid keys throughout brewtility to support multiple conventions." + :fahrenheit) + + +(def f + "The [Fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `fahrenheit` and `f` are all valid keys throughout brewtility to support multiple conventions." + :f) + + +(def kelvin + "The [Kelvin](https://en.wikipedia.org/wiki/Kelvin) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `kelvin` and `k` are all valid keys throughout brewtility to support multiple conventions." + :kelvin) + + +(def k + "The [Kelvin](https://en.wikipedia.org/wiki/Kelvin) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions. + Note: `kelvin` and `k` are all valid keys throughout brewtility to support multiple conventions." + :k) + + +;; Time Units + +(def day + "The [day](https://en.wikipedia.org/wiki/Day) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :day) + + +(def hour + "The [hour](https://en.wikipedia.org/wiki/Hour) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :hour) + + +(def microsecond + "The [microsecond](https://en.wikipedia.org/wiki/Microsecond) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :microsecond) + + +(def millisecond + "The [millisecond](https://en.wikipedia.org/wiki/Millisecond) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :millisecond) + + +(def minute + "The [minute](https://en.wikipedia.org/wiki/Minute) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :minute) + + +(def nanosecond + "The [nanosecond](https://en.wikipedia.org/wiki/Nanosecond) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :nanosecond) + + +(def second + "The [second](https://en.wikipedia.org/wiki/Second) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :second) + + +(def week + "The [week](https://en.wikipedia.org/wiki/Week) unit of measure. + + Commonly used with `brewtility.units` and in argument maps for enricher functions." + :week) diff --git a/test/brewtility/data/equipment.cljc b/test/brewtility/data/equipment.cljc index e9941ef..76b7864 100644 --- a/test/brewtility/data/equipment.cljc +++ b/test/brewtility/data/equipment.cljc @@ -8,7 +8,7 @@ #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) -(def ^:const sample-equipment +(def sample-equipment "A hard-coded sample equipment for static unit tests" {:batch-size 19.9 :boil-size 26.2 @@ -28,7 +28,7 @@ :version 1}) -(def ^:const sample-equipment-wrapper +(def sample-equipment-wrapper "A hard-coded sample equipment-wrapper for static unit tests" {:equipment sample-equipment}) diff --git a/test/brewtility/data/fermentables.cljc b/test/brewtility/data/fermentables.cljc index ca23319..544030e 100644 --- a/test/brewtility/data/fermentables.cljc +++ b/test/brewtility/data/fermentables.cljc @@ -8,7 +8,7 @@ #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) -(def ^:const sample-fermentable +(def sample-fermentable "A hard-coded sample fermentable for static unit tests" {:amount 0.45 :coarse-fine-diff 1.5 @@ -26,17 +26,17 @@ :yield 78.0}) -(def ^:const sample-fermentable-wrapper +(def sample-fermentable-wrapper "A hard-coded sample fermentable-wrapper for static unit tests" {:fermentable sample-fermentable}) -(def ^:const sample-fermentables +(def sample-fermentables "A hard-coded sample fermentables for static unit tests" [sample-fermentable-wrapper]) -(def ^:const sample-fermentables-wrapper +(def sample-fermentables-wrapper "A hard-coded sample fermentables-wrapper for static unit tests" {:fermentables sample-fermentables}) diff --git a/test/brewtility/data/hops.cljc b/test/brewtility/data/hops.cljc index e7b781e..0ed0a19 100644 --- a/test/brewtility/data/hops.cljc +++ b/test/brewtility/data/hops.cljc @@ -41,7 +41,7 @@ (is (spoon.spec/test-valid? ::hops.format/form (random-hop-form))))) -(def ^:const sample-hop +(def sample-hop "A hard-coded sample hop for static unit tests" {:alpha 5.0 :amount 0.0638 @@ -52,17 +52,17 @@ :version 1}) -(def ^:const sample-hop-wrapper +(def sample-hop-wrapper "A hard-coded sample hop-wrapper for static unit tests" {:hop sample-hop}) -(def ^:const sample-hops +(def sample-hops "A hard-coded sample hops for static unit tests" [sample-hop-wrapper]) -(def ^:const sample-hops-wrapper +(def sample-hops-wrapper "A hard-coded sample hops-wrapper for static unit tests" {:hops sample-hops}) diff --git a/test/brewtility/data/mash.cljc b/test/brewtility/data/mash.cljc index 8a688a7..99f3234 100644 --- a/test/brewtility/data/mash.cljc +++ b/test/brewtility/data/mash.cljc @@ -8,7 +8,7 @@ #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) -(def ^:const sample-mash-step +(def sample-mash-step "A hard-coded sample mash step for static unit tests" {:infuse-amount 10.0 :name "Conversion Step, 68C" @@ -18,17 +18,17 @@ :version 1}) -(def ^:const sample-mash-step-wrapper +(def sample-mash-step-wrapper "A hard-coded sample mash-step-wrapper for static unit tests" {:mash-step sample-mash-step}) -(def ^:const sample-mash-steps +(def sample-mash-steps "A hard-coded sample mash-steps for static unit tests" [sample-mash-step-wrapper]) -(def ^:const sample-mash +(def sample-mash "A hard-coded sample mash for static unit tests" {:name "Single Step Infusion, 68 C" :version 1 @@ -36,7 +36,7 @@ :mash-steps sample-mash-steps}) -(def ^:const sample-mash-wrapper +(def sample-mash-wrapper "A hard-coded sample mash-wrapper for static unit tests" {:mash sample-mash}) diff --git a/test/brewtility/data/miscs.cljc b/test/brewtility/data/miscs.cljc index d81c9a2..b40f46f 100644 --- a/test/brewtility/data/miscs.cljc +++ b/test/brewtility/data/miscs.cljc @@ -8,7 +8,7 @@ #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) -(def ^:const sample-misc +(def sample-misc "A hard-coded sample misc for static unit tests" {:amount 0.010 :name "Irish Moss" @@ -19,17 +19,17 @@ :version 1}) -(def ^:const sample-misc-wrapper +(def sample-misc-wrapper "A hard-coded sample misc-wrapper for static unit tests" {:misc sample-misc}) -(def ^:const sample-miscs +(def sample-miscs "A hard-coded sample miscs for static unit tests" [sample-misc-wrapper]) -(def ^:const sample-miscs-wrapper +(def sample-miscs-wrapper "A hard-coded sample miscs-wrapper for static unit tests" {:miscs sample-miscs}) diff --git a/test/brewtility/data/recipes.cljc b/test/brewtility/data/recipes.cljc index 5fd8b06..d7f6fe5 100644 --- a/test/brewtility/data/recipes.cljc +++ b/test/brewtility/data/recipes.cljc @@ -33,7 +33,7 @@ (is (spoon.spec/test-valid? ::recipes.format/ibu-method (random-ibu-method))))) -(def ^:const sample-recipe +(def sample-recipe "A hard-coded sample recipe for static unit tests" (merge {:age 24.0 :age-temp 17.0 @@ -63,17 +63,17 @@ yeasts/sample-yeasts-wrapper)) -(def ^:const sample-recipe-wrapper +(def sample-recipe-wrapper "A hard-coded sample recipe-wrapper for static unit tests" {:recipe sample-recipe}) -(def ^:const sample-recipes +(def sample-recipes "A hard-coded sample recipes for static unit tests" [sample-recipe-wrapper]) -(def ^:const sample-recipes-wrapper +(def sample-recipes-wrapper "A hard-coded sample recipes-wrapper for static unit tests" {:recipes sample-recipes}) diff --git a/test/brewtility/data/styles.cljc b/test/brewtility/data/styles.cljc index debc1a4..41c744b 100644 --- a/test/brewtility/data/styles.cljc +++ b/test/brewtility/data/styles.cljc @@ -8,7 +8,7 @@ #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) -(def ^:const sample-style +(def sample-style "A hard-coded sample style for static unit tests" {:abv-max 5.5 :abv-min 3.2 @@ -32,17 +32,17 @@ :version 1}) -(def ^:const sample-style-wrapper +(def sample-style-wrapper "A hard-coded sample style-wrapper for static unit tests" {:style sample-style}) -(def ^:const sample-styles +(def sample-styles "A hard-coded sample styles for static unit tests" [sample-style-wrapper]) -(def ^:const sample-styles-wrapper +(def sample-styles-wrapper "A hard-coded sample styles-wrapper for static unit tests" {:styles sample-styles}) diff --git a/test/brewtility/data/waters.cljc b/test/brewtility/data/waters.cljc index 16c8834..19b7144 100644 --- a/test/brewtility/data/waters.cljc +++ b/test/brewtility/data/waters.cljc @@ -21,7 +21,7 @@ (is (spoon.spec/test-valid? ::waters.format/ph (random-ph))))) -(def ^:const sample-water +(def sample-water "A hard-coded sample water for static unit tests" {:amount 20.0 :bicarbonate 300.0 @@ -36,17 +36,17 @@ :version 1}) -(def ^:const sample-water-wrapper +(def sample-water-wrapper "A hard-coded sample water-wrapper for static unit tests" {:water sample-water}) -(def ^:const sample-waters +(def sample-waters "A hard-coded sample waters for static unit tests" [sample-water-wrapper]) -(def ^:const sample-waters-wrapper +(def sample-waters-wrapper "A hard-coded sample waters-wrapper for static unit tests" {:waters sample-waters}) diff --git a/test/brewtility/data/yeasts.cljc b/test/brewtility/data/yeasts.cljc index 73fee94..1095176 100644 --- a/test/brewtility/data/yeasts.cljc +++ b/test/brewtility/data/yeasts.cljc @@ -25,7 +25,7 @@ (is (spoon.spec/test-valid? ::yeasts.format/flocculation (random-flocculation))))) -(def ^:const sample-yeast +(def sample-yeast "A hard-coded sample yeast for static unit tests" {:amount 0.250 :attenuation 73.0 @@ -42,17 +42,17 @@ :version 1}) -(def ^:const sample-yeast-wrapper +(def sample-yeast-wrapper "A hard-coded sample yeast-wrapper for static unit tests" {:yeast sample-yeast}) -(def ^:const sample-yeasts +(def sample-yeasts "A hard-coded sample yeasts for static unit tests" [sample-yeast-wrapper]) -(def ^:const sample-yeasts-wrapper +(def sample-yeasts-wrapper "A hard-coded sample yeasts-wrapper for static unit tests" {:yeasts sample-yeasts}) diff --git a/test/brewtility/units/color_test.cljc b/test/brewtility/units/color_test.cljc new file mode 100644 index 0000000..fa569d5 --- /dev/null +++ b/test/brewtility/units/color_test.cljc @@ -0,0 +1,94 @@ +(ns brewtility.units.color-test + (:require [brewtility.precision :as precision] + [brewtility.units.color :as sut] + [brewtility.units.options :as options] + #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) + + +(deftest srm->rgba-test + (testing "SRM color lookup behaves as expected" + (is (= sut/srm-1 (sut/convert 1 options/srm options/rgba))) + (is (= sut/srm-1 (sut/convert 1 :srm :rgba))) + (is (= sut/srm-2 (sut/convert 2 options/srm options/rgba))) + (is (= sut/srm-3 (sut/convert 3 options/srm options/rgba))) + (is (= sut/srm-4 (sut/convert 4 options/srm options/rgba))) + (is (= sut/srm-5 (sut/convert 5 options/srm options/rgba))) + (is (= sut/srm-6 (sut/convert 6 options/srm options/rgba))) + (is (= sut/srm-7 (sut/convert 7 options/srm options/rgba))) + (is (= sut/srm-8 (sut/convert 8 options/srm options/rgba))) + (is (= sut/srm-9 (sut/convert 9 options/srm options/rgba))) + (is (= sut/srm-10 (sut/convert 10 options/srm options/rgba))) + (is (= sut/srm-11 (sut/convert 11 options/srm options/rgba))) + (is (= sut/srm-12 (sut/convert 12 options/srm options/rgba))) + (is (= sut/srm-13 (sut/convert 13 options/srm options/rgba))) + (is (= sut/srm-14 (sut/convert 14 options/srm options/rgba))) + (is (= sut/srm-15 (sut/convert 15 options/srm options/rgba))) + (is (= sut/srm-16 (sut/convert 16 options/srm options/rgba))) + (is (= sut/srm-17 (sut/convert 17 options/srm options/rgba))) + (is (= sut/srm-18 (sut/convert 18 options/srm options/rgba))) + (is (= sut/srm-19 (sut/convert 19 options/srm options/rgba))) + (is (= sut/srm-20 (sut/convert 20 options/srm options/rgba))) + (is (= sut/srm-21 (sut/convert 21 options/srm options/rgba))) + (is (= sut/srm-22 (sut/convert 22 options/srm options/rgba))) + (is (= sut/srm-23 (sut/convert 23 options/srm options/rgba))) + (is (= sut/srm-24 (sut/convert 24 options/srm options/rgba))) + (is (= sut/srm-25 (sut/convert 25 options/srm options/rgba))) + (is (= sut/srm-26 (sut/convert 26 options/srm options/rgba))) + (is (= sut/srm-27 (sut/convert 27 options/srm options/rgba))) + (is (= sut/srm-28 (sut/convert 28 options/srm options/rgba))) + (is (= sut/srm-29 (sut/convert 29 options/srm options/rgba))) + (is (= sut/srm-30 (sut/convert 30 options/srm options/rgba))) + (is (= sut/srm-31 (sut/convert 31 options/srm options/rgba))) + (is (= sut/srm-32 (sut/convert 32 options/srm options/rgba))) + (is (= sut/srm-33 (sut/convert 33 options/srm options/rgba))) + (is (= sut/srm-34 (sut/convert 34 options/srm options/rgba))) + (is (= sut/srm-35 (sut/convert 35 options/srm options/rgba))) + (is (= sut/srm-36 (sut/convert 36 options/srm options/rgba))) + (is (= sut/srm-37 (sut/convert 37 options/srm options/rgba))) + (is (= sut/srm-38 (sut/convert 38 options/srm options/rgba))) + (is (= sut/srm-39 (sut/convert 39 options/srm options/rgba))) + (is (= sut/srm-40 (sut/convert 40 options/srm options/rgba))) + (is (= sut/srm-1 (sut/convert 0 options/srm options/rgba))) + (is (= sut/srm-1 (sut/convert -1 options/srm options/rgba))) + (is (= sut/srm-6 (sut/convert 6.2 options/srm options/rgba))) + (is (= sut/srm-6 (sut/convert 6.8 options/srm options/rgba))) + (is (= sut/srm-40 (sut/convert 41 options/srm options/rgba))))) + + +(deftest lovibond->rgba-test + (testing "lovibond -> SRM -> rgba lookup behaves as expected" + (is (= sut/srm-13 (sut/convert 10.16 options/lovibond options/rgba))) + (is (= sut/srm-13 (sut/convert 10.16 :lovibond :rgba))) + (is (= sut/srm-1 (sut/convert 0.56 options/lovibond options/rgba))) + (is (= sut/srm-1 (sut/convert -0.18 options/lovibond options/rgba))) + (is (= sut/srm-6 (sut/convert 5.14 options/lovibond options/rgba))) + (is (= sut/srm-6 (sut/convert 5.58 options/lovibond options/rgba))) + (is (= sut/srm-40 (sut/convert 30.83 options/lovibond options/rgba))))) + + +(deftest ebc->rgba-test + (testing "EBC -> SRM -> rgba lookup behaves as expected" + (is (= sut/srm-13 (sut/convert 25.61 options/ebc options/rgba))) + (is (= sut/srm-13 (sut/convert 25.61 :ebc :rgba))) + (is (= sut/srm-1 (sut/convert 0 options/ebc options/rgba))) + (is (= sut/srm-1 (sut/convert -1.97 options/ebc options/rgba))) + (is (= sut/srm-6 (sut/convert 12.21 options/ebc options/rgba))) + (is (= sut/srm-6 (sut/convert 13.40 options/ebc options/rgba))) + (is (= sut/srm-40 (sut/convert 80.77 options/ebc options/rgba))))) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 10.0 + (precision/->2dp (sut/convert 7.94 options/lovibond options/srm)) + (precision/->2dp (sut/convert 19.69 options/ebc options/srm)))) + (is (= 10.0 + (precision/->2dp (sut/convert 7.94 :lovibond :srm)) + (precision/->2dp (sut/convert 19.69 :ebc :srm)))) + (is (= 23.2 + (precision/->2dp (sut/convert 30.66 options/srm options/lovibond)) + (precision/->2dp (sut/convert 60.38 options/ebc options/lovibond)))) + (is (= -45.53 + (precision/->2dp (sut/convert -23.11 options/srm options/ebc)) + (precision/->2dp (sut/convert -16.5 options/lovibond options/ebc)))))) From bb5fe4473e8cf506d517b623f1a56e6e95f3055b Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Thu, 25 May 2023 21:16:55 +0000 Subject: [PATCH 02/11] WIP --- src/brewtility/units.cljc | 97 +--------- src/brewtility/units/color.cljc | 10 +- src/brewtility/units/options.cljc | 156 ++++++++++++----- src/brewtility/units/pressure.cljc | 195 +++++++++++++++++++++ src/brewtility/units/specific_gravity.cljc | 88 ++++++++++ 5 files changed, 403 insertions(+), 143 deletions(-) create mode 100644 src/brewtility/units/pressure.cljc create mode 100644 src/brewtility/units/specific_gravity.cljc diff --git a/src/brewtility/units.cljc b/src/brewtility/units.cljc index 1fa1ccf..1648e01 100644 --- a/src/brewtility/units.cljc +++ b/src/brewtility/units.cljc @@ -20,6 +20,7 @@ {:added "1.0" :changed "2.0"} (:require [brewtility.units.color :as color] + [brewtility.units.options :as opts] [brewtility.units.pressure :as pressure] [brewtility.units.specific-gravity :as specific-gravity] [brewtility.units.temperature :as temperature] @@ -27,96 +28,6 @@ [brewtility.units.volume :as volume] [brewtility.units.weight :as weight])) -(def system-of-measure - "The system of measure used in the recipe or for a given unit. - - Brewility supports the following systems of measure: - - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) - - [Metric system](https://en.wikipedia.org/wiki/Metric_system) - - [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) - - [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units)" - :system-of-measure) - - -(def imperial - "The [British imperial](https://en.wikipedia.org/wiki/Imperial_units) system of measure. - - Commonly used with `brewtility.units` and in argument maps for enricher functions." - :imperial) - - -(def metric - "The [metric system](https://en.wikipedia.org/wiki/Metric_system) of measure. - - Commonly used with `brewtility.units` and in argument maps for enricher functions." - :metric) - - -(def us-customary - "The [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) system of measure. - - Commonly used with `brewtility.units` and in argument maps for enricher functions.." - :us) - - -(def international-system - "The [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units) system of measure. - - Commonly used with `brewtility.units` and in argument maps for enricher functions." - :si) - -(def systems-of-meaure - "The systems of measure available across brewtility. - - Brewility supports the following systems of measure: - - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) - - [Metric system](https://en.wikipedia.org/wiki/Metric_system) - - [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) - - [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units)" - #{imperial - international-system - metric - us-customary}) - -(def suffix - "The type of suffix to add on to displayed units of measure. - - Brewility supports the following suffix types: - - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") - - `:full` - The full name as a suffix (e.g. \"teaspoon\")" - :suffix) - - -(def short - "A short suffix. - (e.g. \"tsp\" instead of \"teaspoon\")" - :short) - - -(def full - "The full name as a suffix. - (e.g. \"teaspoon\")" - :full) - - -(def supported-suffixes - "A set of supported suffix types. - - Brewility supports the following suffix types: - - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") - - `:full` - The full name as a suffix (e.g. \"teaspoon\")" - #{short full}) - -(def measurement-types - "The measurement types available across brewtility." - #{:color - :pressure - :specific-gravity - :temperature - :time - :volume - :weight}) - (defn convert "Given a `measurement` in `source-units`, convert it to the `target-units` in a given `measurement-type`. @@ -147,7 +58,7 @@ :weight (weight/convert measurement source-units target-units) :else (throw (ex-info "Unsupported unit system" {:measurement-type measurement-type - :allowed-values measurement-types + :allowed-values opts/measurement-types :measurement measurement})))) @@ -175,7 +86,7 @@ (display measurement source-units measurement-type {})) ([measurement-type measurement source-units opts] - (let [options (merge {:precision 2 suffix short} opts)] + (let [options (merge {opts/precision opts/default-precision opts/suffix opts/short} opts)] (case measurement-type :color (color/display measurement source-units options) :pressure (pressure/display measurement source-units options) @@ -186,5 +97,5 @@ :weight (weight/display measurement source-units options) :else (throw (ex-info "Unsupported unit system" {:measurement-type measurement-type - :allowed-values measurement-types + :allowed-values opts/measurement-types :measurement measurement})))))) diff --git a/src/brewtility/units/color.cljc b/src/brewtility/units/color.cljc index c6b4a46..e85e83c 100644 --- a/src/brewtility/units/color.cljc +++ b/src/brewtility/units/color.cljc @@ -1,8 +1,14 @@ (ns brewtility.units.color "A namespace for converting between different units of color. - By the BeerXML spec, color is measured in different units in different contexts. - This namespace provides a way to convert between SRM and other units of color." + In the BeerXML spec, color is measured in different units in different contexts. + This namespace provides a way to convert between SRM and other units of color. + + Currently, brewtility supports the following types of color: + - [SRM](https://en.wikipedia.org/wiki/Standard_Reference_Method) + - [EBC](https://en.wikipedia.org/wiki/European_Brewery_Convention) + - [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) + - [RGBa](https://en.wikipedia.org/wiki/RGBA_color_model)" {:added "2.0"} (:require [brewtility.precision :as precision] [brewtility.units.options :as opts])) diff --git a/src/brewtility/units/options.cljc b/src/brewtility/units/options.cljc index 245f63b..c100b95 100644 --- a/src/brewtility/units/options.cljc +++ b/src/brewtility/units/options.cljc @@ -17,7 +17,7 @@ (def system-of-measure "The system of measure used in the recipe or for a given unit. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Brewility supports the following systems of measure: - [British imperial](https://en.wikipedia.org/wiki/Imperial_units) - [Metric system](https://en.wikipedia.org/wiki/Metric_system) @@ -29,28 +29,28 @@ (def imperial "The [British imperial](https://en.wikipedia.org/wiki/Imperial_units) system of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :imperial) (def metric "The [metric system](https://en.wikipedia.org/wiki/Metric_system) of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :metric) (def us-customary "The [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) system of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions.." + Commonly used with `brewtility.units` and in argument/option maps.." :us) (def international-system "The [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units) system of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :si) @@ -60,14 +60,14 @@ (def precision "The number of decimal places to which a value is rounded. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :precision) (def suffix "The type of suffix to add on to displayed units of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Brewility supports the following suffix types: - `:short` - A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") - `:full` - The full name as a suffix (e.g. \"teaspoon\")" @@ -78,7 +78,7 @@ "A short suffix. (e.g. \"tsp\" instead of \"teaspoon\") - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :short) @@ -86,7 +86,7 @@ "The full name as a suffix. (e.g. \"teaspoon\") - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :full) @@ -94,34 +94,44 @@ "A set of supported suffix types." #{short full}) +(def measurement-types + "The measurement types available across brewtility." + #{:color + :pressure + :specific-gravity + :temperature + :time + :volume + :weight}) + ;; Color Systems (def srm "The [Standard Reference Method](https://en.wikipedia.org/wiki/Standard_Reference_Method) color system. - Commonly used with `brewtility.color` and in argument maps for enricher functions." + Commonly used with `brewtility.units.color` and in argument/option maps." :srm) (def ebc "The [European Brewery Convention](https://en.wikipedia.org/wiki/European_Brewery_Convention) color system. - Commonly used with `brewtility.color` and in argument maps for enricher functions." + Commonly used with `brewtility.units.color` and in argument/option maps." :ebc) (def lovibond "The [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) color system. - Commonly used with `brewtility.color` and in argument maps for enricher functions." + Commonly used with `brewtility.units.color` and in argument/option maps." :lovibond) (def rgba "The [RGBA](https://en.wikipedia.org/wiki/RGBA_color_space) color system. - Commonly used with `brewtility.color` and in argument maps for enricher functions." + Commonly used with `brewtility.units.color` and in argument/option maps." :rgba) @@ -130,70 +140,70 @@ (def american-fluid-ounce "The [American fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce#American_fluid_ounce) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :american-fluid-ounce) (def american-gallon "The [American gallon](https://en.wikipedia.org/wiki/Gallon#Us_liquid_gallon) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :american-gallon) (def american-pint "The [American pint](https://en.wikipedia.org/wiki/Pint#US_liquid_pint) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :american-pint) (def american-quart "The [American quart](https://en.wikipedia.org/wiki/Quart#US_liquid_quart) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :american-quart) (def cup "The [cup](https://en.wikipedia.org/wiki/Cup_(unit)) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :cup) (def imperial-fluid-ounce "The [Imperial fluid ounce](https://en.wikipedia.org/wiki/Fluid_ounce#Imperial_fluid_ounce) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :imperial-fluid-ounce) (def imperial-gallon "The [Imperial gallon](https://en.wikipedia.org/wiki/Gallon#Imperial_gallon) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :imperial-gallon) (def imperial-pint "The [Imperial pint](https://en.wikipedia.org/wiki/Pint#Imperial_pint) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :imperial-pint) (def imperial-quart "The [Imperial quart](https://en.wikipedia.org/wiki/Quart#Imperial_quart) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :imperial-quart) (def liter "The [liter](https://en.wikipedia.org/wiki/Liter) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: Both `litre` and `liter` are valid keys throughout brewtility to support regional spelling." :liter) @@ -201,7 +211,7 @@ (def litre "The [liter](https://en.wikipedia.org/wiki/Litre) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: Both `litre` and `liter` are valid keys throughout brewtility to support regional spelling." :litre) @@ -209,7 +219,7 @@ (def milliliter "The [milliliter](https://en.wikipedia.org/wiki/Milliliter) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: Both `millilitre` and `milliliter` are valid keys throughout brewtility to support regional spelling." :milliliter) @@ -217,7 +227,7 @@ (def millilitre "The [milliliter](https://en.wikipedia.org/wiki/Millilitre) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: Both `millilitre` and `milliliter` are valid keys throughout brewtility to support regional spelling." :millilitre) @@ -225,14 +235,14 @@ (def teaspoon "The [teaspoon](https://en.wikipedia.org/wiki/Teaspoon) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :teaspoon) (def tablespoon "The [tablespoon](https://en.wikipedia.org/wiki/Tablespoon) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :tablespoon) @@ -241,35 +251,35 @@ (def gram "The [gram](https://en.wikipedia.org/wiki/Gram) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :gram) (def kilogram "The [kilogram](https://en.wikipedia.org/wiki/Kilogram) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :kilogram) (def milligram "The [milligram](https://en.wikipedia.org/wiki/Milligram) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :milligram) (def ounce "The [ounce](https://en.wikipedia.org/wiki/Ounce#International_avoirdupois_ounce) unit of measure; specifically, the International avoirdupois ounce. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :ounce) (def pound "The [pound](https://en.wikipedia.org/wiki/Pound_(mass)) unit of measure - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :pound) @@ -278,7 +288,7 @@ (def celsius "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." :celsius) @@ -286,7 +296,7 @@ (def c "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." :c) @@ -294,7 +304,7 @@ (def centigrade "The [Celsius](https://en.wikipedia.org/wiki/Celsius) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `celsius`, `centigrade`, and `c` are all valid keys throughout brewtility to support multiple conventions." :centigrade) @@ -302,7 +312,7 @@ (def fahrenheit "The [Fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `fahrenheit` and `f` are all valid keys throughout brewtility to support multiple conventions." :fahrenheit) @@ -310,7 +320,7 @@ (def f "The [Fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `fahrenheit` and `f` are all valid keys throughout brewtility to support multiple conventions." :f) @@ -318,7 +328,7 @@ (def kelvin "The [Kelvin](https://en.wikipedia.org/wiki/Kelvin) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `kelvin` and `k` are all valid keys throughout brewtility to support multiple conventions." :kelvin) @@ -326,7 +336,7 @@ (def k "The [Kelvin](https://en.wikipedia.org/wiki/Kelvin) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions. + Commonly used with `brewtility.units` and in argument/option maps. Note: `kelvin` and `k` are all valid keys throughout brewtility to support multiple conventions." :k) @@ -336,54 +346,104 @@ (def day "The [day](https://en.wikipedia.org/wiki/Day) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :day) (def hour "The [hour](https://en.wikipedia.org/wiki/Hour) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :hour) (def microsecond "The [microsecond](https://en.wikipedia.org/wiki/Microsecond) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :microsecond) (def millisecond "The [millisecond](https://en.wikipedia.org/wiki/Millisecond) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :millisecond) (def minute "The [minute](https://en.wikipedia.org/wiki/Minute) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :minute) (def nanosecond "The [nanosecond](https://en.wikipedia.org/wiki/Nanosecond) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :nanosecond) (def second "The [second](https://en.wikipedia.org/wiki/Second) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :second) (def week "The [week](https://en.wikipedia.org/wiki/Week) unit of measure. - Commonly used with `brewtility.units` and in argument maps for enricher functions." + Commonly used with `brewtility.units` and in argument/option maps." :week) + +;; Pressure Units + +(def pascal + "The [Pascal](https://en.wikipedia.org/wiki/Pascal_(unit)) unit of measure. + + Commonly used with `brewtility.units.pressure` and in argument maps." + :pascal) + + +(def kilopascal + "The [Kilopascal](https://en.wikipedia.org/wiki/Kilopascal) unit of measure. + + Commonly used with `brewtility.units.pressure` and in argument maps." + :kilopascal) + + +(def bar + "The [Bar](https://en.wikipedia.org/wiki/Bar_(unit)) unit of measure. + + Commonly used with `brewtility.units.pressure` and in argument maps." + :bar) + + +(def atmosphere + "The [Atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit)) unit of measure. + Specifically, this is a [standard atmosphere](https://en.wikipedia.org/wiki/Standard_atmosphere). + + Commonly used with `brewtility.units.pressure` and in argument maps." + :atmosphere) + + +(def torr + "The [Torr](https://en.wikipedia.org/wiki/Torr_(unit)) unit of measure. + + Commonly used with `brewtility.units.pressure` and in argument maps." + :torr) + + +(def psi + "The [Pounds per square inch](https://en.wikipedia.org/wiki/Pound-force_per_square_inch) unit of measure. + + Commonly used with `brewtility.units.pressure` and in argument maps." + :psi) + +;; Specific Gravity Units + +(def ^:const specific-gravity + "The [Specific Gravity](https://en.wikipedia.org/wiki/Specific_gravity) unit of measure." + :specific-gravity) diff --git a/src/brewtility/units/pressure.cljc b/src/brewtility/units/pressure.cljc new file mode 100644 index 0000000..7e66966 --- /dev/null +++ b/src/brewtility/units/pressure.cljc @@ -0,0 +1,195 @@ +(ns brewtility.units.pressure + "A namespace for converting between different units of pressure. + + In the BeerXML spec, pressure is measured in kilopascals (kPa). + This namespace provides a way to convert between kilopascals and other units. + + Currently, brewtility supports the following types of pressure: + - [pascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) + - [kilopascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) + - [bar](https://en.wikipedia.org/wiki/Bar_(unit)) + - [atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit)) + - [torr](https://en.wikipedia.org/wiki/Torr) + - [psi](https://en.wikipedia.org/wiki/Pound-force_per_square_inch" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + +(def ^:const measurements + "The pressure measurements supported by brewtility." + #{opts/pascal + opts/kilopascal + opts/bar + opts/atmosphere + opts/torr + opts/psi}) + +(def ^:const measurements->display-name + "A map of pressure measurements to their display names." + {opts/pascal {opts/full "pascal" + opts/short "ka"} + opts/kilopascal {opts/full "kilopascals" + opts/short "kpa"} + opts/bar {opts/full "bar" + opts/short "bar"} + opts/atmosphere {opts/full "atmosphere" + opts/short "atm"} + opts/torr {opts/full "torr" + opts/short "torr"} + opts/psi {opts/full "pounds per square inch" + opts/short "psi"}}) + + +(defn- pascal->kilopascal + "An implementation to convert `pressure` from pascal to kilopascal." + {:no-doc true + :added "2.0"} + [pressure] + (/ pressure 1000.0)) + + +(defn- bar->kilopascal + "An implementation to convert `pressure` from bar to kilopascal." + {:no-doc true + :added "2.0"} + [pressure] + (* pressure 100.0)) + + +(defn- atmosphere->kilopascal + "An implementation to convert `pressure` from atmosphere to kilopascal." + {:no-doc true + :added "2.0"} + [pressure] + (* pressure 101.325)) + + +(defn- torr->kilopascal + "An implementation to convert `pressure` from torr to kilopascal." + {:no-doc true + :added "2.0"} + [pressure] + (* pressure 0.133322)) + + +(defn- psi->kilopascal + "An implementation to convert `pressure` from psi to kilopascal." + {:no-doc true + :added "2.0"} + [pressure] + (* pressure 6.894757)) + + +(defn- kilopascal->pascal + "An implementation to convert `pressure` from kilopascal to pascal." + {:no-doc true + :added "2.0"} + [pressure] + (* pressure 1000.0)) + + +(defn- kilopascal->bar + "An implementation to convert `pressure` from kilopascal to bar." + {:no-doc true + :added "2.0"} + [pressure] + (/ pressure 100.0)) + + +(defn- kilopascal->atmosphere + "An implementation to convert `pressure` from kilopascal to atmosphere." + {:no-doc true + :added "2.0"} + [pressure] + (/ pressure 101.325)) + + +(defn- kilopascal->torr + "An implementation to convert `pressure` from kilopascal to torr." + {:no-doc true + :added "2.0"} + [pressure] + (/ pressure 0.133322)) + + +(defn- kilopascal->psi + "An implementation to convert `pressure` from kilopascal to psi." + {:no-doc true + :added "2.0"} + [pressure] + (/ pressure 6.894757)) + + +(def ^:const measurement->kilopascal + "A map of pressure measurements to functions that convert to kilopascals." + {opts/pascal pascal->kilopascal + opts/kilopascal identity + opts/bar bar->kilopascal + opts/atmosphere atmosphere->kilopascal + opts/torr torr->kilopascal + opts/psi psi->kilopascal}) + + +(def ^:const kilopascal->measurement + "A map of pressure measurements to functions that convert from kilopascals." + {opts/pascal kilopascal->pascal + opts/kilopascal identity + opts/bar kilopascal->bar + opts/atmosphere kilopascal->atmosphere + opts/torr kilopascal->torr + opts/psi kilopascal->psi}) + + +(defn convert + "Given a `pressure` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[pressure-measurements]]. + This function will throw an exception if unsupported measurement values are passed." + {:changed "2.0"} + [pressure source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? pressure)) + (if (= source-measurement target-measurement) + pressure + (let [source->kilopascal-fn (measurement->kilopascal source-measurement) + kilopascal->target-fn (kilopascal->measurement target-measurement)] + (-> pressure source->kilopascal-fn kilopascal->target-fn))) + (throw (ex-info "Unsupported pressure conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :pressure pressure})))) + + +(defn display + "A function to render a human-readable `pressure` in `source-units`. + For example, + ```clj + (display 1.5 :pascal) ;; => \"1.5 pa\" + ``` + This function accepts an option map as an optional third argument. The following options are available: + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([pressure source-units] + (display pressure source-units {})) + ([pressure source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? pressure) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> pressure + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported pressure display options" + {:source-units source-units + :allowed-values measurements + :pressure pressure + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/src/brewtility/units/specific_gravity.cljc b/src/brewtility/units/specific_gravity.cljc new file mode 100644 index 0000000..4c3d62b --- /dev/null +++ b/src/brewtility/units/specific_gravity.cljc @@ -0,0 +1,88 @@ +(ns brewtility.units.specific-gravity + "A namespace for converting between different units of specific gravity. + + In the BeerXML spec, pecific gravity is measured in relative to the weight of the same size sample of water. + This namespace converts between that ration and other units. + + Currently, brewtility supports the following types of specific gravity: + - [specific-gravity](https://en.wikipedia.org/wiki/Specific_gravity)" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + +(def ^:const measurements + "The specific gravity systems available across brewtility." + #{opts/specific-gravity}) + + +(def ^:const measurements->display-name + "A map from specific gravity system names to their full and short unit names." + {opts/specific-gravity {opts/full "specific gravity" + opts/short "sg"}}) + + +(def ^:const measurement->specific-gravity + "A map from specific gravity system names to the conversion function to specific gravity." + {opts/specific-gravity identity}) + + +(def ^:const specific-gravity->measurement + "A map from specific gravity system names to the conversion function from specific gravity." + {opts/specific-gravity identity}) + + +(defn convert + "Given a `specific-gravity` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[specific-gravity-measurements]]. + This function will throw an exception if unsupported measurement values are passed." + {:changed "2.0"} + [gravity source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? gravity)) + (if (= source-measurement target-measurement) + gravity + (let [source->specific-gravity-fn (measurement->specific-gravity source-measurement) + specific-gravity->target-fn (specific-gravity->measurement target-measurement)] + (-> gravity source->specific-gravity-fn specific-gravity->target-fn))) + (throw (ex-info "Unsupported specific-gravity conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :specific-gravity gravity})))) + + +(defn display + "A function to render a human-readable `specific-gravity` in `source-units`. + + For example, + ```clj + (display 1.5 :pound) ;; => \"1.5 lb\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([gravity source-units] + (display gravity source-units {})) + ([gravity source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? gravity) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> gravity + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported specific-gravity display options" + {:source-units source-units + :allowed-values measurements + :specific-gravity gravity + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) From 4f913f7a845baf6f5cd6c78d8b4b967a3f321981 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sat, 3 Jun 2023 20:50:02 +0000 Subject: [PATCH 03/11] Add all conversion namespaces --- pom.xml | 2 +- src/brewtility/calculations.cljc | 28 ++-- src/brewtility/units.cljc | 1 + src/brewtility/units/options.cljc | 4 + src/brewtility/units/pressure.cljc | 4 +- src/brewtility/units/specific_gravity.cljc | 5 +- src/brewtility/units/temperature.cljc | 156 +++++++++++++++++++ src/brewtility/units/time.cljc | 131 ++++++++++++++++ src/brewtility/units/volume.cljc | 165 +++++++++++++++++++++ src/brewtility/units/weight.cljc | 115 ++++++++++++++ test/brewtility/calculations_test.cljc | 2 +- test/brewtility/color_test.cljc | 42 ------ test/brewtility/units_test.cljc | 28 ++-- 13 files changed, 609 insertions(+), 74 deletions(-) create mode 100644 src/brewtility/units/temperature.cljc create mode 100644 src/brewtility/units/time.cljc create mode 100644 src/brewtility/units/volume.cljc create mode 100644 src/brewtility/units/weight.cljc delete mode 100644 test/brewtility/color_test.cljc diff --git a/pom.xml b/pom.xml index 8176bf0..f336774 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ https://github.com/Wall-Brew-Co/brewtility scm:git:git://github.com/Wall-Brew-Co/brewtility.git scm:git:ssh://git@github.com/Wall-Brew-Co/brewtility.git - 47bfea691592555cd7be8f910c170d69591be94d + bb5fe4473e8cf506d517b623f1a56e6e95f3055b src diff --git a/src/brewtility/calculations.cljc b/src/brewtility/calculations.cljc index ebb0db2..7a2cf4a 100644 --- a/src/brewtility/calculations.cljc +++ b/src/brewtility/calculations.cljc @@ -2,9 +2,11 @@ "Namespace for handling recipe calculations. This namespace assumes ingredients that conform to the common-beer-format." {:added "1.0"} - (:require [brewtility.color :as color] - [brewtility.predicates.fermentables :as fermentables] - [brewtility.units :as units])) + (:require [brewtility.predicates.fermentables :as fermentables] + [brewtility.units.color :as color] + [brewtility.units.options :as opts] + [brewtility.units.volume :as volume] + [brewtility.units.weight :as weight])) (defn normalize-fermentable @@ -12,10 +14,10 @@ {:added "1.0"} [fermentable] (let [is-not-grain? (not (fermentables/grain? fermentable)) - kg->lbs (fn [w] (units/convert-weight w :kilogram :pound))] ; MCU is calculated against pounds + kg->lbs (fn [w] (weight/convert w opts/kilogram opts/pound))] ; MCU is calculated against pounds (cond-> fermentable true (update :amount kg->lbs) - is-not-grain? (update :color color/srm->lovibond)))) ; Grain color is in Lovibond, all other fermentables use SRM + is-not-grain? (update :color #(color/convert % opts/srm opts/lovibond))))) ; Grain color is in Lovibond, all other fermentables use SRM (defn calculate-malt-color-units "Given a collection of `common-beer-format` conforming `fermentables`, and a conformed `batch-size` in liters, return the overall Malt Color Units for a recipe." @@ -25,7 +27,7 @@ (let [normalized-fermentables (map normalize-fermentable fermentables) reducing-fn (fn [acc v] (+ acc (* (:amount v) (:color v)))) color (reduce reducing-fn 0 normalized-fermentables) - imperial-volume (units/convert-volume batch-size :litre :american-gallon)] + imperial-volume (volume/convert batch-size opts/litre opts/american-gallon)] (/ color imperial-volume))) @@ -48,7 +50,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - color/srm->ebc)) + (color/convert opts/srm opts/ebc))) (defn calculate-lovibond-color @@ -61,7 +63,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - color/srm->lovibond)) + (color/convert opts/srm opts/lovibond))) (defn calculate-rgba-color @@ -74,7 +76,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - color/srm->rgba)) + (color/convert opts/srm opts/rgba))) (defn potential-gravity->gravity-points @@ -82,7 +84,7 @@ {:added "1.0" :see-also ["gravity-points->potential-gravity"]} [potential-gravity weight] - (let [weight-in-pounds (units/convert-weight weight :kilogram :pound)] + (let [weight-in-pounds (weight/convert weight opts/kilogram opts/pound)] (-> potential-gravity (* 1000) (- 1000) @@ -94,7 +96,7 @@ {:added "1.0" :see-also ["potential-gravity->gravity-points"]} [gravity-points volume] - (let [volume-in-gallons (units/convert-volume volume :litre :american-gallon)] + (let [volume-in-gallons (volume/convert volume opts/litre opts/american-gallon)] (-> gravity-points (/ volume-in-gallons) (+ 1000) @@ -176,7 +178,7 @@ "Calculate the maximum amount of alpha acid released by `weight` ounce of a hop at `percent` alpha acid." {:added "1.0"} [weight alpha] - (let [weight-in-ounces (units/convert-weight weight :kilogram :ounce) + (let [weight-in-ounces (weight/convert weight opts/kilogram opts/ounce) aau-normalization-factor 100] (* aau-normalization-factor weight-in-ounces alpha))) @@ -187,7 +189,7 @@ [hop batch-size potential-gravity] (let [utilization (calculate-hop-utilization potential-gravity (:time hop)) alpha-acid-units (calculate-alpha-acid-units (:amount hop) (:alpha hop)) - imperial-volume (units/convert-volume batch-size :litre :american-gallon) + imperial-volume (volume/convert batch-size opts/litre opts/american-gallon) conversion-factor 74.89] (/ (* alpha-acid-units utilization conversion-factor) imperial-volume))) diff --git a/src/brewtility/units.cljc b/src/brewtility/units.cljc index 1648e01..385311e 100644 --- a/src/brewtility/units.cljc +++ b/src/brewtility/units.cljc @@ -28,6 +28,7 @@ [brewtility.units.volume :as volume] [brewtility.units.weight :as weight])) + (defn convert "Given a `measurement` in `source-units`, convert it to the `target-units` in a given `measurement-type`. diff --git a/src/brewtility/units/options.cljc b/src/brewtility/units/options.cljc index c100b95..11a4994 100644 --- a/src/brewtility/units/options.cljc +++ b/src/brewtility/units/options.cljc @@ -5,6 +5,7 @@ {:added "2.0"} (:refer-clojure :exclude [short second])) + ;; Defaults (def default-precision @@ -94,6 +95,7 @@ "A set of supported suffix types." #{short full}) + (def measurement-types "The measurement types available across brewtility." #{:color @@ -398,6 +400,7 @@ Commonly used with `brewtility.units` and in argument/option maps." :week) + ;; Pressure Units (def pascal @@ -442,6 +445,7 @@ Commonly used with `brewtility.units.pressure` and in argument maps." :psi) + ;; Specific Gravity Units (def ^:const specific-gravity diff --git a/src/brewtility/units/pressure.cljc b/src/brewtility/units/pressure.cljc index 7e66966..80a7109 100644 --- a/src/brewtility/units/pressure.cljc +++ b/src/brewtility/units/pressure.cljc @@ -1,5 +1,5 @@ (ns brewtility.units.pressure - "A namespace for converting between different units of pressure. + "A namespace for converting between different units of pressure. In the BeerXML spec, pressure is measured in kilopascals (kPa). This namespace provides a way to convert between kilopascals and other units. @@ -15,6 +15,7 @@ (:require [brewtility.precision :as precision] [brewtility.units.options :as opts])) + (def ^:const measurements "The pressure measurements supported by brewtility." #{opts/pascal @@ -24,6 +25,7 @@ opts/torr opts/psi}) + (def ^:const measurements->display-name "A map of pressure measurements to their display names." {opts/pascal {opts/full "pascal" diff --git a/src/brewtility/units/specific_gravity.cljc b/src/brewtility/units/specific_gravity.cljc index 4c3d62b..966e399 100644 --- a/src/brewtility/units/specific_gravity.cljc +++ b/src/brewtility/units/specific_gravity.cljc @@ -1,8 +1,8 @@ (ns brewtility.units.specific-gravity "A namespace for converting between different units of specific gravity. - In the BeerXML spec, pecific gravity is measured in relative to the weight of the same size sample of water. - This namespace converts between that ration and other units. + In the BeerXML spec, specific gravity is measured in relative to the weight of the same size sample of water. + This namespace converts between that ratio and other units. Currently, brewtility supports the following types of specific gravity: - [specific-gravity](https://en.wikipedia.org/wiki/Specific_gravity)" @@ -10,6 +10,7 @@ (:require [brewtility.precision :as precision] [brewtility.units.options :as opts])) + (def ^:const measurements "The specific gravity systems available across brewtility." #{opts/specific-gravity}) diff --git a/src/brewtility/units/temperature.cljc b/src/brewtility/units/temperature.cljc new file mode 100644 index 0000000..2f752c1 --- /dev/null +++ b/src/brewtility/units/temperature.cljc @@ -0,0 +1,156 @@ +(ns brewtility.units.temperature + "A namespace for converting between different units of temperature + + In the BeerXML spec, temperature is measured in degrees Celsius. + This namespace converts between that and other units. + + Currently, brewtility supports the following types of temperature measurements: + - [clesius](https://en.wikipedia.org/wiki/Celsius) + - [fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) + - [kelvin](https://en.wikipedia.org/wiki/Kelvin_(unit))" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + + +(def ^:const measurements + "The temperature measurements supported by brewtility." + #{opts/c + opts/celsius + opts/centigrade + opts/f + opts/fahrenheit + opts/k + opts/kelvin}) + + +(def ^:const measurements->display-name + "The temperature measurements supported by brewtility." + {opts/c {opts/full "celsius" + opts/short "c"} + opts/celsius {opts/full "celsius" + opts/short "c"} + opts/centigrade {opts/full "centigrade" + opts/short "mg"} + opts/f {opts/full "fahrenheit" + opts/short "f"} + opts/fahrenheit {opts/full "fahrenheit" + opts/short "f"} + opts/k {opts/full "kelvin" + opts/short "k"} + opts/kelvin {opts/full "kelvin" + opts/short "k"}}) + + +(defn- celsius->fahrenheit + "An implementation function to convert `temp` from celsius to fahrenheit" + {:no-doc true + :added "2.0"} + [temp] + (+ 32 (* 1.8 temp))) + + +(defn- celsius->kelvin + "An implementation function to convert `temp` from celsius to kelvin" + {:no-doc true + :added "2.0"} + [temp] + (+ temp 273.15)) + + +(defn- fahrenheit->celsius + "An implementation function to convert `temp` from fahrenheit to celsius" + {:no-doc true + :added "2.0"} + [temp] + (/ (* (- temp 32) 5) 9.0)) + + +(defn- kelvin->celsius + "An implementation function to convert `temp` from kelvin to celsius" + {:no-doc true + :added "2.0"} + [temp] + (- temp 273.15)) + + +(def ^:const measurement->celsius + "A map from measurement names to the implementation function which converts them to degrees celsius" + {opts/c identity + opts/celsius identity + opts/centigrade identity + opts/f fahrenheit->celsius + opts/fahrenheit fahrenheit->celsius + opts/k kelvin->celsius + opts/kelvin kelvin->celsius}) + + +(def ^:const celsius->measurement + "A map from measurement names to the implementation function which converts them from degrees celsius" + {opts/celsius identity + opts/c identity + opts/centigrade identity + opts/fahrenheit celsius->fahrenheit + opts/f celsius->fahrenheit + opts/kelvin celsius->kelvin + opts/k celsius->kelvin}) + + +(defn convert + "Given a `temperature` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[temperature-measurements]]. + + This function will throw an exception if unsupported measurement values are passed." + {:changed "2.0"} + [temperature source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? temperature)) + (if (= source-measurement target-measurement) + temperature + (let [source->celsius-fn (measurement->celsius source-measurement) + celsius->target-fn (celsius->measurement target-measurement)] + (-> temperature source->celsius-fn celsius->target-fn))) + (throw (ex-info "Unsupported temperature conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :temperature temperature})))) + + +(defn display + "A function to render a human-readable `temperature` in `source-units`. + + For example, + + ```clj + (display 1.5 :pound) ;; => \"1.5 lb\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([temperature source-units] + (display temperature source-units {})) + ([temperature source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? temperature) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> temperature + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported temperature display options" + {:source-units source-units + :allowed-values measurements + :temperature temperature + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/src/brewtility/units/time.cljc b/src/brewtility/units/time.cljc new file mode 100644 index 0000000..74c8006 --- /dev/null +++ b/src/brewtility/units/time.cljc @@ -0,0 +1,131 @@ +(ns brewtility.units.time + "A namespace for converting between different units of time. + + In the BeerXML spec, temperature is measured in minutes. + This namespace converts between that and other units. + + Currently, brewtility supports the following types of time measurements: + - [microsecond](https://en.wikipedia.org/wiki/Microsecond) + - [nanosecond](https://en.wikipedia.org/wiki/Nanosecond) + - [millisecond](https://en.wikipedia.org/wiki/Millisecond) + - [second](https://en.wikipedia.org/wiki/Second) + - [minute](https://en.wikipedia.org/wiki/Minute) + - [hour](https://en.wikipedia.org/wiki/Hour) + - [day](https://en.wikipedia.org/wiki/Day) + - [week](https://en.wikipedia.org/wiki/Week)" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts]) + (:refer-clojure :exclude [time])) + + +(def ^:const measurements + "The time measurements available across brewtility" + #{opts/day + opts/hour + opts/microsecond + opts/millisecond + opts/minute + opts/nanosecond + opts/second + opts/week}) + + +(def ^:const measurement->minute + "A map from measurement names to doubles representing their fractional value to one minute" + {opts/day 1440.0 + opts/hour 60.0 + opts/microsecond (/ 1.0 60000000) + opts/millisecond (/ 1.0 60000) + opts/minute 1.0 + opts/nanosecond (/ 1.0 60000000000) + opts/second (/ 1.0 60) + opts/week 10080.0}) + + +(def ^:const measurements->display-name + "A map from measurement names to their full name and abbreviation" + {opts/day {opts/full "days" + opts/short "d"} + opts/hour {opts/full "hour" + opts/short "hr"} + opts/microsecond {opts/full "microsecond" + opts/short "µs"} + opts/millisecond {opts/full "millisecond" + opts/short "ims"} + opts/minute {opts/full "minute" + opts/short "m"} + opts/nanosecond {opts/full "nanosecond" + opts/short "ns"} + opts/second {opts/full "second" + opts/short "s"} + opts/week {opts/full "week" + opts/short "w"}}) + + +(defn- minute->measurement + "An implementation function to convert minutes to another unit of time" + {:no-doc true + :added "2.0"} + [measurement-name] + (/ 1.0 (get measurement->minute measurement-name))) + + +(defn convert + "Given a `time-val` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[time-measurements]]. + + This function will throw an exception if unsupported measurement values are passed." + {:added "2.0"} + [time-val source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? time-val)) + (if (= source-measurement target-measurement) + time-val + (let [source->litre-multiplier (measurement->minute source-measurement) + litre->target-multiplier (minute->measurement target-measurement)] + (* time-val source->litre-multiplier litre->target-multiplier))) + (throw (ex-info "Unsupported time conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :time time-val})))) + + +(defn display + "A function to render a human-readable `time` in `source-units`. + + For example, + + ```clj + (display 1.5 :second) ;; => \"1.5 s\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([time source-units] + (display time source-units {})) + ([time source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? time) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> time + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported time display units" + {:source-units source-units + :allowed-values measurements + :time time + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/src/brewtility/units/volume.cljc b/src/brewtility/units/volume.cljc new file mode 100644 index 0000000..9f18100 --- /dev/null +++ b/src/brewtility/units/volume.cljc @@ -0,0 +1,165 @@ +(ns brewtility.units.volume + "A namespace for converting between different units of volume. + + In the BeerXML spec, volume is measured in liters. + This namespace provides a way to convert between liters and other units. + + Currently, brewtility supports the following types of volume: + - [american-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) + - [american-gallon](https://en.wikipedia.org/wiki/Gallon) + - [american-pint](https://en.wikipedia.org/wiki/Pint) + - [american-quart](https://en.wikipedia.org/wiki/Quart) + - [cup](https://en.wikipedia.org/wiki/Cup_(unit)) + - [imperial-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) + - [imperial-gallon](https://en.wikipedia.org/wiki/Gallon) + - [imperial-pint](https://en.wikipedia.org/wiki/Pint) + - [imperial-quart](https://en.wikipedia.org/wiki/Quart) + - [liter](https://en.wikipedia.org/wiki/Litre) + - [litre](https://en.wikipedia.org/wiki/Litre) + - [milliliter](https://en.wikipedia.org/wiki/Millilitre) + - [millilitre](https://en.wikipedia.org/wiki/Millilitre) + - [tablespoon](https://en.wikipedia.org/wiki/Tablespoon) + - [teaspoon](https://en.wikipedia.org/wiki/Teaspoon))" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + + +(def ^:const measurements + "The volume measurements available across brewtility" + #{opts/american-gallon + opts/american-pint + opts/american-quart + opts/cup + opts/imperial-gallon + opts/imperial-pint + opts/imperial-quart + opts/liter + opts/litre + opts/milliliter + opts/millilitre + opts/tablespoon + opts/teaspoon + opts/american-fluid-ounce + opts/imperial-fluid-ounce}) + + +(def ^:const measurement->litre + "A map from measurement names to doubles representing their fractional value to one liter" + {opts/american-fluid-ounce 0.0295735 + opts/american-gallon 3.78541 + opts/american-pint 0.473176 + opts/american-quart 0.946353 + opts/cup 0.236588 + opts/imperial-fluid-ounce 0.0284131 + opts/imperial-gallon 4.54609 + opts/imperial-pint 0.568261 + opts/imperial-quart 1.13652 + opts/liter 1.0 + opts/litre 1.0 + opts/milliliter 0.001 + opts/millilitre 0.001 + opts/tablespoon 0.0147868 + opts/teaspoon 0.00492892}) + + +(def ^:const measurements->display-name + "A map from measurement names to their full name and abbreviation" + {opts/american-fluid-ounce {opts/full "fluid ounce" + opts/short "fl oz"} + opts/american-gallon {opts/full "US gallon" + opts/short "gal"} + opts/american-pint {opts/full "american pint" + opts/short "pt"} + opts/american-quart {opts/full "american quart" + opts/short "qt"} + opts/cup {opts/full "cup" + opts/short "c"} + opts/imperial-fluid-ounce {opts/full "imperial fluid ounce" + opts/short "imp fl oz"} + opts/imperial-gallon {opts/full "imperial gallon" + opts/short "gal"} + opts/imperial-pint {opts/full "imperial pint" + opts/short "pt"} + opts/imperial-quart {opts/full "imperial quart" + opts/short "qt"} + opts/liter {opts/full "liter" + opts/short "l"} + opts/litre {opts/full "litre" + opts/short "l"} + opts/milliliter {opts/full "milliliter" + opts/short "ml"} + opts/millilitre {opts/full "millilitre" + opts/short "ml"} + opts/tablespoon {opts/full "tablespoon" + opts/short "tbsp"} + opts/teaspoon {opts/full "teaspoon" + opts/short "tsp"}}) + + +(defn- liter->measurement + "An implementation function to convert liters to another unit of volume" + {:no-doc true + :added "2.0"} + [measurement-name] + (/ 1.0 (get measurement->litre measurement-name))) + + +(defn convert + "Given a `volume` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[measurements]]. + + This function will throw an exception if unsupported measurement values are passed." + {:changed "2.0"} + [volume source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? volume)) + (if (= source-measurement target-measurement) + volume + (let [source->litre-multiplier (measurement->litre source-measurement) + litre->target-multiplier (liter->measurement target-measurement)] + (* volume source->litre-multiplier litre->target-multiplier))) + (throw (ex-info "Unsupported volume conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :volume volume})))) + + +(defn display + "A function to render a human-readable `volume` in `source-units`. + + For example, + + ```clj + (display 1.5 :litre) ;; => \"1.5 l\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([volume source-units] + (display volume source-units {})) + ([volume source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? volume) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> volume + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported volume display units" + {:source-units source-units + :allowed-values measurements + :volume volume + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/src/brewtility/units/weight.cljc b/src/brewtility/units/weight.cljc new file mode 100644 index 0000000..bb6899c --- /dev/null +++ b/src/brewtility/units/weight.cljc @@ -0,0 +1,115 @@ +(ns brewtility.units.weight + "A namespace for converting between different units of weight. + + By the BeerXML spec, weight is measured in kilograms. + This namespace provides a way to convert between kilograms and other units of weight. + + Currently, brewtility supports the following types of weight: + - [gram](https://en.wikipedia.org/wiki/Gram) + - [milligram](https://en.wikipedia.org/wiki/Milligram) + - [kilogram](https://en.wikipedia.org/wiki/Kilogram) + - [ounce](https://en.wikipedia.org/wiki/Ounce) + - [pound](https://en.wikipedia.org/wiki/Pound_(mass))" + {:added "2.0"} + (:require [brewtility.precision :as precision] + [brewtility.units.options :as opts])) + + +(def ^:const measurements + "The weight measurements available across brewtility" + #{opts/gram + opts/kilogram + opts/milligram + opts/ounce + opts/pound}) + + +(def ^:const measurement->kilogram + "A map from measurement names to doubles representing their fractional value to one kilogram" + {opts/gram 0.001 + opts/kilogram 1.0 + opts/milligram 0.000001 + opts/ounce 0.02834952 + opts/pound 0.45359237}) + + +(def ^:const measurements->display-name + "A map from measurement names to their full name and abbreviation" + {opts/gram {opts/full "gram" + opts/short "g"} + opts/kilogram {opts/full "kilogram" + opts/short "kg"} + opts/milligram {opts/full "milligram" + opts/short "mg"} + opts/ounce {opts/full "ounce" + opts/short "oz"} + opts/pound {opts/full "pound" + opts/short "lb"}}) + + +(defn- kilogram->measurement + "An implementation function to convert kilograms to another unit of weight." + {:no-doc true + :added "2.0"} + [measurement-name] + (/ 1.0 (get measurement->kilogram measurement-name))) + + +(defn convert + "Given a `weight` in `source-measurement`, convert it to the `target-measurement`. + Supported values for `source-measurement` and `target-measurement` are enumerated in [[weight-measurements]]. + + This function will throw an exception if unsupported measurement values are passed." + {:added "2.0"} + [weight source-measurement target-measurement] + (if (and (contains? measurements source-measurement) + (contains? measurements target-measurement) + (number? weight)) + (if (= source-measurement target-measurement) + weight + (let [source->kilogram-multiplier (measurement->kilogram source-measurement) + kilogram->target-multiplier (kilogram->measurement target-measurement)] + (* weight source->kilogram-multiplier kilogram->target-multiplier))) + (throw (ex-info "Unsupported weight conversion units" + {:source-measurement source-measurement + :target-measurement target-measurement + :allowed-values measurements + :weight weight})))) + + +(defn display + "A function to render a human-readable `weight` in `source-units`. + + For example, + + ```clj + (display 1.5 :pound) ;; => \"1.5 lb\" + ``` + + This function accepts an option map as an optional third argument. The following options are available: + + - `precision`: The number of decimal places to round to. Defaults to 3. + - `suffix`: The suffix type to append to the displayable fields. Defaults to `:short`. Acceptable values are: + - `:short`: A customary abbreviation for the selected unit. For example, `\"gal\"` for `\" US gallons\"`. + - `:full`: The full name of the selected unit. For example, `\"teaspoon\"` for `\"teaspoon\"`." + {:added "2.0"} + ([weight source-units] + (display weight source-units {})) + ([weight source-units {:keys [precision suffix] + :or {precision opts/default-precision + suffix opts/short}}] + (if (and (contains? measurements source-units) + (number? weight) + (integer? precision) + (contains? opts/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] + (-> weight + (precision/->precision precision) + (str " " display-name))) + (throw (ex-info "Unsupported weight display options" + {:source-units source-units + :allowed-values measurements + :weight weight + :precision precision + :suffix suffix + :allowed-suffixes opts/supported-suffixes}))))) diff --git a/test/brewtility/calculations_test.cljc b/test/brewtility/calculations_test.cljc index 94fb4c9..12b7eac 100644 --- a/test/brewtility/calculations_test.cljc +++ b/test/brewtility/calculations_test.cljc @@ -2,7 +2,7 @@ (:require #? (:clj [clojure.test :refer [deftest is testing]]) #? (:cljs [cljs.test :refer-macros [deftest is testing]]) [brewtility.calculations :as sut] - [brewtility.color :as color] + [brewtility.units.color :as color] [brewtility.precision :as bp] [common-beer-data.fermentables.adjuncts :as adjuncts] [common-beer-data.fermentables.grains :as grains] diff --git a/test/brewtility/color_test.cljc b/test/brewtility/color_test.cljc deleted file mode 100644 index c4f405b..0000000 --- a/test/brewtility/color_test.cljc +++ /dev/null @@ -1,42 +0,0 @@ -(ns brewtility.color-test - (:require #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]) - [brewtility.color :as sut] - [brewtility.precision :as bp])) - - -(deftest srm->rgba-test - (testing "SRM color lookup behaves as expected" - (is (= sut/srm-13 (sut/srm->rgba 13))) - (is (= sut/srm-1 (sut/srm->rgba 0))) - (is (= sut/srm-1 (sut/srm->rgba -1))) - (is (= sut/srm-6 (sut/srm->rgba 6.2))) - (is (= sut/srm-6 (sut/srm->rgba 6.8))) - (is (= sut/srm-40 (sut/srm->rgba 41))))) - - -(deftest lovibond->rgba-test - (testing "lovibond -> SRM -> rgba lookup behaves as expected" - (is (= sut/srm-13 (sut/lovibond->rgba 10.16))) - (is (= sut/srm-1 (sut/lovibond->rgba 0.56))) - (is (= sut/srm-1 (sut/lovibond->rgba -0.18))) - (is (= sut/srm-6 (sut/lovibond->rgba 5.14))) - (is (= sut/srm-6 (sut/lovibond->rgba 5.58))) - (is (= sut/srm-40 (sut/lovibond->rgba 30.83))))) - - -(deftest ebc->rgba-test - (testing "EBC -> SRM -> rgba lookup behaves as expected" - (is (= sut/srm-13 (sut/ebc->rgba 25.61))) - (is (= sut/srm-1 (sut/ebc->rgba 0))) - (is (= sut/srm-1 (sut/ebc->rgba -1.97))) - (is (= sut/srm-6 (sut/ebc->rgba 12.21))) - (is (= sut/srm-6 (sut/ebc->rgba 13.40))) - (is (= sut/srm-40 (sut/ebc->rgba 80.77))))) - - -(deftest conversion-test - (testing "Ensure various color unit conversions behave as expected" - (is (= 10.0 (bp/->2dp (sut/lovibond->srm 7.94)) (bp/->2dp (sut/ebc->srm 19.69)))) - (is (= 23.2 (bp/->2dp (sut/srm->lovibond 30.66)) (bp/->2dp (sut/ebc->lovibond 60.38)))) - (is (= -45.53 (bp/->2dp (sut/srm->ebc -23.11)) (bp/->2dp (sut/lovibond->ebc -16.5)))))) diff --git a/test/brewtility/units_test.cljc b/test/brewtility/units_test.cljc index 85d1c1f..17dfcc2 100644 --- a/test/brewtility/units_test.cljc +++ b/test/brewtility/units_test.cljc @@ -7,25 +7,25 @@ (deftest convert-volume-test (testing "Volume conversions should behave as expected" - (is (= 1.0 (sut/convert-volume 1.0 :liter :litre))) - (is (= 1.0 (sut/convert-volume 1.0 :litre :litre))) - (is (= 0.099 (bp/->3dp (sut/convert-volume 20 :teaspoon :liter)))) - (is (= 35.1 (bp/->3dp (sut/convert-volume 35.1 :tablespoon :tablespoon)))) - (is (= 12.0 (bp/->3dp (sut/convert-volume 9.99209 :imperial-pint :american-pint)))))) + (is (= 1.0 (sut/convert :volume 1.0 :liter :litre))) + (is (= 1.0 (sut/convert :volume 1.0 :litre :litre))) + (is (= 0.099 (bp/->3dp (sut/convert :volume 20 :teaspoon :liter)))) + (is (= 35.1 (bp/->3dp (sut/convert :volume 35.1 :tablespoon :tablespoon)))) + (is (= 12.0 (bp/->3dp (sut/convert :volume 9.99209 :imperial-pint :american-pint)))))) (deftest convert-weight-test (testing "Weight conversions should behave as expected" - (is (= 1.0 (sut/convert-weight 1.0 :kilogram :kilogram))) - (is (= 1.0 (sut/convert-weight 1.0 :pound :pound))) - (is (= 240.0 (bp/->3dp (sut/convert-weight 15.0 :pound :ounce)))) - (is (= 0.003 (bp/->3dp (sut/convert-weight 1205.5 :milligram :pound)))))) + (is (= 1.0 (sut/convert :weight 1.0 :kilogram :kilogram))) + (is (= 1.0 (sut/convert :weight 1.0 :pound :pound))) + (is (= 240.0 (bp/->3dp (sut/convert :weight 15.0 :pound :ounce)))) + (is (= 0.003 (bp/->3dp (sut/convert :weight 1205.5 :milligram :pound)))))) (deftest convert-temperature-test (testing "Temperature conversions should behave as expected" - (is (= 32.0 (sut/convert-temperature 32.0 :fahrenheit :fahrenheit))) - (is (= 32.0 (sut/convert-temperature 0.0 :celsius :fahrenheit))) - (is (= 340.706 (bp/->3dp (sut/convert-temperature 153.6008 :fahrenheit :kelvin)))) - (is (= 1743.15 (bp/->3dp (sut/convert-temperature 2016.3 :kelvin :c)))) - (is (= -40.0 (bp/->3dp (sut/convert-temperature -40.0 :f :c)))))) + (is (= 32.0 (sut/convert :temperature 32.0 :fahrenheit :fahrenheit))) + (is (= 32.0 (sut/convert :temperature 0.0 :celsius :fahrenheit))) + (is (= 340.706 (bp/->3dp (sut/convert :temperature 153.6008 :fahrenheit :kelvin)))) + (is (= 1743.15 (bp/->3dp (sut/convert :temperature 2016.3 :kelvin :c)))) + (is (= -40.0 (bp/->3dp (sut/convert :temperature -40.0 :f :c)))))) From 6b528618c2787274ef1a69c69c015e24e401339e Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 14:45:27 +0000 Subject: [PATCH 04/11] Add structural tests --- src/brewtility/calculations.cljc | 25 ++-- src/brewtility/units.cljc | 8 +- src/brewtility/units/color.cljc | 77 ++++++----- src/brewtility/units/pressure.cljc | 70 +++++----- src/brewtility/units/specific_gravity.cljc | 20 +-- src/brewtility/units/temperature.cljc | 80 +++++------ src/brewtility/units/time.cljc | 74 +++++----- src/brewtility/units/volume.cljc | 130 +++++++++--------- src/brewtility/units/weight.cljc | 50 +++---- test/brewtility/calculations_test.cljc | 6 +- test/brewtility/precision_test.cljc | 4 +- test/brewtility/units/color_test.cljc | 59 +++++++- test/brewtility/units/pressure_test.cljc | 67 +++++++++ .../units/specific_gravity_test.cljc | 55 ++++++++ test/brewtility/units_test.cljc | 51 ++++--- test/brewtility/wrapping_test.cljc | 4 +- 16 files changed, 487 insertions(+), 293 deletions(-) create mode 100644 test/brewtility/units/pressure_test.cljc create mode 100644 test/brewtility/units/specific_gravity_test.cljc diff --git a/src/brewtility/calculations.cljc b/src/brewtility/calculations.cljc index 7a2cf4a..14e1259 100644 --- a/src/brewtility/calculations.cljc +++ b/src/brewtility/calculations.cljc @@ -4,7 +4,8 @@ {:added "1.0"} (:require [brewtility.predicates.fermentables :as fermentables] [brewtility.units.color :as color] - [brewtility.units.options :as opts] + [brewtility.units.options :as options] + [brewtility.units.time :as time] [brewtility.units.volume :as volume] [brewtility.units.weight :as weight])) @@ -14,10 +15,10 @@ {:added "1.0"} [fermentable] (let [is-not-grain? (not (fermentables/grain? fermentable)) - kg->lbs (fn [w] (weight/convert w opts/kilogram opts/pound))] ; MCU is calculated against pounds + kg->lbs (fn [w] (weight/convert w options/kilogram options/pound))] ; MCU is calculated against pounds (cond-> fermentable true (update :amount kg->lbs) - is-not-grain? (update :color #(color/convert % opts/srm opts/lovibond))))) ; Grain color is in Lovibond, all other fermentables use SRM + is-not-grain? (update :color #(color/convert % options/srm options/lovibond))))) ; Grain color is in Lovibond, all other fermentables use SRM (defn calculate-malt-color-units "Given a collection of `common-beer-format` conforming `fermentables`, and a conformed `batch-size` in liters, return the overall Malt Color Units for a recipe." @@ -27,7 +28,7 @@ (let [normalized-fermentables (map normalize-fermentable fermentables) reducing-fn (fn [acc v] (+ acc (* (:amount v) (:color v)))) color (reduce reducing-fn 0 normalized-fermentables) - imperial-volume (volume/convert batch-size opts/litre opts/american-gallon)] + imperial-volume (volume/convert batch-size options/litre options/american-gallon)] (/ color imperial-volume))) @@ -50,7 +51,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - (color/convert opts/srm opts/ebc))) + (color/convert options/srm options/ebc))) (defn calculate-lovibond-color @@ -63,7 +64,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - (color/convert opts/srm opts/lovibond))) + (color/convert options/srm options/lovibond))) (defn calculate-rgba-color @@ -76,7 +77,7 @@ [fermentables batch-size] (-> fermentables (calculate-srm-color batch-size) - (color/convert opts/srm opts/rgba))) + (color/convert options/srm options/rgba))) (defn potential-gravity->gravity-points @@ -84,7 +85,7 @@ {:added "1.0" :see-also ["gravity-points->potential-gravity"]} [potential-gravity weight] - (let [weight-in-pounds (weight/convert weight opts/kilogram opts/pound)] + (let [weight-in-pounds (weight/convert weight options/kilogram options/pound)] (-> potential-gravity (* 1000) (- 1000) @@ -96,7 +97,7 @@ {:added "1.0" :see-also ["potential-gravity->gravity-points"]} [gravity-points volume] - (let [volume-in-gallons (volume/convert volume opts/litre opts/american-gallon)] + (let [volume-in-gallons (volume/convert volume options/litre options/american-gallon)] (-> gravity-points (/ volume-in-gallons) (+ 1000) @@ -178,7 +179,7 @@ "Calculate the maximum amount of alpha acid released by `weight` ounce of a hop at `percent` alpha acid." {:added "1.0"} [weight alpha] - (let [weight-in-ounces (weight/convert weight opts/kilogram opts/ounce) + (let [weight-in-ounces (weight/convert weight options/kilogram options/ounce) aau-normalization-factor 100] (* aau-normalization-factor weight-in-ounces alpha))) @@ -189,7 +190,7 @@ [hop batch-size potential-gravity] (let [utilization (calculate-hop-utilization potential-gravity (:time hop)) alpha-acid-units (calculate-alpha-acid-units (:amount hop) (:alpha hop)) - imperial-volume (volume/convert batch-size opts/litre opts/american-gallon) + imperial-volume (volume/convert batch-size options/litre options/american-gallon) conversion-factor 74.89] (/ (* alpha-acid-units utilization conversion-factor) imperial-volume))) @@ -209,7 +210,7 @@ [{:keys [batch-size top-up-water trub-chiller-loss boil-time evap-rate]}] (if (every? number? [batch-size top-up-water trub-chiller-loss boil-time evap-rate]) (let [starting-water (- batch-size top-up-water trub-chiller-loss) - boil-time-in-hours (/ boil-time 60.0) + boil-time-in-hours (time/convert boil-time options/minute options/hour) evaporation-decimal (/ evap-rate 100.0) evaporated-water (+ 1 (* boil-time-in-hours evaporation-decimal))] (* starting-water evaporated-water)) diff --git a/src/brewtility/units.cljc b/src/brewtility/units.cljc index 385311e..a334fa0 100644 --- a/src/brewtility/units.cljc +++ b/src/brewtility/units.cljc @@ -20,7 +20,7 @@ {:added "1.0" :changed "2.0"} (:require [brewtility.units.color :as color] - [brewtility.units.options :as opts] + [brewtility.units.options :as options] [brewtility.units.pressure :as pressure] [brewtility.units.specific-gravity :as specific-gravity] [brewtility.units.temperature :as temperature] @@ -59,7 +59,7 @@ :weight (weight/convert measurement source-units target-units) :else (throw (ex-info "Unsupported unit system" {:measurement-type measurement-type - :allowed-values opts/measurement-types + :allowed-values options/measurement-types :measurement measurement})))) @@ -87,7 +87,7 @@ (display measurement source-units measurement-type {})) ([measurement-type measurement source-units opts] - (let [options (merge {opts/precision opts/default-precision opts/suffix opts/short} opts)] + (let [options (merge {options/precision options/default-precision options/suffix options/short} opts)] (case measurement-type :color (color/display measurement source-units options) :pressure (pressure/display measurement source-units options) @@ -98,5 +98,5 @@ :weight (weight/display measurement source-units options) :else (throw (ex-info "Unsupported unit system" {:measurement-type measurement-type - :allowed-values opts/measurement-types + :allowed-values options/measurement-types :measurement measurement})))))) diff --git a/src/brewtility/units/color.cljc b/src/brewtility/units/color.cljc index e85e83c..0c50262 100644 --- a/src/brewtility/units/color.cljc +++ b/src/brewtility/units/color.cljc @@ -11,27 +11,27 @@ - [RGBa](https://en.wikipedia.org/wiki/RGBA_color_model)" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) -(def measurements +(def ^:const measurements "The color systems available across brewtility." - #{opts/srm - opts/ebc - opts/lovibond - opts/rgba}) + #{options/srm + options/ebc + options/lovibond + options/rgba}) -(def measurements->display-name +(def ^:const measurements->display-name "A map from color system names to their full and short unit names" - {opts/srm {opts/full "standard reference method" - opts/short "srm"} - opts/ebc {opts/full "ebc" - opts/short "ebc"} - opts/lovibond {opts/full "degrees lovibond" - opts/short "°L"} - opts/rgba {opts/full "" - opts/short ""}}) + {options/srm {options/full "standard reference method" + options/short "srm"} + options/ebc {options/full "ebc" + options/short "ebc"} + options/lovibond {options/full "degrees lovibond" + options/short "°L"} + options/rgba {options/full "" + options/short ""}}) ;; @@ -326,17 +326,17 @@ "A map from color systems to the implementation function that converts to SRM. Note: RGBa is not included because it is not a color system, but a color representation." - {opts/ebc ebc->srm - opts/lovibond lovibond->srm - opts/srm identity}) + {options/ebc ebc->srm + options/lovibond lovibond->srm + options/srm identity}) (def srm->measurement "A map from color systems to the implementation function that converts from SRM" - {opts/ebc srm->ebc - opts/lovibond srm->lovibond - opts/srm identity - opts/rgba srm->rgba}) + {options/ebc srm->ebc + options/lovibond srm->lovibond + options/srm identity + options/rgba srm->rgba}) (defn convert @@ -380,22 +380,23 @@ ([color source-units] (display color source-units {})) ([color source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] - (if (and (contains? measurements source-units) - (number? color) - (integer? precision) - (contains? opts/supported-suffixes suffix)) - (if (= source-units :rgba) - color + :or {precision options/default-precision + suffix options/short}}] + (if (= source-units :rgba) + color + (if (and (contains? measurements source-units) + (number? color) + (integer? precision) + (contains? options/supported-suffixes suffix)) + (let [display-name (get-in measurements->display-name [source-units suffix])] (-> color (precision/->precision precision) - (str " " display-name)))) - (throw (ex-info "Unsupported color display options" - {:source-units source-units - :allowed-values measurements - :color color - :precision precision - :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + (str " " display-name))) + (throw (ex-info "Unsupported color display options" + {:source-units source-units + :allowed-values measurements + :color color + :precision precision + :suffix suffix + :allowed-suffixes options/supported-suffixes})))))) diff --git a/src/brewtility/units/pressure.cljc b/src/brewtility/units/pressure.cljc index 80a7109..26efaba 100644 --- a/src/brewtility/units/pressure.cljc +++ b/src/brewtility/units/pressure.cljc @@ -13,33 +13,33 @@ - [psi](https://en.wikipedia.org/wiki/Pound-force_per_square_inch" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) (def ^:const measurements "The pressure measurements supported by brewtility." - #{opts/pascal - opts/kilopascal - opts/bar - opts/atmosphere - opts/torr - opts/psi}) + #{options/pascal + options/kilopascal + options/bar + options/atmosphere + options/torr + options/psi}) (def ^:const measurements->display-name "A map of pressure measurements to their display names." - {opts/pascal {opts/full "pascal" - opts/short "ka"} - opts/kilopascal {opts/full "kilopascals" - opts/short "kpa"} - opts/bar {opts/full "bar" - opts/short "bar"} - opts/atmosphere {opts/full "atmosphere" - opts/short "atm"} - opts/torr {opts/full "torr" - opts/short "torr"} - opts/psi {opts/full "pounds per square inch" - opts/short "psi"}}) + {options/pascal {options/full "pascal" + options/short "pa"} + options/kilopascal {options/full "kilopascals" + options/short "kpa"} + options/bar {options/full "bar" + options/short "bar"} + options/atmosphere {options/full "atmosphere" + options/short "atm"} + options/torr {options/full "torr" + options/short "torr"} + options/psi {options/full "pounds per square inch" + options/short "psi"}}) (defn- pascal->kilopascal @@ -124,22 +124,22 @@ (def ^:const measurement->kilopascal "A map of pressure measurements to functions that convert to kilopascals." - {opts/pascal pascal->kilopascal - opts/kilopascal identity - opts/bar bar->kilopascal - opts/atmosphere atmosphere->kilopascal - opts/torr torr->kilopascal - opts/psi psi->kilopascal}) + {options/pascal pascal->kilopascal + options/kilopascal identity + options/bar bar->kilopascal + options/atmosphere atmosphere->kilopascal + options/torr torr->kilopascal + options/psi psi->kilopascal}) (def ^:const kilopascal->measurement "A map of pressure measurements to functions that convert from kilopascals." - {opts/pascal kilopascal->pascal - opts/kilopascal identity - opts/bar kilopascal->bar - opts/atmosphere kilopascal->atmosphere - opts/torr kilopascal->torr - opts/psi kilopascal->psi}) + {options/pascal kilopascal->pascal + options/kilopascal identity + options/bar kilopascal->bar + options/atmosphere kilopascal->atmosphere + options/torr kilopascal->torr + options/psi kilopascal->psi}) (defn convert @@ -178,12 +178,12 @@ ([pressure source-units] (display pressure source-units {})) ([pressure source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? pressure) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> pressure (precision/->precision precision) @@ -194,4 +194,4 @@ :pressure pressure :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/src/brewtility/units/specific_gravity.cljc b/src/brewtility/units/specific_gravity.cljc index 966e399..8d770cf 100644 --- a/src/brewtility/units/specific_gravity.cljc +++ b/src/brewtility/units/specific_gravity.cljc @@ -8,28 +8,28 @@ - [specific-gravity](https://en.wikipedia.org/wiki/Specific_gravity)" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) (def ^:const measurements "The specific gravity systems available across brewtility." - #{opts/specific-gravity}) + #{options/specific-gravity}) (def ^:const measurements->display-name "A map from specific gravity system names to their full and short unit names." - {opts/specific-gravity {opts/full "specific gravity" - opts/short "sg"}}) + {options/specific-gravity {options/full "specific gravity" + options/short "sg"}}) (def ^:const measurement->specific-gravity "A map from specific gravity system names to the conversion function to specific gravity." - {opts/specific-gravity identity}) + {options/specific-gravity identity}) (def ^:const specific-gravity->measurement "A map from specific gravity system names to the conversion function from specific gravity." - {opts/specific-gravity identity}) + {options/specific-gravity identity}) (defn convert @@ -70,12 +70,12 @@ ([gravity source-units] (display gravity source-units {})) ([gravity source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? gravity) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> gravity (precision/->precision precision) @@ -86,4 +86,4 @@ :specific-gravity gravity :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/src/brewtility/units/temperature.cljc b/src/brewtility/units/temperature.cljc index 2f752c1..f5b7420 100644 --- a/src/brewtility/units/temperature.cljc +++ b/src/brewtility/units/temperature.cljc @@ -10,36 +10,36 @@ - [kelvin](https://en.wikipedia.org/wiki/Kelvin_(unit))" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) (def ^:const measurements "The temperature measurements supported by brewtility." - #{opts/c - opts/celsius - opts/centigrade - opts/f - opts/fahrenheit - opts/k - opts/kelvin}) + #{options/c + options/celsius + options/centigrade + options/f + options/fahrenheit + options/k + options/kelvin}) (def ^:const measurements->display-name "The temperature measurements supported by brewtility." - {opts/c {opts/full "celsius" - opts/short "c"} - opts/celsius {opts/full "celsius" - opts/short "c"} - opts/centigrade {opts/full "centigrade" - opts/short "mg"} - opts/f {opts/full "fahrenheit" - opts/short "f"} - opts/fahrenheit {opts/full "fahrenheit" - opts/short "f"} - opts/k {opts/full "kelvin" - opts/short "k"} - opts/kelvin {opts/full "kelvin" - opts/short "k"}}) + {options/c {options/full "celsius" + options/short "c"} + options/celsius {options/full "celsius" + options/short "c"} + options/centigrade {options/full "centigrade" + options/short "mg"} + options/f {options/full "fahrenheit" + options/short "f"} + options/fahrenheit {options/full "fahrenheit" + options/short "f"} + options/k {options/full "kelvin" + options/short "k"} + options/kelvin {options/full "kelvin" + options/short "k"}}) (defn- celsius->fahrenheit @@ -76,24 +76,24 @@ (def ^:const measurement->celsius "A map from measurement names to the implementation function which converts them to degrees celsius" - {opts/c identity - opts/celsius identity - opts/centigrade identity - opts/f fahrenheit->celsius - opts/fahrenheit fahrenheit->celsius - opts/k kelvin->celsius - opts/kelvin kelvin->celsius}) + {options/c identity + options/celsius identity + options/centigrade identity + options/f fahrenheit->celsius + options/fahrenheit fahrenheit->celsius + options/k kelvin->celsius + options/kelvin kelvin->celsius}) (def ^:const celsius->measurement "A map from measurement names to the implementation function which converts them from degrees celsius" - {opts/celsius identity - opts/c identity - opts/centigrade identity - opts/fahrenheit celsius->fahrenheit - opts/f celsius->fahrenheit - opts/kelvin celsius->kelvin - opts/k celsius->kelvin}) + {options/celsius identity + options/c identity + options/centigrade identity + options/fahrenheit celsius->fahrenheit + options/f celsius->fahrenheit + options/kelvin celsius->kelvin + options/k celsius->kelvin}) (defn convert @@ -137,12 +137,12 @@ ([temperature source-units] (display temperature source-units {})) ([temperature source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? temperature) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> temperature (precision/->precision precision) @@ -153,4 +153,4 @@ :temperature temperature :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/src/brewtility/units/time.cljc b/src/brewtility/units/time.cljc index 74c8006..742f709 100644 --- a/src/brewtility/units/time.cljc +++ b/src/brewtility/units/time.cljc @@ -15,52 +15,52 @@ - [week](https://en.wikipedia.org/wiki/Week)" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts]) + [brewtility.units.options :as options]) (:refer-clojure :exclude [time])) (def ^:const measurements "The time measurements available across brewtility" - #{opts/day - opts/hour - opts/microsecond - opts/millisecond - opts/minute - opts/nanosecond - opts/second - opts/week}) + #{options/day + options/hour + options/microsecond + options/millisecond + options/minute + options/nanosecond + options/second + options/week}) (def ^:const measurement->minute "A map from measurement names to doubles representing their fractional value to one minute" - {opts/day 1440.0 - opts/hour 60.0 - opts/microsecond (/ 1.0 60000000) - opts/millisecond (/ 1.0 60000) - opts/minute 1.0 - opts/nanosecond (/ 1.0 60000000000) - opts/second (/ 1.0 60) - opts/week 10080.0}) + {options/day 1440.0 + options/hour 60.0 + options/microsecond (/ 1.0 60000000) + options/millisecond (/ 1.0 60000) + options/minute 1.0 + options/nanosecond (/ 1.0 60000000000) + options/second (/ 1.0 60) + options/week 10080.0}) (def ^:const measurements->display-name "A map from measurement names to their full name and abbreviation" - {opts/day {opts/full "days" - opts/short "d"} - opts/hour {opts/full "hour" - opts/short "hr"} - opts/microsecond {opts/full "microsecond" - opts/short "µs"} - opts/millisecond {opts/full "millisecond" - opts/short "ims"} - opts/minute {opts/full "minute" - opts/short "m"} - opts/nanosecond {opts/full "nanosecond" - opts/short "ns"} - opts/second {opts/full "second" - opts/short "s"} - opts/week {opts/full "week" - opts/short "w"}}) + {options/day {options/full "days" + options/short "d"} + options/hour {options/full "hour" + options/short "hr"} + options/microsecond {options/full "microsecond" + options/short "µs"} + options/millisecond {options/full "millisecond" + options/short "ims"} + options/minute {options/full "minute" + options/short "m"} + options/nanosecond {options/full "nanosecond" + options/short "ns"} + options/second {options/full "second" + options/short "s"} + options/week {options/full "week" + options/short "w"}}) (defn- minute->measurement @@ -112,12 +112,12 @@ ([time source-units] (display time source-units {})) ([time source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? time) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> time (precision/->precision precision) @@ -128,4 +128,4 @@ :time time :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/src/brewtility/units/volume.cljc b/src/brewtility/units/volume.cljc index 9f18100..fa7e87c 100644 --- a/src/brewtility/units/volume.cljc +++ b/src/brewtility/units/volume.cljc @@ -22,79 +22,79 @@ - [teaspoon](https://en.wikipedia.org/wiki/Teaspoon))" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) (def ^:const measurements "The volume measurements available across brewtility" - #{opts/american-gallon - opts/american-pint - opts/american-quart - opts/cup - opts/imperial-gallon - opts/imperial-pint - opts/imperial-quart - opts/liter - opts/litre - opts/milliliter - opts/millilitre - opts/tablespoon - opts/teaspoon - opts/american-fluid-ounce - opts/imperial-fluid-ounce}) + #{options/american-gallon + options/american-pint + options/american-quart + options/cup + options/imperial-gallon + options/imperial-pint + options/imperial-quart + options/liter + options/litre + options/milliliter + options/millilitre + options/tablespoon + options/teaspoon + options/american-fluid-ounce + options/imperial-fluid-ounce}) (def ^:const measurement->litre "A map from measurement names to doubles representing their fractional value to one liter" - {opts/american-fluid-ounce 0.0295735 - opts/american-gallon 3.78541 - opts/american-pint 0.473176 - opts/american-quart 0.946353 - opts/cup 0.236588 - opts/imperial-fluid-ounce 0.0284131 - opts/imperial-gallon 4.54609 - opts/imperial-pint 0.568261 - opts/imperial-quart 1.13652 - opts/liter 1.0 - opts/litre 1.0 - opts/milliliter 0.001 - opts/millilitre 0.001 - opts/tablespoon 0.0147868 - opts/teaspoon 0.00492892}) + {options/american-fluid-ounce 0.0295735 + options/american-gallon 3.78541 + options/american-pint 0.473176 + options/american-quart 0.946353 + options/cup 0.236588 + options/imperial-fluid-ounce 0.0284131 + options/imperial-gallon 4.54609 + options/imperial-pint 0.568261 + options/imperial-quart 1.13652 + options/liter 1.0 + options/litre 1.0 + options/milliliter 0.001 + options/millilitre 0.001 + options/tablespoon 0.0147868 + options/teaspoon 0.00492892}) (def ^:const measurements->display-name "A map from measurement names to their full name and abbreviation" - {opts/american-fluid-ounce {opts/full "fluid ounce" - opts/short "fl oz"} - opts/american-gallon {opts/full "US gallon" - opts/short "gal"} - opts/american-pint {opts/full "american pint" - opts/short "pt"} - opts/american-quart {opts/full "american quart" - opts/short "qt"} - opts/cup {opts/full "cup" - opts/short "c"} - opts/imperial-fluid-ounce {opts/full "imperial fluid ounce" - opts/short "imp fl oz"} - opts/imperial-gallon {opts/full "imperial gallon" - opts/short "gal"} - opts/imperial-pint {opts/full "imperial pint" - opts/short "pt"} - opts/imperial-quart {opts/full "imperial quart" - opts/short "qt"} - opts/liter {opts/full "liter" - opts/short "l"} - opts/litre {opts/full "litre" - opts/short "l"} - opts/milliliter {opts/full "milliliter" - opts/short "ml"} - opts/millilitre {opts/full "millilitre" - opts/short "ml"} - opts/tablespoon {opts/full "tablespoon" - opts/short "tbsp"} - opts/teaspoon {opts/full "teaspoon" - opts/short "tsp"}}) + {options/american-fluid-ounce {options/full "fluid ounce" + options/short "fl oz"} + options/american-gallon {options/full "US gallon" + options/short "gal"} + options/american-pint {options/full "american pint" + options/short "pt"} + options/american-quart {options/full "american quart" + options/short "qt"} + options/cup {options/full "cup" + options/short "c"} + options/imperial-fluid-ounce {options/full "imperial fluid ounce" + options/short "imp fl oz"} + options/imperial-gallon {options/full "imperial gallon" + options/short "gal"} + options/imperial-pint {options/full "imperial pint" + options/short "pt"} + options/imperial-quart {options/full "imperial quart" + options/short "qt"} + options/liter {options/full "liter" + options/short "l"} + options/litre {options/full "litre" + options/short "l"} + options/milliliter {options/full "milliliter" + options/short "ml"} + options/millilitre {options/full "millilitre" + options/short "ml"} + options/tablespoon {options/full "tablespoon" + options/short "tbsp"} + options/teaspoon {options/full "teaspoon" + options/short "tsp"}}) (defn- liter->measurement @@ -146,12 +146,12 @@ ([volume source-units] (display volume source-units {})) ([volume source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? volume) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> volume (precision/->precision precision) @@ -162,4 +162,4 @@ :volume volume :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/src/brewtility/units/weight.cljc b/src/brewtility/units/weight.cljc index bb6899c..eb09fe8 100644 --- a/src/brewtility/units/weight.cljc +++ b/src/brewtility/units/weight.cljc @@ -12,39 +12,39 @@ - [pound](https://en.wikipedia.org/wiki/Pound_(mass))" {:added "2.0"} (:require [brewtility.precision :as precision] - [brewtility.units.options :as opts])) + [brewtility.units.options :as options])) (def ^:const measurements "The weight measurements available across brewtility" - #{opts/gram - opts/kilogram - opts/milligram - opts/ounce - opts/pound}) + #{options/gram + options/kilogram + options/milligram + options/ounce + options/pound}) (def ^:const measurement->kilogram "A map from measurement names to doubles representing their fractional value to one kilogram" - {opts/gram 0.001 - opts/kilogram 1.0 - opts/milligram 0.000001 - opts/ounce 0.02834952 - opts/pound 0.45359237}) + {options/gram 0.001 + options/kilogram 1.0 + options/milligram 0.000001 + options/ounce 0.02834952 + options/pound 0.45359237}) (def ^:const measurements->display-name "A map from measurement names to their full name and abbreviation" - {opts/gram {opts/full "gram" - opts/short "g"} - opts/kilogram {opts/full "kilogram" - opts/short "kg"} - opts/milligram {opts/full "milligram" - opts/short "mg"} - opts/ounce {opts/full "ounce" - opts/short "oz"} - opts/pound {opts/full "pound" - opts/short "lb"}}) + {options/gram {options/full "gram" + options/short "g"} + options/kilogram {options/full "kilogram" + options/short "kg"} + options/milligram {options/full "milligram" + options/short "mg"} + options/ounce {options/full "ounce" + options/short "oz"} + options/pound {options/full "pound" + options/short "lb"}}) (defn- kilogram->measurement @@ -96,12 +96,12 @@ ([weight source-units] (display weight source-units {})) ([weight source-units {:keys [precision suffix] - :or {precision opts/default-precision - suffix opts/short}}] + :or {precision options/default-precision + suffix options/short}}] (if (and (contains? measurements source-units) (number? weight) (integer? precision) - (contains? opts/supported-suffixes suffix)) + (contains? options/supported-suffixes suffix)) (let [display-name (get-in measurements->display-name [source-units suffix])] (-> weight (precision/->precision precision) @@ -112,4 +112,4 @@ :weight weight :precision precision :suffix suffix - :allowed-suffixes opts/supported-suffixes}))))) + :allowed-suffixes options/supported-suffixes}))))) diff --git a/test/brewtility/calculations_test.cljc b/test/brewtility/calculations_test.cljc index 12b7eac..7b3a7a6 100644 --- a/test/brewtility/calculations_test.cljc +++ b/test/brewtility/calculations_test.cljc @@ -1,9 +1,9 @@ (ns brewtility.calculations-test - (:require #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) [brewtility.calculations :as sut] - [brewtility.units.color :as color] [brewtility.precision :as bp] + [brewtility.units.color :as color] [common-beer-data.fermentables.adjuncts :as adjuncts] [common-beer-data.fermentables.grains :as grains] [common-beer-data.hops.both :as hops])) diff --git a/test/brewtility/precision_test.cljc b/test/brewtility/precision_test.cljc index d9010ff..1994220 100644 --- a/test/brewtility/precision_test.cljc +++ b/test/brewtility/precision_test.cljc @@ -1,6 +1,6 @@ (ns brewtility.precision-test - (:require #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) [brewtility.precision :as sut])) diff --git a/test/brewtility/units/color_test.cljc b/test/brewtility/units/color_test.cljc index fa569d5..636b147 100644 --- a/test/brewtility/units/color_test.cljc +++ b/test/brewtility/units/color_test.cljc @@ -2,8 +2,8 @@ (:require [brewtility.precision :as precision] [brewtility.units.color :as sut] [brewtility.units.options :as options] - #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) + #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]))) (deftest srm->rgba-test @@ -91,4 +91,57 @@ (precision/->2dp (sut/convert 60.38 options/ebc options/lovibond)))) (is (= -45.53 (precision/->2dp (sut/convert -23.11 options/srm options/ebc)) - (precision/->2dp (sut/convert -16.5 options/lovibond options/ebc)))))) + (precision/->2dp (sut/convert -16.5 options/lovibond options/ebc))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :srm)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :srm)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "10.0 srm" + (sut/display 10.0 options/srm)) + (sut/display 10.0 :srm)) + (is (= "23.2 ebc" + (sut/display 23.2 options/ebc)) + (sut/display 23.2 :ebc)) + (is (= "10.0 degrees lovibond" + (sut/display 10.0 options/lovibond {options/suffix options/full})) + (sut/display 10.0 :lovibond)) + (is (= sut/srm-37 (sut/display sut/srm-37 options/rgba)))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "SRM color map" + (is (map? sut/srm-color-map)) + (is (not-empty sut/srm-color-map)) + (is (every? number? (keys sut/srm-color-map))) + (is (every? string? (vals sut/srm-color-map)))) + (testing "measurement->srm" + (is (map? sut/measurement->srm)) + (is (not-empty sut/measurement->srm)) + (is (every? keyword? (keys sut/measurement->srm))) + (is (every? ifn? (vals sut/measurement->srm)))) + (testing "srm->measurement" + (is (map? sut/srm->measurement)) + (is (not-empty sut/srm->measurement)) + (is (every? keyword? (keys sut/srm->measurement))) + (is (= (sort (keys sut/srm->measurement)) + (sort (keys sut/measurements->display-name)) + (sort sut/measurements))) + (is (every? ifn? (vals sut/srm->measurement)))))) diff --git a/test/brewtility/units/pressure_test.cljc b/test/brewtility/units/pressure_test.cljc new file mode 100644 index 0000000..9b37e67 --- /dev/null +++ b/test/brewtility/units/pressure_test.cljc @@ -0,0 +1,67 @@ +(ns brewtility.units.pressure-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.pressure :as sut])) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/pascal options/pascal)) + (precision/->2dp (sut/convert 100.0 :pascal :pascal)))) + (is (= 100000.0 (precision/->2dp (sut/convert 100.0 options/kilopascal options/pascal)))) + (is (= 1.0 (precision/->2dp (sut/convert 100.0 options/kilopascal options/bar)))) + (is (= 0.99 (precision/->2dp (sut/convert 100.0 options/kilopascal options/atmosphere)))) + (is (= 750.06 (precision/->2dp (sut/convert 100.0 options/kilopascal options/torr)))) + (is (= 14.5 (precision/->2dp (sut/convert 100.0 options/kilopascal options/psi)))) + (is (= 0.1 (precision/->2dp (sut/convert 100.0 options/pascal options/kilopascal)))) + (is (= 10000.0 (precision/->2dp (sut/convert 100.0 options/bar options/kilopascal)))) + (is (= 10132.5 (precision/->2dp (sut/convert 100.0 options/atmosphere options/kilopascal)))) + (is (= 13.33 (precision/->2dp (sut/convert 100.0 options/torr options/kilopascal)))) + (is (= 689.48 (precision/->2dp (sut/convert 100.0 options/psi options/kilopascal))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 pa" + (sut/display 1.5 :pascal) + (sut/display 1.5 options/pascal))) + (is (= "1.5 pascal" + (sut/display 1.5 :pascal {options/suffix options/full})))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->kilopascal" + (is (map? sut/measurement->kilopascal)) + (is (not-empty sut/measurement->kilopascal)) + (is (every? keyword? (keys sut/measurement->kilopascal))) + (is (every? ifn? (vals sut/measurement->kilopascal)))) + (testing "kilopascal->measurement" + (is (map? sut/kilopascal->measurement)) + (is (not-empty sut/kilopascal->measurement)) + (is (every? keyword? (keys sut/kilopascal->measurement))) + (is (= (set (keys sut/measurement->kilopascal)) + (set (keys sut/kilopascal->measurement)) + (set (keys sut/measurements->display-name)) + sut/measurements)) + (is (every? ifn? (vals sut/kilopascal->measurement)))))) diff --git a/test/brewtility/units/specific_gravity_test.cljc b/test/brewtility/units/specific_gravity_test.cljc new file mode 100644 index 0000000..f386473 --- /dev/null +++ b/test/brewtility/units/specific_gravity_test.cljc @@ -0,0 +1,55 @@ +(ns brewtility.units.specific-gravity-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.specific-gravity :as sut])) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/specific-gravity options/specific-gravity)) + (precision/->2dp (sut/convert 100.0 :specific-gravity :specific-gravity))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 sg" + (sut/display 1.5 :specific-gravity) + (sut/display 1.5 options/specific-gravity)))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->specific-gravity" + (is (map? sut/measurement->specific-gravity)) + (is (not-empty sut/measurement->specific-gravity)) + (is (every? keyword? (keys sut/measurement->specific-gravity))) + (is (every? ifn? (vals sut/measurement->specific-gravity)))) + (testing "specific-gravity->measurement" + (is (map? sut/specific-gravity->measurement)) + (is (not-empty sut/specific-gravity->measurement)) + (is (every? keyword? (keys sut/specific-gravity->measurement))) + (is (= (set (keys sut/measurement->specific-gravity)) + (set (keys sut/specific-gravity->measurement)) + (set (keys sut/measurements->display-name)) + sut/measurements)) + (is (every? ifn? (vals sut/specific-gravity->measurement)))))) diff --git a/test/brewtility/units_test.cljc b/test/brewtility/units_test.cljc index 17dfcc2..78cb950 100644 --- a/test/brewtility/units_test.cljc +++ b/test/brewtility/units_test.cljc @@ -1,31 +1,48 @@ (ns brewtility.units-test - (:require #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) [brewtility.precision :as bp] - [brewtility.units :as sut])) + [brewtility.units :as sut] + [brewtility.units.color :as color] + [brewtility.units.options :as options])) (deftest convert-volume-test (testing "Volume conversions should behave as expected" - (is (= 1.0 (sut/convert :volume 1.0 :liter :litre))) - (is (= 1.0 (sut/convert :volume 1.0 :litre :litre))) - (is (= 0.099 (bp/->3dp (sut/convert :volume 20 :teaspoon :liter)))) - (is (= 35.1 (bp/->3dp (sut/convert :volume 35.1 :tablespoon :tablespoon)))) - (is (= 12.0 (bp/->3dp (sut/convert :volume 9.99209 :imperial-pint :american-pint)))))) + (is (= 1.0 (sut/convert :volume 1.0 options/liter options/litre))) + (is (= 1.0 (sut/convert :volume 1.0 options/litre options/litre))) + (is (= 0.099 (bp/->3dp (sut/convert :volume 20 options/teaspoon options/liter)))) + (is (= 35.1 (bp/->3dp (sut/convert :volume 35.1 options/tablespoon options/tablespoon)))) + (is (= 12.0 (bp/->3dp (sut/convert :volume 9.99209 options/imperial-pint options/american-pint)))))) (deftest convert-weight-test (testing "Weight conversions should behave as expected" - (is (= 1.0 (sut/convert :weight 1.0 :kilogram :kilogram))) - (is (= 1.0 (sut/convert :weight 1.0 :pound :pound))) - (is (= 240.0 (bp/->3dp (sut/convert :weight 15.0 :pound :ounce)))) - (is (= 0.003 (bp/->3dp (sut/convert :weight 1205.5 :milligram :pound)))))) + (is (= 1.0 (sut/convert :weight 1.0 options/kilogram options/kilogram))) + (is (= 1.0 (sut/convert :weight 1.0 options/pound options/pound))) + (is (= 240.0 (bp/->3dp (sut/convert :weight 15.0 options/pound options/ounce)))) + (is (= 0.003 (bp/->3dp (sut/convert :weight 1205.5 options/milligram options/pound)))))) (deftest convert-temperature-test (testing "Temperature conversions should behave as expected" - (is (= 32.0 (sut/convert :temperature 32.0 :fahrenheit :fahrenheit))) - (is (= 32.0 (sut/convert :temperature 0.0 :celsius :fahrenheit))) - (is (= 340.706 (bp/->3dp (sut/convert :temperature 153.6008 :fahrenheit :kelvin)))) - (is (= 1743.15 (bp/->3dp (sut/convert :temperature 2016.3 :kelvin :c)))) - (is (= -40.0 (bp/->3dp (sut/convert :temperature -40.0 :f :c)))))) + (is (= 32.0 (sut/convert :temperature 32.0 options/fahrenheit options/fahrenheit))) + (is (= 32.0 (sut/convert :temperature 0.0 options/celsius options/fahrenheit))) + (is (= 340.706 (bp/->3dp (sut/convert :temperature 153.6008 options/fahrenheit options/kelvin)))) + (is (= 1743.15 (bp/->3dp (sut/convert :temperature 2016.3 options/kelvin options/c)))) + (is (= -40.0 (bp/->3dp (sut/convert :temperature -40.0 options/f options/c)))))) + + +(deftest convert-color-test + (testing "Color conversions behaves as expected" + (is (= color/srm-1 (sut/convert :color 1 options/srm options/rgba))) + (is (= color/srm-1 (sut/convert :color 1 :srm :rgba))) + (is (= color/srm-13 (sut/convert :color 10.16 options/lovibond options/rgba))) + (is (= color/srm-13 (sut/convert :color 10.16 :lovibond :rgba))) + (is (= color/srm-13 (sut/convert :color 25.61 options/ebc options/rgba))) + (is (= 13.00988 (sut/convert :color 25.61 options/ebc options/srm))) + (is (= 25.61 (sut/convert :color 25.61 options/srm options/srm))) + (is (= 50.451699999999995 (sut/convert :color 25.61 options/srm options/ebc))) + (is (= 10.16527388158866 (sut/convert :color 25.61 options/ebc options/lovibond))) + (is (= 66.84467282 (sut/convert :color 25.61 options/lovibond options/ebc))) + (is (= color/srm-13 (sut/convert :color 25.61 :ebc :rgba))))) diff --git a/test/brewtility/wrapping_test.cljc b/test/brewtility/wrapping_test.cljc index 7616215..eafc330 100644 --- a/test/brewtility/wrapping_test.cljc +++ b/test/brewtility/wrapping_test.cljc @@ -1,6 +1,6 @@ (ns brewtility.wrapping-test - (:require #? (:clj [clojure.test :refer [deftest is testing]]) - #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) [brewtility.data.equipment :as equipment.data] [brewtility.data.fermentables :as fermentables.data] [brewtility.data.hops :as hops.data] From 0b09ddbe3c9a9bee79bdfb5ef37cdbe870e5eeef Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 17:40:42 +0000 Subject: [PATCH 05/11] Test all unit conversions --- src/brewtility/units.cljc | 21 +- src/brewtility/units/options.cljc | 136 ++++++- src/brewtility/units/specific_gravity.cljc | 2 +- src/brewtility/units/temperature.cljc | 54 ++- src/brewtility/units/time.cljc | 2 +- src/brewtility/units/volume.cljc | 32 +- test/brewtility/precision_test.cljc | 6 +- test/brewtility/runner.cljs | 18 + test/brewtility/units/color_test.cljc | 12 +- test/brewtility/units/temperature_test.cljc | 103 +++++ test/brewtility/units/time_test.cljc | 108 +++++ test/brewtility/units/volume_test.cljc | 176 +++++++++ test/brewtility/units/weight_test.cljc | 96 +++++ test/brewtility/units_test.cljc | 414 +++++++++++++++++++- 14 files changed, 1125 insertions(+), 55 deletions(-) create mode 100644 test/brewtility/units/temperature_test.cljc create mode 100644 test/brewtility/units/time_test.cljc create mode 100644 test/brewtility/units/volume_test.cljc create mode 100644 test/brewtility/units/weight_test.cljc diff --git a/src/brewtility/units.cljc b/src/brewtility/units.cljc index a334fa0..76b1735 100644 --- a/src/brewtility/units.cljc +++ b/src/brewtility/units.cljc @@ -57,10 +57,10 @@ :time (time/convert measurement source-units target-units) :volume (volume/convert measurement source-units target-units) :weight (weight/convert measurement source-units target-units) - :else (throw (ex-info "Unsupported unit system" - {:measurement-type measurement-type - :allowed-values options/measurement-types - :measurement measurement})))) + (throw (ex-info "Unsupported unit system" + {:measurement-type measurement-type + :allowed-values options/measurement-types + :measurement measurement})))) (defn display @@ -84,10 +84,11 @@ "brewtility.units.volume/display" "brewtility.units.weight/display"]} ([measurement-type measurement source-units] - (display measurement source-units measurement-type {})) + (display measurement-type measurement source-units {})) ([measurement-type measurement source-units opts] - (let [options (merge {options/precision options/default-precision options/suffix options/short} opts)] + (let [options (merge {options/precision options/default-precision + options/suffix options/short} opts)] (case measurement-type :color (color/display measurement source-units options) :pressure (pressure/display measurement source-units options) @@ -96,7 +97,7 @@ :time (time/display measurement source-units options) :volume (volume/display measurement source-units options) :weight (weight/display measurement source-units options) - :else (throw (ex-info "Unsupported unit system" - {:measurement-type measurement-type - :allowed-values options/measurement-types - :measurement measurement})))))) + (throw (ex-info "Unsupported unit system" + {:measurement-type measurement-type + :allowed-values options/measurement-types + :measurement measurement})))))) diff --git a/src/brewtility/units/options.cljc b/src/brewtility/units/options.cljc index 11a4994..b1f2676 100644 --- a/src/brewtility/units/options.cljc +++ b/src/brewtility/units/options.cljc @@ -3,7 +3,7 @@ Implemented as part of the Symbolic Keyword pattern." {:added "2.0"} - (:refer-clojure :exclude [short second])) + (:refer-clojure :exclude [second short time])) ;; Defaults @@ -15,6 +15,9 @@ ;; Systems of Measure +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} + + (def system-of-measure "The system of measure used in the recipe or for a given unit. @@ -27,6 +30,9 @@ :system-of-measure) +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} + + (def imperial "The [British imperial](https://en.wikipedia.org/wiki/Imperial_units) system of measure. @@ -34,6 +40,9 @@ :imperial) +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} + + (def metric "The [metric system](https://en.wikipedia.org/wiki/Metric_system) of measure. @@ -41,6 +50,9 @@ :metric) +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} + + (def us-customary "The [United States Customary Units](https://en.wikipedia.org/wiki/United_States_customary_units) system of measure. @@ -48,6 +60,9 @@ :us) +#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} + + (def international-system "The [International System of Units](https://en.wikipedia.org/wiki/International_System_of_Units) system of measure. @@ -96,15 +111,114 @@ #{short full}) +(def color + "The color systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.color` and in argument/option maps. + Brewility supports the following color systems: + - [Standard Reference Method](https://en.wikipedia.org/wiki/Standard_Reference_Method) + - [European Brewery Convention](https://en.wikipedia.org/wiki/European_Brewery_Convention) + - [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) + - [RGBA](https://en.wikipedia.org/wiki/RGBA_color_space)" + :color) + + +(def pressure + "The pressure systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.pressure` and in argument/option maps. + Currently, brewtility supports the following types of pressure: + - [pascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) + - [kilopascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) + - [bar](https://en.wikipedia.org/wiki/Bar_(unit)) + - [atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit)) + - [torr](https://en.wikipedia.org/wiki/Torr) + - [psi](https://en.wikipedia.org/wiki/Pound-force_per_square_inch" + :pressure) + + +(def specific-gravity + "The specific gravity systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.specific-gravity` and in argument/option maps. + Currently, brewtility supports the following types of specific gravity: + Currently, brewtility supports the following types of specific gravity: + - [specific-gravity](https://en.wikipedia.org/wiki/Specific_gravity)" + :specific-gravity) + + +(def temperature + "The temperature systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.temperature` and in argument/option maps. + Currently, brewtility supports the following types of temperature: + - [celsius](https://en.wikipedia.org/wiki/Celsius) + - [fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) + - [kelvin](https://en.wikipedia.org/wiki/Kelvin)" + :temperature) + + +(def time + "The time systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.time` and in argument/option maps. + Currently, brewtility supports the following types of time measurements: + - [microsecond](https://en.wikipedia.org/wiki/Microsecond) + - [nanosecond](https://en.wikipedia.org/wiki/Nanosecond) + - [millisecond](https://en.wikipedia.org/wiki/Millisecond) + - [second](https://en.wikipedia.org/wiki/Second) + - [minute](https://en.wikipedia.org/wiki/Minute) + - [hour](https://en.wikipedia.org/wiki/Hour) + - [day](https://en.wikipedia.org/wiki/Day) + - [week](https://en.wikipedia.org/wiki/Week)" + :time) + + +(def volume + "The volume systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.volume` and in argument/option maps. + Currently, brewtility supports the following types of volume: + - [american-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) + - [american-gallon](https://en.wikipedia.org/wiki/Gallon) + - [american-pint](https://en.wikipedia.org/wiki/Pint) + - [american-quart](https://en.wikipedia.org/wiki/Quart) + - [cup](https://en.wikipedia.org/wiki/Cup_(unit)) + - [imperial-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) + - [imperial-gallon](https://en.wikipedia.org/wiki/Gallon) + - [imperial-pint](https://en.wikipedia.org/wiki/Pint) + - [imperial-quart](https://en.wikipedia.org/wiki/Quart) + - [liter](https://en.wikipedia.org/wiki/Litre) + - [litre](https://en.wikipedia.org/wiki/Litre) + - [milliliter](https://en.wikipedia.org/wiki/Millilitre) + - [millilitre](https://en.wikipedia.org/wiki/Millilitre) + - [tablespoon](https://en.wikipedia.org/wiki/Tablespoon) + - [teaspoon](https://en.wikipedia.org/wiki/Teaspoon))" + :volume) + + +(def weight + "The weight systems used in the recipe or for a given unit. + + Commonly used with `brewtility.units.weight` and in argument/option maps. + Currently, brewtility supports the following types of weight: + - [gram](https://en.wikipedia.org/wiki/Gram) + - [milligram](https://en.wikipedia.org/wiki/Milligram) + - [kilogram](https://en.wikipedia.org/wiki/Kilogram) + - [ounce](https://en.wikipedia.org/wiki/Ounce) + - [pound](https://en.wikipedia.org/wiki/Pound_(mass))" + :weight) + + (def measurement-types "The measurement types available across brewtility." - #{:color - :pressure - :specific-gravity - :temperature - :time - :volume - :weight}) + #{color + pressure + specific-gravity + temperature + time + volume + weight}) ;; Color Systems @@ -445,9 +559,3 @@ Commonly used with `brewtility.units.pressure` and in argument maps." :psi) - -;; Specific Gravity Units - -(def ^:const specific-gravity - "The [Specific Gravity](https://en.wikipedia.org/wiki/Specific_gravity) unit of measure." - :specific-gravity) diff --git a/src/brewtility/units/specific_gravity.cljc b/src/brewtility/units/specific_gravity.cljc index 8d770cf..808c4af 100644 --- a/src/brewtility/units/specific_gravity.cljc +++ b/src/brewtility/units/specific_gravity.cljc @@ -58,7 +58,7 @@ For example, ```clj - (display 1.5 :pound) ;; => \"1.5 lb\" + (display 1.5 :specific-gravity) ;; => \"1.5 sg\" ``` This function accepts an option map as an optional third argument. The following options are available: diff --git a/src/brewtility/units/temperature.cljc b/src/brewtility/units/temperature.cljc index f5b7420..efa8245 100644 --- a/src/brewtility/units/temperature.cljc +++ b/src/brewtility/units/temperature.cljc @@ -96,6 +96,56 @@ options/k celsius->kelvin}) +(defn- is-celsius? + "A function to determine if a temperature measurement is in celsius." + {:no-doc true + :added "2.0"} + [measurement] + (boolean (or (= measurement options/c) + (= measurement options/celsius)))) + + +(defn- is-fahrenheit? + "A function to determine if a temperature measurement is in fahrenheit." + {:no-doc true + :added "2.0"} + [measurement] + (boolean (or (= measurement options/f) + (= measurement options/fahrenheit)))) + + +(defn- is-kelvin? + "A function to determine if a temperature measurement is in kelvin." + {:no-doc true + :added "2.0"} + [measurement] + (boolean (or (= measurement options/k) + (= measurement options/kelvin)))) + + +(defn- is-centigrade? + "A function to determine if a temperature measurement is in centigrade." + {:no-doc true + :added "2.0"} + [measurement] + (= measurement options/centigrade)) + + +(defn- same-measurement? + "A function to determine if two temperature measurements systems are the same." + {:no-doc true + :added "2.0"} + [measurement1 measurement2] + (boolean (or (and (is-celsius? measurement1) + (is-celsius? measurement2)) + (and (is-fahrenheit? measurement1) + (is-fahrenheit? measurement2)) + (and (is-kelvin? measurement1) + (is-kelvin? measurement2)) + (and (is-centigrade? measurement1) + (is-centigrade? measurement2))))) + + (defn convert "Given a `temperature` in `source-measurement`, convert it to the `target-measurement`. Supported values for `source-measurement` and `target-measurement` are enumerated in [[temperature-measurements]]. @@ -106,7 +156,7 @@ (if (and (contains? measurements source-measurement) (contains? measurements target-measurement) (number? temperature)) - (if (= source-measurement target-measurement) + (if (same-measurement? source-measurement target-measurement) temperature (let [source->celsius-fn (measurement->celsius source-measurement) celsius->target-fn (celsius->measurement target-measurement)] @@ -124,7 +174,7 @@ For example, ```clj - (display 1.5 :pound) ;; => \"1.5 lb\" + (display 1.5 :celsius) ;; => \"1.5 c\" ``` This function accepts an option map as an optional third argument. The following options are available: diff --git a/src/brewtility/units/time.cljc b/src/brewtility/units/time.cljc index 742f709..3b4bcee 100644 --- a/src/brewtility/units/time.cljc +++ b/src/brewtility/units/time.cljc @@ -52,7 +52,7 @@ options/microsecond {options/full "microsecond" options/short "µs"} options/millisecond {options/full "millisecond" - options/short "ims"} + options/short "ms"} options/minute {options/full "minute" options/short "m"} options/nanosecond {options/full "nanosecond" diff --git a/src/brewtility/units/volume.cljc b/src/brewtility/units/volume.cljc index fa7e87c..0c009a6 100644 --- a/src/brewtility/units/volume.cljc +++ b/src/brewtility/units/volume.cljc @@ -97,6 +97,36 @@ options/short "tsp"}}) +(defn- liters? + "An implementation function to determine if a measurement is in liters." + {:no-doc true + :added "2.0"} + [measurement-name] + (boolean (or (= measurement-name options/liter) + (= measurement-name options/litre)))) + + +(defn- milliliters? + "An implementation function to determine if a measurement is in milliliters." + {:no-doc true + :added "2.0"} + [measurement-name] + (boolean (or (= measurement-name options/milliliter) + (= measurement-name options/millilitre)))) + + +(defn- same-measurement? + "An implementation function to determine if two measurement systems are the same." + {:no-doc true + :added "2.0"} + [measurement-name1 measurement-name2] + (boolean (or (= measurement-name1 measurement-name2) + (and (liters? measurement-name1) + (liters? measurement-name2)) + (and (milliliters? measurement-name1) + (milliliters? measurement-name2))))) + + (defn- liter->measurement "An implementation function to convert liters to another unit of volume" {:no-doc true @@ -115,7 +145,7 @@ (if (and (contains? measurements source-measurement) (contains? measurements target-measurement) (number? volume)) - (if (= source-measurement target-measurement) + (if (same-measurement? source-measurement target-measurement) volume (let [source->litre-multiplier (measurement->litre source-measurement) litre->target-multiplier (liter->measurement target-measurement)] diff --git a/test/brewtility/precision_test.cljc b/test/brewtility/precision_test.cljc index 1994220..948e18e 100644 --- a/test/brewtility/precision_test.cljc +++ b/test/brewtility/precision_test.cljc @@ -12,7 +12,7 @@ (deftest ->1dp-test - (testing "Ensure that rounding to 1 decimal point works" + (testing "Ensure that rounding to 1 decimal point works." (is (= 0.0 (sut/->1dp 0.0))) (is (= 0.0 (sut/->1dp 0.001))) (is (= 5.1 (sut/->1dp 5.05))) @@ -20,7 +20,7 @@ (deftest ->2dp-test - (testing "Ensure that rounding to 2 decimal point works" + (testing "Ensure that rounding to 2 decimal point works." (is (= 0.00 (sut/->2dp 0.0))) (is (= 0.00 (sut/->2dp 0.001))) (is (= 5.05 (sut/->2dp 5.0513))) @@ -29,7 +29,7 @@ (deftest ->3dp-test - (testing "Ensure that rounding to 3 decimal point works" + (testing "Ensure that rounding to 3 decimal point works." (is (= 0.000 (sut/->3dp 0.0))) (is (= 0.001 (sut/->3dp 0.001))) (is (= 5.050 (sut/->3dp 5.05))) diff --git a/test/brewtility/runner.cljs b/test/brewtility/runner.cljs index ce2e9f5..507283c 100644 --- a/test/brewtility/runner.cljs +++ b/test/brewtility/runner.cljs @@ -1,4 +1,8 @@ (ns brewtility.runner + "The ClojureScript test runner for brewtility. + + This namespace is responsible for running all of the ClojureScript tests. + To add new test namespaces, add them to the `:require` and `doo-tests` clauses below." (:require [brewtility.calculations-test] [brewtility.color-test] [brewtility.data.equipment] @@ -22,6 +26,13 @@ [brewtility.predicates.waters-test] [brewtility.predicates.yeasts-test] [brewtility.units-test] + [brewtility.units.color-test] + [brewtility.units.pressure-test] + [brewtility.units.specific-gravity-test] + [brewtility.units.temperature-test] + [brewtility.units.time-test] + [brewtility.units.volume-test] + [brewtility.units.weight-test] [brewtility.wrapping-test] [doo.runner :refer-macros [doo-tests]])) @@ -49,4 +60,11 @@ 'brewtility.predicates.waters-test 'brewtility.predicates.yeasts-test 'brewtility.units-test + 'brewtility.units.color-test + 'brewtility.units.pressure-test + 'brewtility.units.specific-gravity-test + 'brewtility.units.temperature-test + 'brewtility.units.time-test + 'brewtility.units.volume-test + 'brewtility.units.weight-test 'brewtility.wrapping-test) diff --git a/test/brewtility/units/color_test.cljc b/test/brewtility/units/color_test.cljc index 636b147..52f22b4 100644 --- a/test/brewtility/units/color_test.cljc +++ b/test/brewtility/units/color_test.cljc @@ -100,14 +100,14 @@ (deftest display-test (testing "Ensure various color unit conversions behave as expected" (is (= "10.0 srm" - (sut/display 10.0 options/srm)) - (sut/display 10.0 :srm)) + (sut/display 10.0 options/srm) + (sut/display 10.0 :srm))) (is (= "23.2 ebc" - (sut/display 23.2 options/ebc)) - (sut/display 23.2 :ebc)) + (sut/display 23.2 options/ebc) + (sut/display 23.2 :ebc))) (is (= "10.0 degrees lovibond" - (sut/display 10.0 options/lovibond {options/suffix options/full})) - (sut/display 10.0 :lovibond)) + (sut/display 10.0 options/lovibond {options/suffix options/full}) + (sut/display 10.0 :lovibond {options/suffix options/full}))) (is (= sut/srm-37 (sut/display sut/srm-37 options/rgba)))) (testing "Invalid options throw an exception" #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) diff --git a/test/brewtility/units/temperature_test.cljc b/test/brewtility/units/temperature_test.cljc new file mode 100644 index 0000000..3633b8f --- /dev/null +++ b/test/brewtility/units/temperature_test.cljc @@ -0,0 +1,103 @@ +(ns brewtility.units.temperature-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.temperature :as sut])) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->celsius" + (is (map? sut/measurement->celsius)) + (is (not-empty sut/measurement->celsius)) + (is (every? keyword? (keys sut/measurement->celsius))) + (is (every? ifn? (vals sut/measurement->celsius)))) + (testing "celsius->measurement" + (is (map? sut/celsius->measurement)) + (is (not-empty sut/celsius->measurement)) + (is (every? keyword? (keys sut/celsius->measurement))) + (is (= (set (keys sut/measurement->celsius)) + (set (keys sut/celsius->measurement)) + (set (keys sut/measurements->display-name)) + sut/measurements)) + (is (every? ifn? (vals sut/celsius->measurement)))))) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (testing "Source and target systems are identical" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/c options/c)) + (precision/->2dp (sut/convert 100.0 :celsius :c)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/f options/fahrenheit)) + (precision/->2dp (sut/convert 100.0 :f :f)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/k options/kelvin)) + (precision/->2dp (sut/convert 100.0 :k :k)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/centigrade options/centigrade)) + (precision/->2dp (sut/convert 100.0 :centigrade :centigrade))))) + (testing "Source and target systems differ" + (is (= 212.0 (precision/->2dp (sut/convert 100 options/celsius options/fahrenheit)))) + (is (= 363.15 (precision/->2dp (sut/convert 90 options/celsius options/kelvin)))) + (is (= 100.0 (precision/->2dp (sut/convert 100.0 options/celsius options/centigrade)))) + (is (= 32.22 (precision/->2dp (sut/convert 90 options/fahrenheit options/celsius)))) + (is (= 305.37 (precision/->2dp (sut/convert 90 options/fahrenheit options/kelvin)))) + (is (= 32.22 (precision/->2dp (sut/convert 90 options/fahrenheit options/centigrade)))) + (is (= -173.15 (precision/->2dp (sut/convert 100 options/kelvin options/celsius)))) + (is (= -297.67 (precision/->2dp (sut/convert 90 options/kelvin options/fahrenheit)))) + (is (= -183.15 (precision/->2dp (sut/convert 90 options/kelvin options/centigrade)))) + (is (= 100.0 (precision/->2dp (sut/convert 100 options/centigrade options/celsius)))) + (is (= 212.0 (precision/->2dp (sut/convert 100 options/centigrade options/fahrenheit)))) + (is (= 373.15 (precision/->2dp (sut/convert 100 options/centigrade options/kelvin)))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 c" + (sut/display 1.5 :celsius) + (sut/display 1.5 options/celsius) + (sut/display 1.5 options/c))) + (is (= "1.5 k" + (sut/display 1.5 :kelvin) + (sut/display 1.5 options/kelvin) + (sut/display 1.5 options/k))) + (is (= "1.5 f" + (sut/display 1.5 :fahrenheit) + (sut/display 1.5 options/fahrenheit) + (sut/display 1.5 options/f))) + (is (= "1.5 mg" + (sut/display 1.5 options/centigrade))) + (is (= "1.5 celsius" + (sut/display 1.5 :celsius {options/suffix options/full}) + (sut/display 1.5 options/celsius {options/suffix options/full}) + (sut/display 1.5 options/c {options/suffix options/full}))) + (is (= "1.5 kelvin" + (sut/display 1.5 :kelvin {options/suffix options/full}) + (sut/display 1.5 options/kelvin {options/suffix options/full}) + (sut/display 1.5 options/k {options/suffix options/full}))) + (is (= "1.5 fahrenheit" + (sut/display 1.5 :fahrenheit {options/suffix options/full}) + (sut/display 1.5 options/fahrenheit {options/suffix options/full}) + (sut/display 1.5 options/f {options/suffix options/full}))) + (is (= "1.5 centigrade" + (sut/display 1.5 options/centigrade {options/suffix options/full})))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) diff --git a/test/brewtility/units/time_test.cljc b/test/brewtility/units/time_test.cljc new file mode 100644 index 0000000..6306d3b --- /dev/null +++ b/test/brewtility/units/time_test.cljc @@ -0,0 +1,108 @@ +(ns brewtility.units.time-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.time :as sut])) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->minute" + (is (map? sut/measurement->minute)) + (is (not-empty sut/measurement->minute)) + (is (every? keyword? (keys sut/measurement->minute))) + (is (every? number? (vals sut/measurement->minute)))) + (is (= (set (keys sut/measurement->minute)) + (set (keys sut/measurements->display-name)) + sut/measurements)))) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/minute options/minute)) + (precision/->2dp (sut/convert 100.0 :minute :minute)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/nanosecond options/nanosecond)) + (precision/->2dp (sut/convert 100.0 :nanosecond :nanosecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/second options/second)) + (precision/->2dp (sut/convert 100.0 :second :second)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/millisecond options/millisecond)) + (precision/->2dp (sut/convert 100.0 :millisecond :millisecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/microsecond options/microsecond)) + (precision/->2dp (sut/convert 100.0 :microsecond :microsecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/day options/day)) + (precision/->2dp (sut/convert 100.0 :day :day)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/hour options/hour)) + (precision/->2dp (sut/convert 100.0 :hour :hour)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/week options/week)) + (precision/->2dp (sut/convert 100.0 :week :week)))) + (is (= 6000.0 (precision/->2dp (sut/convert 100.0 options/minute options/second)))) + (is (= 6000000.0 (precision/->2dp (sut/convert 100.0 options/minute options/millisecond)))) + (is (= 60000.0 (precision/->2dp (sut/convert 0.001 options/minute options/microsecond)))) + (is (= 600000.0 (precision/->2dp (sut/convert 0.00001 options/minute options/nanosecond)))) + (is (= 0.07 (precision/->2dp (sut/convert 100.0 options/minute options/day)))) + (is (= 1.67 (precision/->2dp (sut/convert 100.0 options/minute options/hour)))) + (is (= 0.01 (precision/->2dp (sut/convert 100.0 options/minute options/week)))) + (is (= 1.67 (precision/->2dp (sut/convert 100.0 options/second options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert 100.0 options/millisecond options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert 100.0 options/microsecond options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert 100.0 options/nanosecond options/minute)))) + (is (= 144000.0 (precision/->2dp (sut/convert 100.0 options/day options/minute)))) + (is (= 6000.0 (precision/->2dp (sut/convert 100.0 options/hour options/minute)))) + (is (= 1008000.0 (precision/->2dp (sut/convert 100.0 options/week options/minute))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 m" + (sut/display 1.5 :minute) + (sut/display 1.5 options/minute))) + (is (= "1.5 d" + (sut/display 1.5 :day) + (sut/display 1.5 options/day))) + (is (= "1.5 hr" + (sut/display 1.5 :hour) + (sut/display 1.5 options/hour))) + (is (= "1.5 w" + (sut/display 1.5 :week) + (sut/display 1.5 options/week))) + (is (= "1.5 ns" + (sut/display 1.5 :nanosecond) + (sut/display 1.5 options/nanosecond))) + (is (= "1.5 ms" + (sut/display 1.5 :millisecond) + (sut/display 1.5 options/millisecond))) + (is (= "1.5 µs" + (sut/display 1.5 :microsecond) + (sut/display 1.5 options/microsecond))) + (is (= "1.5 s" + (sut/display 1.5 :second) + (sut/display 1.5 options/second))) + (is (= "1.5 minute" + (sut/display 1.5 :minute {options/suffix options/full}) + (sut/display 1.5 options/minute {options/suffix options/full})))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) diff --git a/test/brewtility/units/volume_test.cljc b/test/brewtility/units/volume_test.cljc new file mode 100644 index 0000000..753f7a8 --- /dev/null +++ b/test/brewtility/units/volume_test.cljc @@ -0,0 +1,176 @@ +(ns brewtility.units.volume-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.volume :as sut])) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->litre" + (is (map? sut/measurement->litre)) + (is (not-empty sut/measurement->litre)) + (is (every? keyword? (keys sut/measurement->litre))) + (is (every? number? (vals sut/measurement->litre)))) + (is (= (set (keys sut/measurement->litre)) + (set (keys sut/measurements->display-name)) + sut/measurements)))) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/liter options/litre)) + (precision/->2dp (sut/convert 100.0 :litre :liter)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/milliliter options/millilitre)) + (precision/->2dp (sut/convert 100.0 :milliliter :milliliter)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/american-fluid-ounce options/american-fluid-ounce)) + (precision/->2dp (sut/convert 100.0 :american-fluid-ounce :american-fluid-ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/american-gallon options/american-gallon)) + (precision/->2dp (sut/convert 100.0 :american-gallon :american-gallon)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/american-pint options/american-pint)) + (precision/->2dp (sut/convert 100.0 :american-pint :american-pint)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/american-quart options/american-quart)) + (precision/->2dp (sut/convert 100.0 :american-quart :american-quart)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/cup options/cup)) + (precision/->2dp (sut/convert 100.0 :cup :cup)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/imperial-fluid-ounce options/imperial-fluid-ounce)) + (precision/->2dp (sut/convert 100.0 :imperial-fluid-ounce :imperial-fluid-ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/imperial-gallon options/imperial-gallon)) + (precision/->2dp (sut/convert 100.0 :imperial-gallon :imperial-gallon)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/imperial-pint options/imperial-pint)) + (precision/->2dp (sut/convert 100.0 :imperial-pint :imperial-pint)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/imperial-quart options/imperial-quart)) + (precision/->2dp (sut/convert 100.0 :imperial-quart :imperial-quart)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/tablespoon options/tablespoon)) + (precision/->2dp (sut/convert 100.0 :tablespoon :tablespoon)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/teaspoon options/teaspoon)) + (precision/->2dp (sut/convert 100.0 :teaspoon :teaspoon)))) + (is (= 3381.41 (precision/->2dp (sut/convert 100.0 options/liter options/american-fluid-ounce)))) + (is (= 26.42 (precision/->2dp (sut/convert 100.0 options/liter options/american-gallon)))) + (is (= 211.34 (precision/->2dp (sut/convert 100.0 options/liter options/american-pint)))) + (is (= 105.67 (precision/->2dp (sut/convert 100.0 options/liter options/american-quart)))) + (is (= 422.68 (precision/->2dp (sut/convert 100.0 options/liter options/cup)))) + (is (= 3519.5 (precision/->2dp (sut/convert 100.0 options/liter options/imperial-fluid-ounce)))) + (is (= 22.0 (precision/->2dp (sut/convert 100.0 options/liter options/imperial-gallon)))) + (is (= 175.98 (precision/->2dp (sut/convert 100.0 options/liter options/imperial-pint)))) + (is (= 87.99 (precision/->2dp (sut/convert 100.0 options/liter options/imperial-quart)))) + (is (= 6762.79 (precision/->2dp (sut/convert 100.0 options/liter options/tablespoon)))) + (is (= 20288.42 (precision/->2dp (sut/convert 100.0 options/liter options/teaspoon)))) + (is (= 2.96 (precision/->2dp (sut/convert 100.0 options/american-fluid-ounce options/liter)))) + (is (= 378.54 (precision/->2dp (sut/convert 100.0 options/american-gallon options/liter)))) + (is (= 47.32 (precision/->2dp (sut/convert 100.0 options/american-pint options/liter)))) + (is (= 94.64 (precision/->2dp (sut/convert 100.0 options/american-quart options/liter)))) + (is (= 23.66 (precision/->2dp (sut/convert 100.0 options/cup options/liter)))) + (is (= 2.84 (precision/->2dp (sut/convert 100.0 options/imperial-fluid-ounce options/liter)))) + (is (= 454.61 (precision/->2dp (sut/convert 100.0 options/imperial-gallon options/liter)))) + (is (= 56.83 (precision/->2dp (sut/convert 100.0 options/imperial-pint options/liter)))) + (is (= 113.65 (precision/->2dp (sut/convert 100.0 options/imperial-quart options/liter)))) + (is (= 1.48 (precision/->2dp (sut/convert 100.0 options/tablespoon options/liter)))) + (is (= 0.49 (precision/->2dp (sut/convert 100.0 options/teaspoon options/liter))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 l" + (sut/display 1.5 :liter) + (sut/display 1.5 options/litre))) + (is (= "1.5 liter" + (sut/display 1.5 :liter {options/suffix options/full}) + (sut/display 1.5 options/liter {options/suffix options/full}))) + (is (= "1.5 fl oz" + (sut/display 1.5 :american-fluid-ounce) + (sut/display 1.5 options/american-fluid-ounce))) + (is (= "1.5 fluid ounce" + (sut/display 1.5 :american-fluid-ounce {options/suffix options/full}) + (sut/display 1.5 options/american-fluid-ounce {options/suffix options/full}))) + (is (= "1.5 gal" + (sut/display 1.5 :american-gallon) + (sut/display 1.5 options/american-gallon))) + (is (= "1.5 US gallon" + (sut/display 1.5 :american-gallon {options/suffix options/full}) + (sut/display 1.5 options/american-gallon {options/suffix options/full}))) + (is (= "1.5 pt" + (sut/display 1.5 :american-pint) + (sut/display 1.5 options/american-pint))) + (is (= "1.5 american pint" + (sut/display 1.5 :american-pint {options/suffix options/full}) + (sut/display 1.5 options/american-pint {options/suffix options/full}))) + (is (= "1.5 qt" + (sut/display 1.5 :american-quart) + (sut/display 1.5 options/american-quart))) + (is (= "1.5 american quart" + (sut/display 1.5 :american-quart {options/suffix options/full}) + (sut/display 1.5 options/american-quart {options/suffix options/full}))) + (is (= "1.5 c" + (sut/display 1.5 :cup) + (sut/display 1.5 options/cup))) + (is (= "1.5 cup" + (sut/display 1.5 :cup {options/suffix options/full}) + (sut/display 1.5 options/cup {options/suffix options/full}))) + (is (= "1.5 imp fl oz" + (sut/display 1.5 :imperial-fluid-ounce) + (sut/display 1.5 options/imperial-fluid-ounce))) + (is (= "1.5 imperial fluid ounce" + (sut/display 1.5 :imperial-fluid-ounce {options/suffix options/full}) + (sut/display 1.5 options/imperial-fluid-ounce {options/suffix options/full}))) + (is (= "1.5 gal" + (sut/display 1.5 :imperial-gallon) + (sut/display 1.5 options/imperial-gallon))) + (is (= "1.5 imperial gallon" + (sut/display 1.5 :imperial-gallon {options/suffix options/full}) + (sut/display 1.5 options/imperial-gallon {options/suffix options/full}))) + (is (= "1.5 pt" + (sut/display 1.5 :imperial-pint) + (sut/display 1.5 options/imperial-pint))) + (is (= "1.5 imperial pint" + (sut/display 1.5 :imperial-pint {options/suffix options/full}) + (sut/display 1.5 options/imperial-pint {options/suffix options/full}))) + (is (= "1.5 qt" + (sut/display 1.5 :imperial-quart) + (sut/display 1.5 options/imperial-quart))) + (is (= "1.5 imperial quart" + (sut/display 1.5 :imperial-quart {options/suffix options/full}) + (sut/display 1.5 options/imperial-quart {options/suffix options/full}))) + (is (= "1.5 tbsp" + (sut/display 1.5 :tablespoon) + (sut/display 1.5 options/tablespoon))) + (is (= "1.5 tablespoon" + (sut/display 1.5 :tablespoon {options/suffix options/full}) + (sut/display 1.5 options/tablespoon {options/suffix options/full}))) + (is (= "1.5 tsp" + (sut/display 1.5 :teaspoon) + (sut/display 1.5 options/teaspoon))) + (is (= "1.5 teaspoon" + (sut/display 1.5 :teaspoon {options/suffix options/full}) + (sut/display 1.5 options/teaspoon {options/suffix options/full})))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) diff --git a/test/brewtility/units/weight_test.cljc b/test/brewtility/units/weight_test.cljc new file mode 100644 index 0000000..72dbae3 --- /dev/null +++ b/test/brewtility/units/weight_test.cljc @@ -0,0 +1,96 @@ +(ns brewtility.units.weight-test + (:require #? (:clj [clojure.test :refer [deftest is testing]]) + #? (:cljs [cljs.test :refer-macros [deftest is testing]]) + [brewtility.precision :as precision] + [brewtility.units.options :as options] + [brewtility.units.weight :as sut])) + + +(deftest code-type-tests + (testing "Ensure maps used for options are structurally correct" + (testing "Measurement types" + (is (set? sut/measurements)) + (is (every? keyword? sut/measurements)) + (is (not-empty sut/measurements))) + (testing "Measurement display names" + (is (map? sut/measurements->display-name)) + (is (not-empty sut/measurements->display-name)) + (is (every? keyword? (keys sut/measurements->display-name))) + (is (every? map? (vals sut/measurements->display-name))) + (is (every? #(contains? % options/full) (vals sut/measurements->display-name))) + (is (every? #(contains? % options/short) (vals sut/measurements->display-name)))) + (testing "measurement->kilogram" + (is (map? sut/measurement->kilogram)) + (is (not-empty sut/measurement->kilogram)) + (is (every? keyword? (keys sut/measurement->kilogram))) + (is (every? number? (vals sut/measurement->kilogram)))) + (is (= (set (keys sut/measurement->kilogram)) + (set (keys sut/measurements->display-name)) + sut/measurements)))) + + +(deftest conversion-test + (testing "Ensure various color unit conversions behave as expected" + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/gram options/gram)) + (precision/->2dp (sut/convert 100.0 :gram :gram)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/kilogram options/kilogram)) + (precision/->2dp (sut/convert 100.0 :kilogram :kilogram)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/milligram options/milligram)) + (precision/->2dp (sut/convert 100.0 :milligram :milligram)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/ounce options/ounce)) + (precision/->2dp (sut/convert 100.0 :ounce :ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert 100.0 options/pound options/pound)) + (precision/->2dp (sut/convert 100.0 :pound :pound)))) + (is (= 0.1 (precision/->2dp (sut/convert 100.0 options/gram options/kilogram)))) + (is (= 100000.0 (precision/->2dp (sut/convert 100.0 options/gram options/milligram)))) + (is (= 3.53 (precision/->2dp (sut/convert 100.0 options/gram options/ounce)))) + (is (= 0.22 (precision/->2dp (sut/convert 100.0 options/gram options/pound)))) + (is (= 100000.0 (precision/->2dp (sut/convert 100.0 options/kilogram options/gram)))) + (is (= 0.1 (precision/->2dp (sut/convert 100.0 options/milligram options/gram)))) + (is (= 2834.95 (precision/->2dp (sut/convert 100.0 options/ounce options/gram)))) + (is (= 45359.24 (precision/->2dp (sut/convert 100.0 options/pound options/gram))))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Ensure various color unit conversions behave as expected" + (is (= "1.5 g" + (sut/display 1.5 :gram) + (sut/display 1.5 options/gram))) + (is (= "1.5 kg" + (sut/display 1.5 :kilogram) + (sut/display 1.5 options/kilogram))) + (is (= "1.5 mg" + (sut/display 1.5 :milligram) + (sut/display 1.5 options/milligram))) + (is (= "1.5 oz" + (sut/display 1.5 :ounce) + (sut/display 1.5 options/ounce))) + (is (= "1.5 lb" + (sut/display 1.5 :pound) + (sut/display 1.5 options/pound))) + (is (= "1.5 gram" + (sut/display 1.5 :gram {options/suffix options/full}) + (sut/display 1.5 options/gram {options/suffix options/full}))) + (is (= "1.5 kilogram" + (sut/display 1.5 :kilogram {options/suffix options/full}) + (sut/display 1.5 options/kilogram {options/suffix options/full}))) + (is (= "1.5 milligram" + (sut/display 1.5 :milligram {options/suffix options/full}) + (sut/display 1.5 options/milligram {options/suffix options/full}))) + (is (= "1.5 ounce" + (sut/display 1.5 :ounce {options/suffix options/full}) + (sut/display 1.5 options/ounce {options/suffix options/full}))) + (is (= "1.5 pound" + (sut/display 1.5 :pound {options/suffix options/full}) + (sut/display 1.5 options/pound {options/suffix options/full})))) + (testing "Invalid options throw an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display 10.0 :invalid)))))) diff --git a/test/brewtility/units_test.cljc b/test/brewtility/units_test.cljc index 78cb950..daeb27d 100644 --- a/test/brewtility/units_test.cljc +++ b/test/brewtility/units_test.cljc @@ -1,43 +1,46 @@ (ns brewtility.units-test (:require #? (:clj [clojure.test :refer [deftest is testing]]) #? (:cljs [cljs.test :refer-macros [deftest is testing]]) - [brewtility.precision :as bp] + [brewtility.precision :as precision] [brewtility.units :as sut] [brewtility.units.color :as color] [brewtility.units.options :as options])) +;; These test cases existed prior to version 2.0.0 of brewtility.units +;; The tests directly against `convert` are more robust +;; These tests are left in place to ensure the new functions behave as expected (deftest convert-volume-test - (testing "Volume conversions should behave as expected" + (testing "Volume conversions should behave as expected." (is (= 1.0 (sut/convert :volume 1.0 options/liter options/litre))) - (is (= 1.0 (sut/convert :volume 1.0 options/litre options/litre))) - (is (= 0.099 (bp/->3dp (sut/convert :volume 20 options/teaspoon options/liter)))) - (is (= 35.1 (bp/->3dp (sut/convert :volume 35.1 options/tablespoon options/tablespoon)))) - (is (= 12.0 (bp/->3dp (sut/convert :volume 9.99209 options/imperial-pint options/american-pint)))))) + (is (= 1.0 (sut/convert options/volume 1.0 options/litre options/litre))) + (is (= 0.099 (precision/->3dp (sut/convert :volume 20 options/teaspoon options/liter)))) + (is (= 35.1 (precision/->3dp (sut/convert :volume 35.1 options/tablespoon options/tablespoon)))) + (is (= 12.0 (precision/->3dp (sut/convert :volume 9.99209 options/imperial-pint options/american-pint)))))) (deftest convert-weight-test - (testing "Weight conversions should behave as expected" + (testing "Weight conversions should behave as expected." (is (= 1.0 (sut/convert :weight 1.0 options/kilogram options/kilogram))) - (is (= 1.0 (sut/convert :weight 1.0 options/pound options/pound))) - (is (= 240.0 (bp/->3dp (sut/convert :weight 15.0 options/pound options/ounce)))) - (is (= 0.003 (bp/->3dp (sut/convert :weight 1205.5 options/milligram options/pound)))))) + (is (= 1.0 (sut/convert options/weight 1.0 options/pound options/pound))) + (is (= 240.0 (precision/->3dp (sut/convert :weight 15.0 options/pound options/ounce)))) + (is (= 0.003 (precision/->3dp (sut/convert :weight 1205.5 options/milligram options/pound)))))) (deftest convert-temperature-test - (testing "Temperature conversions should behave as expected" + (testing "Temperature conversions should behave as expected." (is (= 32.0 (sut/convert :temperature 32.0 options/fahrenheit options/fahrenheit))) - (is (= 32.0 (sut/convert :temperature 0.0 options/celsius options/fahrenheit))) - (is (= 340.706 (bp/->3dp (sut/convert :temperature 153.6008 options/fahrenheit options/kelvin)))) - (is (= 1743.15 (bp/->3dp (sut/convert :temperature 2016.3 options/kelvin options/c)))) - (is (= -40.0 (bp/->3dp (sut/convert :temperature -40.0 options/f options/c)))))) + (is (= 32.0 (sut/convert options/temperature 0.0 options/celsius options/fahrenheit))) + (is (= 340.706 (precision/->3dp (sut/convert :temperature 153.6008 options/fahrenheit options/kelvin)))) + (is (= 1743.15 (precision/->3dp (sut/convert :temperature 2016.3 options/kelvin options/c)))) + (is (= -40.0 (precision/->3dp (sut/convert :temperature -40.0 options/f options/c)))))) (deftest convert-color-test - (testing "Color conversions behaves as expected" + (testing "Color conversions behaves as expected." (is (= color/srm-1 (sut/convert :color 1 options/srm options/rgba))) (is (= color/srm-1 (sut/convert :color 1 :srm :rgba))) - (is (= color/srm-13 (sut/convert :color 10.16 options/lovibond options/rgba))) + (is (= color/srm-13 (sut/convert options/color 10.16 options/lovibond options/rgba))) (is (= color/srm-13 (sut/convert :color 10.16 :lovibond :rgba))) (is (= color/srm-13 (sut/convert :color 25.61 options/ebc options/rgba))) (is (= 13.00988 (sut/convert :color 25.61 options/ebc options/srm))) @@ -46,3 +49,380 @@ (is (= 10.16527388158866 (sut/convert :color 25.61 options/ebc options/lovibond))) (is (= 66.84467282 (sut/convert :color 25.61 options/lovibond options/ebc))) (is (= color/srm-13 (sut/convert :color 25.61 :ebc :rgba))))) + + +(deftest convert-test + (testing "Supported systems can convert all units." + (testing "Color:" + (is (= 10.0 + (precision/->2dp (sut/convert options/color 7.94 options/lovibond options/srm)) + (precision/->2dp (sut/convert options/color 19.69 options/ebc options/srm)))) + (is (= 10.0 + (precision/->2dp (sut/convert options/color 7.94 :lovibond :srm)) + (precision/->2dp (sut/convert options/color 19.69 :ebc :srm)))) + (is (= 23.2 + (precision/->2dp (sut/convert options/color 30.66 options/srm options/lovibond)) + (precision/->2dp (sut/convert options/color 60.38 options/ebc options/lovibond)))) + (is (= -45.53 + (precision/->2dp (sut/convert options/color -23.11 options/srm options/ebc)) + (precision/->2dp (sut/convert options/color -16.5 options/lovibond options/ebc))))) + (testing "Pressure:" + (is (= 100.0 + (precision/->2dp (sut/convert options/pressure 100.0 options/pascal options/pascal)) + (precision/->2dp (sut/convert options/pressure 100.0 :pascal :pascal)))) + (is (= 100000.0 (precision/->2dp (sut/convert options/pressure 100.0 options/kilopascal options/pascal)))) + (is (= 1.0 (precision/->2dp (sut/convert options/pressure 100.0 options/kilopascal options/bar)))) + (is (= 0.99 (precision/->2dp (sut/convert options/pressure 100.0 options/kilopascal options/atmosphere)))) + (is (= 750.06 (precision/->2dp (sut/convert options/pressure 100.0 options/kilopascal options/torr)))) + (is (= 14.5 (precision/->2dp (sut/convert options/pressure 100.0 options/kilopascal options/psi)))) + (is (= 0.1 (precision/->2dp (sut/convert options/pressure 100.0 options/pascal options/kilopascal)))) + (is (= 10000.0 (precision/->2dp (sut/convert options/pressure 100.0 options/bar options/kilopascal)))) + (is (= 10132.5 (precision/->2dp (sut/convert options/pressure 100.0 options/atmosphere options/kilopascal)))) + (is (= 13.33 (precision/->2dp (sut/convert options/pressure 100.0 options/torr options/kilopascal)))) + (is (= 689.48 (precision/->2dp (sut/convert options/pressure 100.0 options/psi options/kilopascal))))) + (testing "Specific Gravity:" + (is (= 100.0 + (precision/->2dp (sut/convert options/specific-gravity 100.0 options/specific-gravity options/specific-gravity)) + (precision/->2dp (sut/convert options/specific-gravity 100.0 :specific-gravity :specific-gravity))))) + (testing "Temperature:" + (is (= 100.0 + (precision/->2dp (sut/convert options/temperature 100.0 options/c options/c)) + (precision/->2dp (sut/convert options/temperature 100.0 :celsius :c)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/temperature 100.0 options/f options/fahrenheit)) + (precision/->2dp (sut/convert options/temperature 100.0 :f :f)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/temperature 100.0 options/k options/kelvin)) + (precision/->2dp (sut/convert options/temperature 100.0 :k :k)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/temperature 100.0 options/centigrade options/centigrade)) + (precision/->2dp (sut/convert options/temperature 100.0 :centigrade :centigrade)))) + (is (= 212.0 (precision/->2dp (sut/convert options/temperature 100 options/celsius options/fahrenheit)))) + (is (= 363.15 (precision/->2dp (sut/convert options/temperature 90 options/celsius options/kelvin)))) + (is (= 100.0 (precision/->2dp (sut/convert options/temperature 100.0 options/celsius options/centigrade)))) + (is (= 32.22 (precision/->2dp (sut/convert options/temperature 90 options/fahrenheit options/celsius)))) + (is (= 305.37 (precision/->2dp (sut/convert options/temperature 90 options/fahrenheit options/kelvin)))) + (is (= 32.22 (precision/->2dp (sut/convert options/temperature 90 options/fahrenheit options/centigrade)))) + (is (= -173.15 (precision/->2dp (sut/convert options/temperature 100 options/kelvin options/celsius)))) + (is (= -297.67 (precision/->2dp (sut/convert options/temperature 90 options/kelvin options/fahrenheit)))) + (is (= -183.15 (precision/->2dp (sut/convert options/temperature 90 options/kelvin options/centigrade)))) + (is (= 100.0 (precision/->2dp (sut/convert options/temperature 100 options/centigrade options/celsius)))) + (is (= 212.0 (precision/->2dp (sut/convert options/temperature 100 options/centigrade options/fahrenheit)))) + (is (= 373.15 (precision/->2dp (sut/convert options/temperature 100 options/centigrade options/kelvin))))) + (testing "Time:" + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/minute options/minute)) + (precision/->2dp (sut/convert options/time 100.0 :minute :minute)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/nanosecond options/nanosecond)) + (precision/->2dp (sut/convert options/time 100.0 :nanosecond :nanosecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/second options/second)) + (precision/->2dp (sut/convert options/time 100.0 :second :second)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/millisecond options/millisecond)) + (precision/->2dp (sut/convert options/time 100.0 :millisecond :millisecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/microsecond options/microsecond)) + (precision/->2dp (sut/convert options/time 100.0 :microsecond :microsecond)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/day options/day)) + (precision/->2dp (sut/convert options/time 100.0 :day :day)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/hour options/hour)) + (precision/->2dp (sut/convert options/time 100.0 :hour :hour)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/time 100.0 options/week options/week)) + (precision/->2dp (sut/convert options/time 100.0 :week :week)))) + (is (= 6000.0 (precision/->2dp (sut/convert options/time 100.0 options/minute options/second)))) + (is (= 6000000.0 (precision/->2dp (sut/convert options/time 100.0 options/minute options/millisecond)))) + (is (= 60000.0 (precision/->2dp (sut/convert options/time 0.001 options/minute options/microsecond)))) + (is (= 600000.0 (precision/->2dp (sut/convert options/time 0.00001 options/minute options/nanosecond)))) + (is (= 0.07 (precision/->2dp (sut/convert options/time 100.0 options/minute options/day)))) + (is (= 1.67 (precision/->2dp (sut/convert options/time 100.0 options/minute options/hour)))) + (is (= 0.01 (precision/->2dp (sut/convert options/time 100.0 options/minute options/week)))) + (is (= 1.67 (precision/->2dp (sut/convert options/time 100.0 options/second options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert options/time 100.0 options/millisecond options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert options/time 100.0 options/microsecond options/minute)))) + (is (= 0.0 (precision/->2dp (sut/convert options/time 100.0 options/nanosecond options/minute)))) + (is (= 144000.0 (precision/->2dp (sut/convert options/time 100.0 options/day options/minute)))) + (is (= 6000.0 (precision/->2dp (sut/convert options/time 100.0 options/hour options/minute)))) + (is (= 1008000.0 (precision/->2dp (sut/convert options/time 100.0 options/week options/minute))))) + (testing "Volume:" + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/liter options/litre)) + (precision/->2dp (sut/convert options/volume 100.0 :litre :liter)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/milliliter options/millilitre)) + (precision/->2dp (sut/convert options/volume 100.0 :milliliter :milliliter)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/american-fluid-ounce options/american-fluid-ounce)) + (precision/->2dp (sut/convert options/volume 100.0 :american-fluid-ounce :american-fluid-ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/american-gallon options/american-gallon)) + (precision/->2dp (sut/convert options/volume 100.0 :american-gallon :american-gallon)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/american-pint options/american-pint)) + (precision/->2dp (sut/convert options/volume 100.0 :american-pint :american-pint)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/american-quart options/american-quart)) + (precision/->2dp (sut/convert options/volume 100.0 :american-quart :american-quart)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/cup options/cup)) + (precision/->2dp (sut/convert options/volume 100.0 :cup :cup)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/imperial-fluid-ounce options/imperial-fluid-ounce)) + (precision/->2dp (sut/convert options/volume 100.0 :imperial-fluid-ounce :imperial-fluid-ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/imperial-gallon options/imperial-gallon)) + (precision/->2dp (sut/convert options/volume 100.0 :imperial-gallon :imperial-gallon)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/imperial-pint options/imperial-pint)) + (precision/->2dp (sut/convert options/volume 100.0 :imperial-pint :imperial-pint)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/imperial-quart options/imperial-quart)) + (precision/->2dp (sut/convert options/volume 100.0 :imperial-quart :imperial-quart)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/tablespoon options/tablespoon)) + (precision/->2dp (sut/convert options/volume 100.0 :tablespoon :tablespoon)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/volume 100.0 options/teaspoon options/teaspoon)) + (precision/->2dp (sut/convert options/volume 100.0 :teaspoon :teaspoon)))) + (is (= 3381.41 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/american-fluid-ounce)))) + (is (= 26.42 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/american-gallon)))) + (is (= 211.34 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/american-pint)))) + (is (= 105.67 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/american-quart)))) + (is (= 422.68 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/cup)))) + (is (= 3519.5 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/imperial-fluid-ounce)))) + (is (= 22.0 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/imperial-gallon)))) + (is (= 175.98 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/imperial-pint)))) + (is (= 87.99 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/imperial-quart)))) + (is (= 6762.79 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/tablespoon)))) + (is (= 20288.42 (precision/->2dp (sut/convert options/volume 100.0 options/liter options/teaspoon)))) + (is (= 2.96 (precision/->2dp (sut/convert options/volume 100.0 options/american-fluid-ounce options/liter)))) + (is (= 378.54 (precision/->2dp (sut/convert options/volume 100.0 options/american-gallon options/liter)))) + (is (= 47.32 (precision/->2dp (sut/convert options/volume 100.0 options/american-pint options/liter)))) + (is (= 94.64 (precision/->2dp (sut/convert options/volume 100.0 options/american-quart options/liter)))) + (is (= 23.66 (precision/->2dp (sut/convert options/volume 100.0 options/cup options/liter)))) + (is (= 2.84 (precision/->2dp (sut/convert options/volume 100.0 options/imperial-fluid-ounce options/liter)))) + (is (= 454.61 (precision/->2dp (sut/convert options/volume 100.0 options/imperial-gallon options/liter)))) + (is (= 56.83 (precision/->2dp (sut/convert options/volume 100.0 options/imperial-pint options/liter)))) + (is (= 113.65 (precision/->2dp (sut/convert options/volume 100.0 options/imperial-quart options/liter)))) + (is (= 1.48 (precision/->2dp (sut/convert options/volume 100.0 options/tablespoon options/liter)))) + (is (= 0.49 (precision/->2dp (sut/convert options/volume 100.0 options/teaspoon options/liter))))) + (testing "Weight:" + (is (= 100.0 + (precision/->2dp (sut/convert options/weight 100.0 options/gram options/gram)) + (precision/->2dp (sut/convert options/weight 100.0 :gram :gram)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/weight 100.0 options/kilogram options/kilogram)) + (precision/->2dp (sut/convert options/weight 100.0 :kilogram :kilogram)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/weight 100.0 options/milligram options/milligram)) + (precision/->2dp (sut/convert options/weight 100.0 :milligram :milligram)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/weight 100.0 options/ounce options/ounce)) + (precision/->2dp (sut/convert options/weight 100.0 :ounce :ounce)))) + (is (= 100.0 + (precision/->2dp (sut/convert options/weight 100.0 options/pound options/pound)) + (precision/->2dp (sut/convert options/weight 100.0 :pound :pound)))) + (is (= 0.1 (precision/->2dp (sut/convert options/weight 100.0 options/gram options/kilogram)))) + (is (= 100000.0 (precision/->2dp (sut/convert options/weight 100.0 options/gram options/milligram)))) + (is (= 3.53 (precision/->2dp (sut/convert options/weight 100.0 options/gram options/ounce)))) + (is (= 0.22 (precision/->2dp (sut/convert options/weight 100.0 options/gram options/pound)))) + (is (= 100000.0 (precision/->2dp (sut/convert options/weight 100.0 options/kilogram options/gram)))) + (is (= 0.1 (precision/->2dp (sut/convert options/weight 100.0 options/milligram options/gram)))) + (is (= 2834.95 (precision/->2dp (sut/convert options/weight 100.0 options/ounce options/gram)))) + (is (= 45359.24 (precision/->2dp (sut/convert options/weight 100.0 options/pound options/gram)))))) + (testing "Invalid options generate an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/convert :ope 10.0 :invalid :broken)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/convert :mistake 10.0 :invalid :broken)))))) + + +(deftest display-test + (testing "Supported systems can cast display values" + (testing "Color:" + (is (= "10.0 srm" + (sut/display options/color 10.0 options/srm) + (sut/display options/color 10.0 :srm))) + (is (= "23.2 ebc" + (sut/display options/color 23.2 options/ebc) + (sut/display options/color 23.2 :ebc))) + (is (= "10.0 degrees lovibond" + (sut/display options/color 10.0 options/lovibond {options/suffix options/full}) + (sut/display options/color 10.0 :lovibond {options/suffix options/full}))) + (is (= color/srm-37 (sut/display options/color color/srm-37 options/rgba)))) + (testing "Pressure:" + (is (= "1.5 pa" + (sut/display options/pressure 1.5 :pascal) + (sut/display options/pressure 1.5 options/pascal))) + (is (= "1.5 pascal" + (sut/display options/pressure 1.5 :pascal {options/suffix options/full})))) + (testing "Specific Gravity:" + (is (= "1.5 sg" + (sut/display options/specific-gravity 1.5 :specific-gravity) + (sut/display options/specific-gravity 1.5 options/specific-gravity)))) + (testing "Temperature:" + (is (= "1.5 c" + (sut/display options/temperature 1.5 :celsius) + (sut/display options/temperature 1.5 options/celsius) + (sut/display options/temperature 1.5 options/c))) + (is (= "1.5 k" + (sut/display options/temperature 1.5 :kelvin) + (sut/display options/temperature 1.5 options/kelvin) + (sut/display options/temperature 1.5 options/k))) + (is (= "1.5 f" + (sut/display options/temperature 1.5 :fahrenheit) + (sut/display options/temperature 1.5 options/fahrenheit) + (sut/display options/temperature 1.5 options/f))) + (is (= "1.5 mg" + (sut/display options/temperature 1.5 options/centigrade))) + (is (= "1.5 celsius" + (sut/display options/temperature 1.5 :celsius {options/suffix options/full}) + (sut/display options/temperature 1.5 options/celsius {options/suffix options/full}) + (sut/display options/temperature 1.5 options/c {options/suffix options/full}))) + (is (= "1.5 kelvin" + (sut/display options/temperature 1.5 :kelvin {options/suffix options/full}) + (sut/display options/temperature 1.5 options/kelvin {options/suffix options/full}) + (sut/display options/temperature 1.5 options/k {options/suffix options/full}))) + (is (= "1.5 fahrenheit" + (sut/display options/temperature 1.5 :fahrenheit {options/suffix options/full}) + (sut/display options/temperature 1.5 options/fahrenheit {options/suffix options/full}) + (sut/display options/temperature 1.5 options/f {options/suffix options/full}))) + (is (= "1.5 centigrade" + (sut/display options/temperature 1.5 options/centigrade {options/suffix options/full})))) + (testing "Time:" + (is (= "1.5 m" + (sut/display options/time 1.5 :minute) + (sut/display options/time 1.5 options/minute))) + (is (= "1.5 d" + (sut/display options/time 1.5 :day) + (sut/display options/time 1.5 options/day))) + (is (= "1.5 hr" + (sut/display options/time 1.5 :hour) + (sut/display options/time 1.5 options/hour))) + (is (= "1.5 w" + (sut/display options/time 1.5 :week) + (sut/display options/time 1.5 options/week))) + (is (= "1.5 ns" + (sut/display options/time 1.5 :nanosecond) + (sut/display options/time 1.5 options/nanosecond))) + (is (= "1.5 ms" + (sut/display options/time 1.5 :millisecond) + (sut/display options/time 1.5 options/millisecond))) + (is (= "1.5 µs" + (sut/display options/time 1.5 :microsecond) + (sut/display options/time 1.5 options/microsecond))) + (is (= "1.5 s" + (sut/display options/time 1.5 :second) + (sut/display options/time 1.5 options/second))) + (is (= "1.5 minute" + (sut/display options/time 1.5 :minute {options/suffix options/full}) + (sut/display options/time 1.5 options/minute {options/suffix options/full})))) + (testing "Volume:" + (is (= "1.5 l" + (sut/display options/volume 1.5 :liter) + (sut/display options/volume 1.5 options/litre))) + (is (= "1.5 liter" + (sut/display options/volume 1.5 :liter {options/suffix options/full}) + (sut/display options/volume 1.5 options/liter {options/suffix options/full}))) + (is (= "1.5 fl oz" + (sut/display options/volume 1.5 :american-fluid-ounce) + (sut/display options/volume 1.5 options/american-fluid-ounce))) + (is (= "1.5 fluid ounce" + (sut/display options/volume 1.5 :american-fluid-ounce {options/suffix options/full}) + (sut/display options/volume 1.5 options/american-fluid-ounce {options/suffix options/full}))) + (is (= "1.5 gal" + (sut/display options/volume 1.5 :american-gallon) + (sut/display options/volume 1.5 options/american-gallon))) + (is (= "1.5 US gallon" + (sut/display options/volume 1.5 :american-gallon {options/suffix options/full}) + (sut/display options/volume 1.5 options/american-gallon {options/suffix options/full}))) + (is (= "1.5 pt" + (sut/display options/volume 1.5 :american-pint) + (sut/display options/volume 1.5 options/american-pint))) + (is (= "1.5 american pint" + (sut/display options/volume 1.5 :american-pint {options/suffix options/full}) + (sut/display options/volume 1.5 options/american-pint {options/suffix options/full}))) + (is (= "1.5 qt" + (sut/display options/volume 1.5 :american-quart) + (sut/display options/volume 1.5 options/american-quart))) + (is (= "1.5 american quart" + (sut/display options/volume 1.5 :american-quart {options/suffix options/full}) + (sut/display options/volume 1.5 options/american-quart {options/suffix options/full}))) + (is (= "1.5 c" + (sut/display options/volume 1.5 :cup) + (sut/display options/volume 1.5 options/cup))) + (is (= "1.5 cup" + (sut/display options/volume 1.5 :cup {options/suffix options/full}) + (sut/display options/volume 1.5 options/cup {options/suffix options/full}))) + (is (= "1.5 imp fl oz" + (sut/display options/volume 1.5 :imperial-fluid-ounce) + (sut/display options/volume 1.5 options/imperial-fluid-ounce))) + (is (= "1.5 imperial fluid ounce" + (sut/display options/volume 1.5 :imperial-fluid-ounce {options/suffix options/full}) + (sut/display options/volume 1.5 options/imperial-fluid-ounce {options/suffix options/full}))) + (is (= "1.5 gal" + (sut/display options/volume 1.5 :imperial-gallon) + (sut/display options/volume 1.5 options/imperial-gallon))) + (is (= "1.5 imperial gallon" + (sut/display options/volume 1.5 :imperial-gallon {options/suffix options/full}) + (sut/display options/volume 1.5 options/imperial-gallon {options/suffix options/full}))) + (is (= "1.5 pt" + (sut/display options/volume 1.5 :imperial-pint) + (sut/display options/volume 1.5 options/imperial-pint))) + (is (= "1.5 imperial pint" + (sut/display options/volume 1.5 :imperial-pint {options/suffix options/full}) + (sut/display options/volume 1.5 options/imperial-pint {options/suffix options/full}))) + (is (= "1.5 qt" + (sut/display options/volume 1.5 :imperial-quart) + (sut/display options/volume 1.5 options/imperial-quart))) + (is (= "1.5 imperial quart" + (sut/display options/volume 1.5 :imperial-quart {options/suffix options/full}) + (sut/display options/volume 1.5 options/imperial-quart {options/suffix options/full}))) + (is (= "1.5 tbsp" + (sut/display options/volume 1.5 :tablespoon) + (sut/display options/volume 1.5 options/tablespoon))) + (is (= "1.5 tablespoon" + (sut/display options/volume 1.5 :tablespoon {options/suffix options/full}) + (sut/display options/volume 1.5 options/tablespoon {options/suffix options/full}))) + (is (= "1.5 tsp" + (sut/display options/volume 1.5 :teaspoon) + (sut/display options/volume 1.5 options/teaspoon))) + (is (= "1.5 teaspoon" + (sut/display options/volume 1.5 :teaspoon {options/suffix options/full}) + (sut/display options/volume 1.5 options/teaspoon {options/suffix options/full})))) + (testing "Weight:" + (is (= "1.5 g" + (sut/display options/weight 1.5 :gram) + (sut/display options/weight 1.5 options/gram))) + (is (= "1.5 kg" + (sut/display options/weight 1.5 :kilogram) + (sut/display options/weight 1.5 options/kilogram))) + (is (= "1.5 mg" + (sut/display options/weight 1.5 :milligram) + (sut/display options/weight 1.5 options/milligram))) + (is (= "1.5 oz" + (sut/display options/weight 1.5 :ounce) + (sut/display options/weight 1.5 options/ounce))) + (is (= "1.5 lb" + (sut/display options/weight 1.5 :pound) + (sut/display options/weight 1.5 options/pound))) + (is (= "1.5 gram" + (sut/display options/weight 1.5 :gram {options/suffix options/full}) + (sut/display options/weight 1.5 options/gram {options/suffix options/full}))) + (is (= "1.5 kilogram" + (sut/display options/weight 1.5 :kilogram {options/suffix options/full}) + (sut/display options/weight 1.5 options/kilogram {options/suffix options/full}))) + (is (= "1.5 milligram" + (sut/display options/weight 1.5 :milligram {options/suffix options/full}) + (sut/display options/weight 1.5 options/milligram {options/suffix options/full}))) + (is (= "1.5 ounce" + (sut/display options/weight 1.5 :ounce {options/suffix options/full}) + (sut/display options/weight 1.5 options/ounce {options/suffix options/full}))) + (is (= "1.5 pound" + (sut/display options/weight 1.5 :pound {options/suffix options/full}) + (sut/display options/weight 1.5 options/pound {options/suffix options/full}))))) + (testing "Invalid options generate an exception" + #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display :ope 10.0 :invalid)))) + #?(:cljs (is (thrown-with-msg? js/Error #"Unsupported" (sut/display :bad 10.0 :invalid)))))) From 60255683fb3eac134e8e73b9f0aaf88e5d3e96f7 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 17:42:25 +0000 Subject: [PATCH 06/11] Remove removed namespace --- test/brewtility/runner.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/brewtility/runner.cljs b/test/brewtility/runner.cljs index 507283c..d4e6855 100644 --- a/test/brewtility/runner.cljs +++ b/test/brewtility/runner.cljs @@ -4,7 +4,6 @@ This namespace is responsible for running all of the ClojureScript tests. To add new test namespaces, add them to the `:require` and `doo-tests` clauses below." (:require [brewtility.calculations-test] - [brewtility.color-test] [brewtility.data.equipment] [brewtility.data.fermentables] [brewtility.data.hops] @@ -38,7 +37,6 @@ (doo-tests 'brewtility.calculations-test - 'brewtility.color-test 'brewtility.data.equipment 'brewtility.data.fermentables 'brewtility.data.hops From 0ba55fb39e3bb89484bddf0324782f4711c344a7 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 17:52:11 +0000 Subject: [PATCH 07/11] Fix clojurescript tests --- test/brewtility/units/color_test.cljc | 6 +++--- test/brewtility/units_test.cljc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/brewtility/units/color_test.cljc b/test/brewtility/units/color_test.cljc index 52f22b4..c1b6fb9 100644 --- a/test/brewtility/units/color_test.cljc +++ b/test/brewtility/units/color_test.cljc @@ -99,9 +99,9 @@ (deftest display-test (testing "Ensure various color unit conversions behave as expected" - (is (= "10.0 srm" - (sut/display 10.0 options/srm) - (sut/display 10.0 :srm))) + (is (= "10.1 srm" + (sut/display 10.1 options/srm) + (sut/display 10.1 :srm))) (is (= "23.2 ebc" (sut/display 23.2 options/ebc) (sut/display 23.2 :ebc))) diff --git a/test/brewtility/units_test.cljc b/test/brewtility/units_test.cljc index daeb27d..a29a4d7 100644 --- a/test/brewtility/units_test.cljc +++ b/test/brewtility/units_test.cljc @@ -242,9 +242,9 @@ (deftest display-test (testing "Supported systems can cast display values" (testing "Color:" - (is (= "10.0 srm" - (sut/display options/color 10.0 options/srm) - (sut/display options/color 10.0 :srm))) + (is (= "10.1 srm" + (sut/display options/color 10.1 options/srm) + (sut/display options/color 10.1 :srm))) (is (= "23.2 ebc" (sut/display options/color 23.2 options/ebc) (sut/display options/color 23.2 :ebc))) From f0e6de1310941f34340f0c2d09cad0b9b969ed2f Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 17:55:13 +0000 Subject: [PATCH 08/11] Fix clojurescript tests --- test/brewtility/units/color_test.cljc | 6 +++--- test/brewtility/units_test.cljc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/brewtility/units/color_test.cljc b/test/brewtility/units/color_test.cljc index c1b6fb9..de39d4e 100644 --- a/test/brewtility/units/color_test.cljc +++ b/test/brewtility/units/color_test.cljc @@ -105,9 +105,9 @@ (is (= "23.2 ebc" (sut/display 23.2 options/ebc) (sut/display 23.2 :ebc))) - (is (= "10.0 degrees lovibond" - (sut/display 10.0 options/lovibond {options/suffix options/full}) - (sut/display 10.0 :lovibond {options/suffix options/full}))) + (is (= "10.1 degrees lovibond" + (sut/display 10.1 options/lovibond {options/suffix options/full}) + (sut/display 10.1 :lovibond {options/suffix options/full}))) (is (= sut/srm-37 (sut/display sut/srm-37 options/rgba)))) (testing "Invalid options throw an exception" #?(:clj (is (thrown-with-msg? Exception #"Unsupported" (sut/display 10.0 :invalid)))) diff --git a/test/brewtility/units_test.cljc b/test/brewtility/units_test.cljc index a29a4d7..991a91e 100644 --- a/test/brewtility/units_test.cljc +++ b/test/brewtility/units_test.cljc @@ -248,9 +248,9 @@ (is (= "23.2 ebc" (sut/display options/color 23.2 options/ebc) (sut/display options/color 23.2 :ebc))) - (is (= "10.0 degrees lovibond" - (sut/display options/color 10.0 options/lovibond {options/suffix options/full}) - (sut/display options/color 10.0 :lovibond {options/suffix options/full}))) + (is (= "10.1 degrees lovibond" + (sut/display options/color 10.1 options/lovibond {options/suffix options/full}) + (sut/display options/color 10.1 :lovibond {options/suffix options/full}))) (is (= color/srm-37 (sut/display options/color color/srm-37 options/rgba)))) (testing "Pressure:" (is (= "1.5 pa" From 72d20aa8adcc5bbe96e730f00fb43cf536d5b415 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 17:56:51 +0000 Subject: [PATCH 09/11] Add extra precision tests --- test/brewtility/precision_test.cljc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/brewtility/precision_test.cljc b/test/brewtility/precision_test.cljc index 948e18e..e0146b3 100644 --- a/test/brewtility/precision_test.cljc +++ b/test/brewtility/precision_test.cljc @@ -16,6 +16,7 @@ (is (= 0.0 (sut/->1dp 0.0))) (is (= 0.0 (sut/->1dp 0.001))) (is (= 5.1 (sut/->1dp 5.05))) + (is (= 5.0 (sut/->1dp 5.04))) (is (= -12.3 (sut/->1dp -12.3))))) @@ -24,6 +25,7 @@ (is (= 0.00 (sut/->2dp 0.0))) (is (= 0.00 (sut/->2dp 0.001))) (is (= 5.05 (sut/->2dp 5.0513))) + (is (= 5.06 (sut/->2dp 5.0553))) (is (= -12.30 (sut/->2dp -12.3))) (is (= 100.01 (sut/->2dp 100.005))))) @@ -33,6 +35,9 @@ (is (= 0.000 (sut/->3dp 0.0))) (is (= 0.001 (sut/->3dp 0.001))) (is (= 5.050 (sut/->3dp 5.05))) + (is (= 5.055 (sut/->3dp 5.055))) + (is (= 5.055 (sut/->3dp 5.0554))) + (is (= 5.056 (sut/->3dp 5.0555))) (is (= -12.300 (sut/->3dp -12.3))) (is (= 100.005 (sut/->3dp 100.005))) (is (= 404.001 (sut/->3dp 404.0009654))))) From 1e9b875c50a61b04b3711da1cee211f836ab8662 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 18:43:27 +0000 Subject: [PATCH 10/11] Update documentation --- README.md | 8 +- doc/api/color.md | 45 ------- doc/api/units.md | 197 +++++++++++++++++++++--------- doc/cljdoc.edn | 7 +- doc/patterns/symbolic_keywords.md | 51 ++++++++ 5 files changed, 199 insertions(+), 109 deletions(-) delete mode 100644 doc/api/color.md create mode 100644 doc/patterns/symbolic_keywords.md diff --git a/README.md b/README.md index 4d59195..d93f3ba 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,18 @@ Below are examples of provided functionality for each namespace. The library does provide access to other functions, but those primarily exist in support of those outlined here. - [Calculations](doc/api/calculations.md) -- [Color](doc/api/color.md) - [Precision](doc/api/precision.md) - [Predicates](doc/api/predicates.md) - [Units of Measure](doc/api/units.md) - [Wrapping](doc/api/wrapping.md) +## Common Patterns + +Brewtility follows several conventions and design patterns that result in artifacts available to library consumers. +These are not strictly required for use; however, they provide additional documentation and ease-of-use to those who adopt them. + +- [Symbolic Keywords](doc/patterns/symbolic_keywords.md) + ## Testing [doo](https://github.com/bensu/doo), a Leiningen plugin used to run ClojureScript tests in many JS environments, is already in `project.clj`. diff --git a/doc/api/color.md b/doc/api/color.md deleted file mode 100644 index 00fa132..0000000 --- a/doc/api/color.md +++ /dev/null @@ -1,45 +0,0 @@ -# Color - -The color of a beer is derived from the color of the fermentables used in the mash. -Brewtility currently supports conversions between three color systems: - -* [Standard Reference Method (SRM)](https://en.wikipedia.org/wiki/Standard_Reference_Method) -* [European Brewery Convention (EBC)](https://en.wikipedia.org/wiki/European_Brewery_Convention) -* [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) - -Additionally, brewtility maintains functionality to render these color systems as RGBa color codes. - -## Color Unit Conversion - -Given a color measurement in one of the three most common systems, convert between each system of measure. - -```clj -(:require [brewtility.color :refer :all]) - -(lovibond->srm 7.94) -;; => 10.0 - -(srm->ebc 23.1) -;; => 45.51 - -(ebc->lovibond 60.38) -;; => 23.2 -``` - -## Display Color Lookup - -Given a color measurement in one of the three most common systems, look up a corresponding RGBa color. -These functions are opinionated, and support and normalize down to the most common SRM scale: 1-40. - -```clj -(:require [brewtility.color :refer :all]) - -(lovibond->rgba 0.56) -;; => "rgba(255,230,153,1)" - -(srm->rgba 6.2) -;; => "rgba(248,166,0,1)" - -(ebc->rgba 25.61) -;; => "rgba(203,98,0,1)" -``` diff --git a/doc/api/units.md b/doc/api/units.md index b417c2f..5416bf3 100644 --- a/doc/api/units.md +++ b/doc/api/units.md @@ -1,94 +1,169 @@ # Units of Measure -Beer has become an international interest/ +Beer has become an international interest. Additionally, brewing spans across the commercial and home environments. As a result, the systems of measure and scales of measure can very greatly between brewers. The BeerXML spec clearly delineates what units of measure are to be used to storing and transmitting data; however, they also allow users to pass `display` formats for values. These are used to translate uniform, machine-friendly systems into more appropriate systems and scales based on geographic region and user case. -## Systems of Measure +The `brewtility.units` namespace contains functions to convert between different systems and units of measure across several different types of measurement. +Additionally, this namespace may be used to render common display formats. -Brewtility supports four systems of measure: +The names of these systems and units are frequently used in code, and shorthand symbolic references to these names may be found in `brewtility.units.options` -- [The British Imperial System](https://en.wikipedia.org/wiki/Imperial_units) -- [The Metric System](https://en.wikipedia.org/wiki/Metric_system) -- [The International System](https://en.wikipedia.org/wiki/International_System_of_Units) -- [The US Customary Scale](https://en.wikipedia.org/wiki/United_States_customary_units) +## Basic Use -## Volume Conversion - -Given a `volume` in `source-measurement`, convert it to the `target-measurement`. -Supported measurements are: - -- Teaspoon -- Tablespoon -- Imperial Fluid Ounce -- American Fluid Ounce -- Cup -- Imperial Pint -- American Pint -- Imperial Quart -- American Quart -- Imperial Gallon -- American Gallon -- Litre (Or Liter, depending on regional convention) -- Millilitre (Or Milliliter, depending on regional convention) +The majority of the functionality belongs to the functions `convert` and `display`. +This provides a consistent interface to every system of measure, unit type, and more. ```clj -(:require [brewtility.units :refer :all]) +(:require [brewtility.units :as units] + [brewtility.units.options :as options] + [brewtility.units.volume :as volume]) -(convert-volume 1.0 :liter :litre) +;; You can use the keys in `brewtility.units.options` as arguments. +;; This guarantees you don't have typos in code, and can link to helpful documentation +(units/convert units/volume 1.0 options/liter options/litre) ;; => 1.0 -(convert-volume 20 :teaspoon :liter) +;; Or, if you prefer, bare keywords are accepted too +(units/convert units/volume 20 :teaspoon :liter) ;; => 0.099 -(convert-volume 9.99209 :imperial-pint :american-pint) +;; If you only plan on dealing with volumes, +;; then you can import the `brewtility.units.volume` namespace +(volume/convert 9.99209 :imperial-pint :american-pint) ;; => 12.0 + +;; You can also render display values +(units/display :volume 1.5 :liter) +;; => \"1.5 l\" + +;; The keys in `brewtility.units.options` are also acceptable +(units/display options/volume 1.5 options/liter) +;; => \"1.5 l\" + +;; You may supply additional options, such as the default precision for rounding +;; and the type of suffix to use +(units/display options/volume 1.45 options/liter {options/precision 1}) +;; => \"1.5 l\" + +(units/display options/volume 1.45 options/liter {options/suffix options/full}) +;; => \"1.45 liter\" + +;; Like `convert`, you can also call the measurement-type's functionality directly +(volume/display 1.45 options/liter {options/precision 1}) +;; => \"1.5 l\" + +;; And, of course, the symbolic keywords are ultimately plain keywords. +(volume/display 1.45 :liter {:suffix :full :precision 1}) +;; => \"1.5 liter\" ``` -## Weight Conversion +## Supported Systems -Given a `weight` in `source-measurement`, convert it to the `target-measurement`. -Supported measurements are: +Brewtility supports four systems of measure: -- Ounce -- Pound -- Milligram -- Gram -- Kilogram +- [The British Imperial System](https://en.wikipedia.org/wiki/Imperial_units) +- [The Metric System](https://en.wikipedia.org/wiki/Metric_system) +- [The International System](https://en.wikipedia.org/wiki/International_System_of_Units) +- [The US Customary Scale](https://en.wikipedia.org/wiki/United_States_customary_units) -```clj -(:require [brewtility.units :refer :all]) +These are the most commonly seen systems in brewing. +There are measurement functions for the most common types of measurements within these systems: -(convert-weight 1.0 :kilogram :kilogram) -;; => 1.0 +- [Color](##color) +- [Pressure](##pressure) +- [Specific Gravity](##specific-gravity) +- [Temperature](##temperature) +- [Time](##time) +- [Volume](##volume) +- [Weight](##weight) -(convert-weight 15.0 :pound :ounce) -;; => 240.0 +### Color -(convert-weight 1205.5 :milligram :pound) -;; => 0.003 -``` +Currently, brewtility supports the following types of color: -## Temperature Conversion +- [SRM](https://en.wikipedia.org/wiki/Standard_Reference_Method) +- [EBC](https://en.wikipedia.org/wiki/European_Brewery_Convention) +- [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour) +- [RGBa](https://en.wikipedia.org/wiki/RGBA_color_model) -Given a `temperature` in `source-measurement`, convert it to the `target-measurement`. -Supported measurements are: +The `RGBa` system is special, as it can only be used as an argument for the result of a unit conversion. +Unfortunately, there is not a great deterministic way to cast the values back to the other systems. +brewtility will thrown an exception in this case and explain the problem. -- Celsius (Or Centigrade, depending on regional convention. Or C, for convenience) -- Kelvin (Or K, for convenience) -- Fahrenheit (Or F, for convenience) +### Pressure -```clj -(:require [brewtility.units :refer :all]) +Currently, brewtility supports the following types of pressure: -(convert-temperature 32.0 :fahrenheit :fahrenheit) -;; => 32.0 +- [pascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) +- [kilopascal](https://en.wikipedia.org/wiki/Pascal_(unit)#Multiples_and_submultiples) +- [bar](https://en.wikipedia.org/wiki/Bar_(unit)) +- [atmosphere](https://en.wikipedia.org/wiki/Atmosphere_(unit)) +- [torr](https://en.wikipedia.org/wiki/Torr) +- [psi]( -(convert-temperature 0.0 :c :f) -;; => 32.0 +### Specific Gravity -(convert-temperature 2016.3 :kelvin :c) -;; => 1743.15 -``` +Currently, brewtility supports the following types of specific gravity: + +- [specific-gravity](https://en.wikipedia.org/wiki/Specific_gravity) + +While there is currently only one system, the same namespace and functionality exists as the other measurement types. +This allows for progressive evolution, and provides a consistent interface to every measurement type encoded in the BeerXML specification. + +### Temperature + +Currently, brewtility supports the following types of temperature measurements: + +- [clesius](https://en.wikipedia.org/wiki/Celsius) +- [fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) +- [kelvin](https://en.wikipedia.org/wiki/Kelvin_(unit)) + +Given the prevalence of shorthand names in temperature measurements, brewtility also accepts `c`, `k`, and `f`. + +### Time + +Currently, brewtility supports the following types of time measurements: + +- [microsecond](https://en.wikipedia.org/wiki/Microsecond) +- [nanosecond](https://en.wikipedia.org/wiki/Nanosecond) +- [millisecond](https://en.wikipedia.org/wiki/Millisecond) +- [second](https://en.wikipedia.org/wiki/Second) +- [minute](https://en.wikipedia.org/wiki/Minute) +- [hour](https://en.wikipedia.org/wiki/Hour) +- [day](https://en.wikipedia.org/wiki/Day) +- [week](https://en.wikipedia.org/wiki/Week) + +### Volume + +Currently, brewtility supports the following types of volume: + +- [american-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) +- [american-gallon](https://en.wikipedia.org/wiki/Gallon) +- [american-pint](https://en.wikipedia.org/wiki/Pint) +- [american-quart](https://en.wikipedia.org/wiki/Quart) +- [cup](https://en.wikipedia.org/wiki/Cup_(unit)) +- [imperial-fluid-ounce](https://en.wikipedia.org/wiki/Fluid_ounce) +- [imperial-gallon](https://en.wikipedia.org/wiki/Gallon) +- [imperial-pint](https://en.wikipedia.org/wiki/Pint) +- [imperial-quart](https://en.wikipedia.org/wiki/Quart) +- [liter](https://en.wikipedia.org/wiki/Litre) +- [litre](https://en.wikipedia.org/wiki/Litre) +- [milliliter](https://en.wikipedia.org/wiki/Millilitre) +- [millilitre](https://en.wikipedia.org/wiki/Millilitre) +- [tablespoon](https://en.wikipedia.org/wiki/Tablespoon) +- [teaspoon](https://en.wikipedia.org/wiki/Teaspoon)) + +Given the prevalence of the French spellings in English recipes, both `:litre` and `:liter` can be passed as options. + +### Weight + +Currently, brewtility supports the following types of weight: + +- [gram](https://en.wikipedia.org/wiki/Gram) +- [milligram](https://en.wikipedia.org/wiki/Milligram) +- [kilogram](https://en.wikipedia.org/wiki/Kilogram) +- [ounce](https://en.wikipedia.org/wiki/Ounce) +- [pound](https://en.wikipedia.org/wiki/Pound_(mass)) diff --git a/doc/cljdoc.edn b/doc/cljdoc.edn index 251a383..f363c4b 100644 --- a/doc/cljdoc.edn +++ b/doc/cljdoc.edn @@ -2,11 +2,14 @@ ["Changelog" {:file "CHANGELOG.md"}] ["Community" {} ["Contributing" {:file "CONTRIBUTING.md"}] - ["Code of Conduct" {:file "CODE_OF_CONDUCT.md"}]] + ["Code of Conduct" {:file "CODE_OF_CONDUCT.md"}] + ["Security Policy" {:file "SECURITY.md"}]] ["Functionality" {} ["Calculations" {:file "doc/api/calculations.md"}] ["Color" {:file "doc/api/color.md"}] ["Precision" {:file "doc/api/precision.md"}] ["Predicates" {:file "doc/api/predicates.md"}] ["Units of Measure" {:file "doc/api/units.md"}] - ["Wrapping" {:file "doc/api/wrapping.md"}]]]} + ["Wrapping" {:file "doc/api/wrapping.md"}]] + ["Adopted Patterns" {} + ["Symbolic Keywords" {:file "doc/patterns/symbolic_keywords.md"}]]]} diff --git a/doc/patterns/symbolic_keywords.md b/doc/patterns/symbolic_keywords.md new file mode 100644 index 0000000..4cba6cf --- /dev/null +++ b/doc/patterns/symbolic_keywords.md @@ -0,0 +1,51 @@ +# Symbolic Keywords + +In many clojure libraries, the behavior of complex functions may be controlled by a map. +For example: + +```clojure +(:require [brewtility.units :as units]) +(units/display :volume 1.5 :liter) ;; => "1.5 l" +(units/display :volume 1.5 :liter {:suffix :full}) ;; => "1.5 liter" +``` + +This allows us to easily extend the definition of a single function to fulfil multiple complex needs; however, option maps come with considerable drawbacks. +When a map is keyed with keywords, it is easy to introduce subtle, hard-to-detect errors. +Since most of these functions select default values for keys not present, typos can lead to meaningful differences in behavior. +For example: + +```clojure +(:require [brewtility.units :as units]) +(units/display :volume 1.5 :liter) ;; => "1.5 l" + +;; Not the missing "f" in the key :sufix +(units/display :volume 1.5 :liter {:sufix :full}) ;; => "1.5 l" +``` + +Because keywords aren't picked up by the auto-complete features of most editors, they're vulnerable to all classes of transpositional errors. +Further, the options themselves are unable to carry metadata and documentation with them, making them little more than magic values. + +For this reason, it is helpful for libraries to include symbols which point to the "magic" keywords used in option maps. +This has several benefits: + +- Misspelled or incorrect option keys are compile-time errors, instead of runtime bugs +- Symbols can carry metadata like documentation, deprecation notices, and point to alternative options +- Context-aware editors can auto-complete options + +Here's can example: + +```clojure +(:require [brewtility.units :as units] + [brewtility.units.options :as options]) + +(units/display options/volume 1.5 options/liter) ;; => "1.5 l" +(units/display options/volume 1.5 options/liter {options/suffix options/full}) ;; => "1.5 liter" +``` + +Most option maps in `brewtility` support symbolic keywords. +These keywords are available in the namespaces ending in `.options`. +These files live as close to the code relying on them as possible; +for example, the options for all unit conversion operations (e.g. `brewtility.units`, `brewtility.units.time`, `brewtility.units.color`, etc.) are available in `brewtility.units.options`. + +Further, the option keywords are preferred for library development. +A single source of truth for the name of a common option, such as `precision`, limits the possibility of incorrectly retrieving the values used by that option. From d36be79b5ffc27eaf06257c2aa2acd58d0b388d8 Mon Sep 17 00:00:00 2001 From: Nick Nichols Date: Sun, 11 Jun 2023 18:55:54 +0000 Subject: [PATCH 11/11] Version bump --- .sealog/changes/2-0-0.edn | 22 ++++++++++++++++++++++ CHANGELOG.md | 21 +++++++++++++++++++++ doc/api/units.md | 2 +- doc/patterns/symbolic_keywords.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- pom.xml | 4 ++-- project.clj | 2 +- src/brewtility/units/temperature.cljc | 2 +- 9 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 .sealog/changes/2-0-0.edn diff --git a/.sealog/changes/2-0-0.edn b/.sealog/changes/2-0-0.edn new file mode 100644 index 0000000..e3b6039 --- /dev/null +++ b/.sealog/changes/2-0-0.edn @@ -0,0 +1,22 @@ +{:version {:major 2 + :minor 0 + :patch 0} + :version-type :semver3 + :changes {:added ["`brewtility.units.color` for converting between SRM, EBC, and Lovibond." + "`brewtility.units.pressure` for converting between PSI, Bar, etc." + "`brewtility.units.specific-gravity` for converting Specific Gravity." + "`brewtility.units.temperature` for converting between Celsius, Fahrenheit, and Kelvin." + "`brewtility.units.time` for converting between seconds, minutes, hours, etc." + "`brewtility.units.volume` for converting between gallons, liters, etc." + "`brewtility.units.weight` for converting between pounds, kilograms, etc." + "`brewtility.units.options` to introduce the symbolic keyword pattern" + "Display functions for all unit types."] + :changed ["Internal functions that convert between unit systems now use `brewtility.units`"] + :deprecated [] + :removed ["`brewility.color` has been removed in favor of `brewtility.units.color`" + "`brewtility.units/convert-weight` has been removed in favor of `brewtility.units/convert` and `brewtility.units.weight/convert`" + "`brewtility.units/convert-volume` has been removed in favor of `brewtility.units/convert` and `brewtility.units.volume/convert`" + "`brewtility.units/convert-temperature` has been removed in favor of `brewtility.units/convert` and `brewtility.units.temperature/convert`"] + :fixed [] + :security []} + :timestamp "2023-06-11T18:47:32.340619900Z"} diff --git a/CHANGELOG.md b/CHANGELOG.md index 53cb6f5..4f1e9b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## Table of Contents +* [2.0.0 - 2023-06-11](#200---2023-06-11) * [1.5.0 - 2023-02-13](#150---2023-02-13) * [1.4.0 - 2023-02-12](#140---2023-02-12) * [1.3.0 - 2023-02-12](#130---2023-02-12) @@ -13,6 +14,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * [1.1.0 - 2020-08-15](#110---2020-08-15) * [1.0.0 - 2020-07-19](#100---2020-07-19) +## 2.0.0 - 2023-06-11 + +* Added + * `brewtility.units.color` for converting between SRM, EBC, and Lovibond. + * `brewtility.units.pressure` for converting between PSI, Bar, etc. + * `brewtility.units.specific-gravity` for converting Specific Gravity. + * `brewtility.units.temperature` for converting between Celsius, Fahrenheit, and Kelvin. + * `brewtility.units.time` for converting between seconds, minutes, hours, etc. + * `brewtility.units.volume` for converting between gallons, liters, etc. + * `brewtility.units.weight` for converting between pounds, kilograms, etc. + * `brewtility.units.options` to introduce the symbolic keyword pattern + * Display functions for all unit types. +* Changed + * Internal functions that convert between unit systems now use `brewtility.units` +* Removed + * `brewility.color` has been removed in favor of `brewtility.units.color` + * `brewtility.units/convert-weight` has been removed in favor of `brewtility.units/convert` and `brewtility.units.weight/convert` + * `brewtility.units/convert-volume` has been removed in favor of `brewtility.units/convert` and `brewtility.units.volume/convert` + * `brewtility.units/convert-temperature` has been removed in favor of `brewtility.units/convert` and `brewtility.units.temperature/convert` + ## 1.5.0 - 2023-02-13 * Added diff --git a/doc/api/units.md b/doc/api/units.md index 5416bf3..a6a3e05 100644 --- a/doc/api/units.md +++ b/doc/api/units.md @@ -117,7 +117,7 @@ This allows for progressive evolution, and provides a consistent interface to ev Currently, brewtility supports the following types of temperature measurements: -- [clesius](https://en.wikipedia.org/wiki/Celsius) +- [celsius](https://en.wikipedia.org/wiki/Celsius) - [fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) - [kelvin](https://en.wikipedia.org/wiki/Kelvin_(unit)) diff --git a/doc/patterns/symbolic_keywords.md b/doc/patterns/symbolic_keywords.md index 4cba6cf..cdda860 100644 --- a/doc/patterns/symbolic_keywords.md +++ b/doc/patterns/symbolic_keywords.md @@ -9,7 +9,7 @@ For example: (units/display :volume 1.5 :liter {:suffix :full}) ;; => "1.5 liter" ``` -This allows us to easily extend the definition of a single function to fulfil multiple complex needs; however, option maps come with considerable drawbacks. +This allows us to easily extend the definition of a single function to fulfill multiple complex needs; however, option maps come with considerable drawbacks. When a map is keyed with keywords, it is easy to introduce subtle, hard-to-detect errors. Since most of these functions select default values for keys not present, typos can lead to meaningful differences in behavior. For example: diff --git a/package-lock.json b/package-lock.json index e9cf449..4926585 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "brewtility", - "version": "1.5.0", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "brewtility", - "version": "1.5.0", + "version": "2.0.0", "license": "MIT", "devDependencies": { "karma": "^6.3.16", diff --git a/package.json b/package.json index f90ea26..322b22c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "brewtility", - "version": "1.5.0", + "version": "2.0.0", "description": "Utility functions for all of your brewing needs.", "main": "index.js", "directories": { diff --git a/pom.xml b/pom.xml index f336774..f9713fb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.wallbrew brewtility jar - 1.5.0 + 2.0.0 brewtility Utility functions for all of your brewing needs. https://github.com/Wall-Brew-Co/brewtility @@ -20,7 +20,7 @@ https://github.com/Wall-Brew-Co/brewtility scm:git:git://github.com/Wall-Brew-Co/brewtility.git scm:git:ssh://git@github.com/Wall-Brew-Co/brewtility.git - bb5fe4473e8cf506d517b623f1a56e6e95f3055b + 1e9b875c50a61b04b3711da1cee211f836ab8662 src diff --git a/project.clj b/project.clj index 2588951..165d112 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject com.wallbrew/brewtility "1.5.0" +(defproject com.wallbrew/brewtility "2.0.0" :description "Utility functions for all of your brewing needs." :url "https://github.com/Wall-Brew-Co/brewtility" :license {:name "MIT" diff --git a/src/brewtility/units/temperature.cljc b/src/brewtility/units/temperature.cljc index efa8245..b7d3a9a 100644 --- a/src/brewtility/units/temperature.cljc +++ b/src/brewtility/units/temperature.cljc @@ -5,7 +5,7 @@ This namespace converts between that and other units. Currently, brewtility supports the following types of temperature measurements: - - [clesius](https://en.wikipedia.org/wiki/Celsius) + - [celsius](https://en.wikipedia.org/wiki/Celsius) - [fahrenheit](https://en.wikipedia.org/wiki/Fahrenheit) - [kelvin](https://en.wikipedia.org/wiki/Kelvin_(unit))" {:added "2.0"}