Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WB-29: Implement Enricher Pattern #110

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2064eb7
Create enricher pattern, create equipment enrichers
nnichols Jul 24, 2022
a5e561e
Clean up doc metadata
nnichols Aug 7, 2022
930a865
Add fermentables enricher
nnichols Aug 7, 2022
122674b
Add hop predicates
nnichols Aug 8, 2022
15628d1
Add (un)wrapping functions
nnichols Aug 9, 2022
ebf92e2
Expand predicates to include recipes, styles, and waters
nnichols Aug 9, 2022
2a5c3db
Finish predicate functions
nnichols Aug 21, 2022
2e470e0
Fix predicate impl file extension
nnichols Aug 21, 2022
094cdb2
Add hop enrichers
nnichols Aug 22, 2022
bddea6e
Run all tests in a cljs context too
nnichols Aug 22, 2022
7c4c078
Add tests for enricher implementation
nnichols Sep 7, 2022
f791b5e
Add mash enrichers
nnichols Sep 12, 2022
db7bcea
Misc enrichers
nnichols Sep 23, 2022
35aa980
Use spoon as source of string functions
nnichols Jan 7, 2023
240a8d3
Use spoons test functionality for spec
nnichols Jan 7, 2023
61f0688
Fix linting
nnichols Jan 7, 2023
f9985be
Enrichers for miscs
nnichols Jan 16, 2023
57fdb26
Add water enrichers
nnichols Jan 16, 2023
db1fd67
Fix spacing
nnichols Jan 16, 2023
30454bd
Add symbolic keys pattern for enrichers
nnichols Jan 16, 2023
82ec958
Add yeast enrichers
nnichols Jan 17, 2023
e2bb3b4
Add symbolic keys for units of measure
nnichols Jan 18, 2023
404bb77
Finish rebasing
nnichols Jun 12, 2023
a07c4f1
Fix rebasing errors
nnichols Jun 12, 2023
a6d3850
Fix rebasing errors
nnichols Jun 12, 2023
d4b23ee
Fix rebasing errors
nnichols Jun 12, 2023
d9664b0
Fix rebasing errors
nnichols Jun 12, 2023
6bdf54a
Fix :added keys
nnichols Jun 12, 2023
da713ce
Finish all base enrichers
nnichols Jun 17, 2023
70564f4
Test all enricher implementation functions
nnichols Jun 26, 2023
f3edf10
Fix typo in test string
nnichols Feb 19, 2024
6f6ff41
De-dupe enricher implementation
nnichols Mar 2, 2024
8e00e01
Fix up linter
nnichols Mar 2, 2024
f754859
WIP
nnichols Mar 3, 2024
1854f17
Clean up test data
nnichols Mar 3, 2024
56bd49d
[Format] Auto-formatting
nnichols Mar 3, 2024
4ab767b
Begin accumulating changes for sealog
nnichols Mar 3, 2024
b2d9c27
Document bitterness unit
nnichols Mar 3, 2024
81bbea0
Add carbonation units
nnichols Mar 3, 2024
96ee281
Add tests for new unit systems
nnichols Mar 3, 2024
bc0fff6
Add degrees plato
nnichols Mar 6, 2024
7aa178d
WIP
nnichols Mar 11, 2024
29b750b
[Format] Auto-formatting
nnichols Mar 11, 2024
6f05b56
Add alcohol content measurements
nnichols Mar 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .clj-kondo/rewrite-clj/rewrite-clj/config.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{:lint-as
{rewrite-clj.zip/subedit-> clojure.core/->
rewrite-clj.zip/subedit->> clojure.core/->>
rewrite-clj.zip/edit-> clojure.core/->
rewrite-clj.zip/edit->> clojure.core/->>}}
17 changes: 17 additions & 0 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ jobs:
ref: ${{ github.head_ref }}
token: ${{ secrets.WALL_BREW_BOT_PAT }}

- name: Cache maven dependencies
uses: actions/cache@v3
env:
cache-name: cache-maven
with:
path: ~/.m2
key: ${{ runner.os }}-clj-${{ hashFiles('**/project.clj') }}
restore-keys: |
${{ runner.os }}-clj

- name: Install Clojure dependencies
run: lein deps

- name: Install cljstyle
uses: just-sultanov/setup-cljstyle@v1
with:
Expand All @@ -22,6 +35,10 @@ jobs:
run: |
cljstyle fix --report --report-timing --verbose

- name: Render Changelog
run: |
lein sealog render

- name: Commit changes
uses: stefanzweifel/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion .sealog/changes/2-0-0.edn
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"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`"
:removed ["`brewtility.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`"]
Expand Down
16 changes: 16 additions & 0 deletions .sealog/changes/2-1-0.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{:version {:major 2
:minor 1
:patch 0}
:version-type :semver3
:changes {:added ["`brewtility.units.alcohol-content` for converting and displaying alcohol content."
"`brewtility.units.bitterness` for rendering displayable IBU values."
"`brewtility.units.carbonation` for rendering displayable carbonation values."
"`brewtility.units.specific-gravity` now supports measurements in degrees plato."
"`brewtility.predicates.options` for option map keys shared across predicate namespaces."]
:changed ["Functions in `brewtility.calculations` will throw targeted exceptions for invalid values instead of relying on the underlying Math implementation to do so."
"Functions in `brewtility.precision` will throw targeted exceptions for invalid values instead of relying on the underlying Math implementation to do so."
"`brewtility.units` now supports a `:precision` option map key for all conversion functions. This key will round the converted value to the specified number of decimal places. Defaults to the precision of the converted value."
"`brewtility.units.color` now supports a reverse-lookup for known RGBa values."
"All functions in `brewtility.predicates` now support an option map as a second argument to make their arities consistent."]
:removed ["`^:const` metadata has been removed from conversion maps"]}
:timestamp "2024-03-11T18:47:13.522931300Z"}
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## Table of Contents

* [2.1.0 - 2024-03-11](#210---2024-03-11)
* [2.0.1 - 2024-03-11](#201---2024-03-11)
* [2.0.0 - 2023-06-11](#200---2023-06-11)
* [1.5.0 - 2023-02-13](#150---2023-02-13)
Expand All @@ -15,6 +16,23 @@ 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.1.0 - 2024-03-11

* Added
* `brewtility.units.alcohol-content` for converting and displaying alcohol content.
* `brewtility.units.bitterness` for rendering displayable IBU values.
* `brewtility.units.carbonation` for rendering displayable carbonation values.
* `brewtility.units.specific-gravity` now supports measurements in degrees plato.
* `brewtility.predicates.options` for option map keys shared across predicate namespaces.
* Changed
* Functions in `brewtility.calculations` will throw targeted exceptions for invalid values instead of relying on the underlying Math implementation to do so.
* Functions in `brewtility.precision` will throw targeted exceptions for invalid values instead of relying on the underlying Math implementation to do so.
* `brewtility.units` now supports a `:precision` option map key for all conversion functions. This key will round the converted value to the specified number of decimal places. Defaults to the precision of the converted value.
* `brewtility.units.color` now supports a reverse-lookup for known RGBa values.
* All functions in `brewtility.predicates` now support an option map as a second argument to make their arities consistent.
* Removed
* `^:const` metadata has been removed from conversion maps

## 2.0.1 - 2024-03-11

* Security
Expand All @@ -35,7 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* 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.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`
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ version/major:
@ lein change version leiningen.release/bump-version release
@ lein sealog bump major
@ lein pom
@ npm version major --no-git-tag-version

version/minor:
$(info Updating minor version and adding CHANGELOG entry...)
Expand All @@ -22,6 +23,7 @@ version/minor:
@ lein change version leiningen.release/bump-version release
@ lein sealog bump minor
@ lein pom
@ npm version minor --no-git-tag-version

version/patch:
$(info Updating patch version and adding CHANGELOG entry...)
Expand All @@ -30,6 +32,7 @@ version/patch:
@ lein change version leiningen.release/bump-version release
@ lein sealog bump patch
@ lein pom
@ npm version patch --no-git-tag-version

changelog/render:
$(info Rendering CHANGELOG...)
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
[![Clojars Project](https://img.shields.io/clojars/v/com.wallbrew/brewtility.svg)](https://clojars.org/com.wallbrew/brewtility)
![Clojure and ClojureScript CI](https://github.com/Wall-Brew-Co/brewtility/workflows/Clojure%20and%20ClojureScript%20CI/badge.svg)
[![cljdoc badge](https://cljdoc.org/badge/com.wallbrew/brewtility)](https://cljdoc.org/d/com.wallbrew/brewtility/CURRENT)
[![GitHub](https://img.shields.io/github/license/Wall-Brew-Co/brewtility)](https://github.com/Wall-Brew-Co/brewtility/blob/master/LICENSE)
[![Twitter Follow](https://img.shields.io/twitter/follow/WallBrew?style=social)](https://twitter.com/WallBrew)

A Clojure(Script) utility library for all of your brewing needs.

Expand Down Expand Up @@ -62,6 +64,10 @@ lein test-build

The tests will also execute on the JVM, to ensure the library is compatible for apps in both deployment environments.

## Contributors

<a href="https://github.com/Wall-Brew-Co/brewtility/graphs/contributors"><img src="https://raw.githubusercontent.com/Wall-Brew-Co/brewtility/master/CONTRIBUTORS.svg" alt="The GitHub profile pictures of all current contributors. Clicking this image will lead you to the GitHub contribution graph." /></a>

## License

Copyright © 2020-2023 - [Wall Brew Co](https://wallbrew.com/)
Expand Down
28 changes: 23 additions & 5 deletions doc/api/units.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ This provides a consistent interface to every system of measure, unit type, and
(units/convert units/volume 20 :teaspoon :liter)
;; => 0.099

;; You can also round the conversion to a specified precision
(units/convert units/volume 20 :teaspoon :liter {:precision 1})
;; => 0.1

;; 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)
Expand Down Expand Up @@ -72,6 +76,8 @@ Brewtility supports four systems of measure:
These are the most commonly seen systems in brewing.
There are measurement functions for the most common types of measurements within these systems:

- [Bitterness](##bitterness)
- [Carbonation](##carbonation)
- [Color](##color)
- [Pressure](##pressure)
- [Specific Gravity](##specific-gravity)
Expand All @@ -80,6 +86,22 @@ There are measurement functions for the most common types of measurements within
- [Volume](##volume)
- [Weight](##weight)

### Bitterness

Currently, brewtility supports the following bitterness measurements:

- [IBU](https://en.wikipedia.org/wiki/International_bitterness_units)

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.

### Carbonation

Currently, brewtility supports the following carbonation measurements:

- [Volumes of CO2](https://en.wikipedia.org/wiki/Carbon_dioxide#Beverages)
- [Grams per Liter](https://en.wikipedia.org/wiki/Carbon_dioxide#Beverages)

### Color

Currently, brewtility supports the following types of color:
Expand All @@ -89,10 +111,6 @@ Currently, brewtility supports the following types of color:
- [Lovibond](https://en.wikipedia.org/wiki/Beer_measurement#Colour)
- [RGBa](https://en.wikipedia.org/wiki/RGBA_color_model)

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.

### Pressure

Currently, brewtility supports the following types of pressure:
Expand Down Expand Up @@ -156,7 +174,7 @@ Currently, brewtility supports the following types of volume:
- [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.
Given the prevalence of the non-US spellings in English recipes, both `:litre` and `:liter` can be passed as options.

### Weight

Expand Down
3 changes: 2 additions & 1 deletion doc/cljdoc.edn
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
["Community" {}
["Contributing" {:file "CONTRIBUTING.md"}]
["Code of Conduct" {:file "CODE_OF_CONDUCT.md"}]
["License" {:file "LICENSE.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"}]]
["Adopted Patterns" {}
["Enricher Pattern" {:file "doc/patterns/enricher-pattern.md"}]
["Symbolic Keywords" {:file "doc/patterns/symbolic_keywords.md"}]]]}
134 changes: 134 additions & 0 deletions doc/patterns/enricher-pattern.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Enricher Pattern

In [BeerXML](http://www.beerxml.com/ "The XML standard to encode beer data"), and, by extension, [common-beer-format](https://github.com/Wall-Brew-Co/common-beer-format "A clojure library/spec for BeerXML"), there are specifications which ultimately represent and encode the same concept.
For a concrete example, consider a sample [equipment](https://github.com/Wall-Brew-Co/common-beer-format/blob/master/src/common_beer_format/equipment.cljc "The specification for a piece of brewing equipment") record.

```clj
{:name "8 Gal pot with 5 gal Igloo Cooler"
:boil-size 22.71
:trub-chiller-loss 0.95
:boil-time 60.0
:version 1
:tun-specific-heat 0.3
:batch-size 18.93
:display-boil-size "6.0 US Gallons"}
```

In the above, you can find two representations of the pre-boil volume that piece of equipment held.
By the standard, the `:boil-size` is standardized to be the volume in litres, and the `:display-boil-size` is meant to represent that value in a region and human friendly way.
nnichols marked this conversation as resolved.
Show resolved Hide resolved
This is great for users; however, it can be unwieldy in applications for a few to store and transmit data which may only be used in the display layer.
Programmatically, we are often more interested in a smaller subset of the equipment record and will defer computing display values until they are needed.

While the unit conversion is simple, it can often lead to front-end code like this:

```clj
(defn display-equipment
[equipment system-of-measure]
[:span
[:h1 (:name equipment)]
[:ul
[:li (str "Boil volume: " (->display (convert-units (:boil-size equipment) system-of-measure)))]
(when (:tun-weight equipment)
[:li (str "Mash Tun Weight: " (->display (convert-units (:tun-weight equipment) system-of-measure)))])
...
]])
```

Helper functions may be extracted, but the above code would certainly benefit from the `:display-boil-size` and `:display-tun-weight` fields which may optionally exist on the `equipment` record.
As a utility library for beer data, brewtility comes with the functionality needed to compute these display fields; however, keeping them as loose datums isn't fully desirable either.
In the contexts we want display data available for an equipment record, it's much easier to provide that data with the equipment record.
To that end, this library implements an enrichment pattern.

## Enrichers

An enrichment function is a function from maps to maps, which non-destructively adds to the source value derived information.
For example, if we know the user's location, we could infer wether Metric or Imperial measurements would be more appropriate to display.
With that information, we could leverage an enricher to modify the prior equipment example:

```clj
(def igloo-cooler
{:name "8 Gal pot with 5 gal Igloo Cooler"
:boil-size 22.71
:trub-chiller-loss 0.95
:boil-time 60.0
:version 1
:tun-specific-heat 0.3
:batch-size 18.93})

(require '[brewtility.enrich.equipment :as enrich])

(enrich/enrich-display-boil-size igloo-cooler {:system-of-measure :imperial})
;; => {:name "8 Gal pot with 5 gal Igloo Cooler"
;; :boil-size 22.71
;; :trub-chiller-loss 0.95
;; :boil-time 60.0
;; :version 1
;; :tun-specific-heat 0.3
;; :batch-size 18.93
;; :display-boil-size "6.0 US Gallons"}
;;
```

If we wanted to calculate multiple display values in serial, we can also perform a best-effort enrichment of the entire record.

```clj
(enrich/enrich-equipment igloo-cooler {:system-of-measure :imperial})
;; => {:name "8 Gal pot with 5 gal Igloo Cooler"
;; :boil-size 22.71
;; :trub-chiller-loss 0.95
;; :boil-time 60.0
;; :version 1
;; :tun-specific-heat 0.3
;; :batch-size 18.93
;; :display-boil-size "6.0 US Gallons"
;; :display-trub-chiller-loss "0.251 US Gallons"
;; :display-batch-size "5.0 US Gallons"}
;;
```

Or, in our example front-end:

```clj
(defn fetch-equipment!
[equipment-id]
(-> (http/get (str some-url "/equipment/" equipment-id))
:body
json/decode
enrich/enrich-equipment {:system-of-measure :imperial})))

(defn display-equipment
[equipment]
[:span
[:h1 (:name equipment)]
[:ul
[:li (str "Boil volume: " (:display-boil-size equipment))))]
(when (:display-tun-weight equipment)
[:li (str "Mash Tun Weight: " (display-tun-weight equipment))])
...
]])

(defn fetch-and-display-equipment!
[equipment-id]
(-> equipment-id fetch-equipment! display-equipment))
```

In the above, we're able to calculate display values for the boil size, batch size, and trub chiller loss since the data sources for that information.
While the equipment record does specify display values for the tun's volume, the source of that data is missing- and therefore excluded.

## Implementations

Enricher functions exist for each of the core specifications supported by BeerXML and common-beer-format.

- [Equipment](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/equipment.cljc "Equipment enrichers")
- [Fermentables](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/fermentables.cljc "Fermentable enrichers")
- [Hops](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/hops.cljc "Hop enrichers")
- [Mash](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/mash.cljc "Mash enrichers")
- [Miscs](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/miscs.cljc "Misc enrichers")
- [Recipes](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/recipes.cljc "Recipe enrichers")
- [Styles](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/styles.cljc "Style enrichers")
- [Waters](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/waters.cljc "Water enrichers")
- [yeasts](https://github.com/Wall-Brew-Co/brewtility/blob/master/src/brewtility/enrich/yeasts.cljc "Yeast enrichers")

## Inspiration

- [Maybe Not - Rich Hickey](https://www.youtube.com/watch?v=YR5WdGrpoug&ab_channel=ClojureTV "A great presentation on the types of problems type systems try to solve")
18 changes: 2 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading