├── .github ├── .gitignore ├── ISSUE_TEMPLATE │ └── issue_template.md ├── SUPPORT.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── .gitignore ├── LICENSE ├── .gitattributes ├── tests └── testthat.R ├── .Rbuildignore ├── NAMESPACE ├── cran-comments.md ├── sourrr.Rproj ├── DESCRIPTION ├── man ├── feeding_from_ratio.Rd ├── calculate_recipe.Rd └── build_recipe.Rd ├── LICENSE.md ├── R ├── starter.R └── dough.R ├── README.Rmd └── README.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | resources/* 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Andrew Heiss 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(sourrr) 3 | 4 | test_check("sourrr") 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^sourrr\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^cran-comments\.md$ 6 | ^\.github$ 7 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(build_recipe) 4 | export(calculate_recipe) 5 | export(feeding_from_ratio) 6 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Test environments 2 | 3 | * GitHub Actions (ubuntu-16.04): 3.3, 3.4, 3.5, oldrel, release, devel 4 | * GitHub Actions (windows): release 5 | * Github Actions (macOS): release, devel 6 | * win-builder: devel 7 | 8 | ## R CMD check results 9 | 10 | 0 errors | 0 warnings | 1 note 11 | 12 | * This is a new release. 13 | -------------------------------------------------------------------------------- /sourrr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: sourrr 2 | Title: Calculate baker's percentages for sourdough bread 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "Andrew", 6 | family = "Heiss", 7 | role = c("aut", "cre"), 8 | email = "andrew@andrewheiss.com", 9 | comment = c(ORCID = "https://orcid.org/0000-0002-3948-3914")) 10 | Description: Calculate baker's percentages for sourdough bread. 11 | License: MIT + file LICENSE 12 | Imports: 13 | glue 14 | Suggests: 15 | testthat 16 | Encoding: UTF-8 17 | LazyData: true 18 | Roxygen: list(markdown = TRUE) 19 | RoxygenNote: 7.2.3 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report or feature request 3 | about: Describe a bug you've seen or make a case for a new feature 4 | --- 5 | 6 | Please briefly describe your problem and what output you expect. If you have a question, please don't use this form. Instead, ask on or . 7 | 8 | Please include a minimal reproducible example (AKA a reprex). If you've never heard of a [reprex](http://reprex.tidyverse.org/) before, start by reading . 9 | 10 | Brief description of the problem 11 | 12 | ```r 13 | # insert reprex here 14 | ``` 15 | -------------------------------------------------------------------------------- /man/feeding_from_ratio.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/starter.R 3 | \name{feeding_from_ratio} 4 | \alias{feeding_from_ratio} 5 | \title{Determine starter feeding amounts based on ratios} 6 | \usage{ 7 | feeding_from_ratio(starter = 1, flour = 4, water = 4, flour_g = 100) 8 | } 9 | \arguments{ 10 | \item{starter}{Numeric. Ratio of starter. (e.g., 1 in a 1:4:4 ratio.)} 11 | 12 | \item{flour}{Numeric. Ratio of flour (e.g., 4 in a 1:4:4 ratio.)} 13 | 14 | \item{water}{Numeric. Ratio of water (e.g., 4 in a 1:4:4 ratio.)} 15 | 16 | \item{flour_g}{Amount of flour to use in the feeding} 17 | } 18 | \value{ 19 | Text printed to the console with feeding amounts. 20 | } 21 | \description{ 22 | Generate measurements for feeding a starter based on starter:flour:water ratios like 1:4:4, 1:5:5, etc. 23 | } 24 | \examples{ 25 | feeding_from_ratio() 26 | 27 | # A 1:5:5 feeding with 50g of flour 28 | feeding_from_ratio( 29 | starter = 1, flour = 5, water = 5, 30 | flour_g = 50 31 | ) 32 | } 33 | -------------------------------------------------------------------------------- /man/calculate_recipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dough.R 3 | \name{calculate_recipe} 4 | \alias{calculate_recipe} 5 | \title{Calculate the hydration of a given recipe} 6 | \usage{ 7 | calculate_recipe(flour, water, starter, starter_hydration, salt) 8 | } 9 | \arguments{ 10 | \item{flour}{Numeric. Amount of flour, in grams.} 11 | 12 | \item{water}{Numeric. Amount of water, in grams.} 13 | 14 | \item{starter}{Numeric. Amount of starter, in grams.} 15 | 16 | \item{starter_hydration}{Numeric. Level of hydration of starter, in percent 17 | (1 = 100\%)} 18 | 19 | \item{salt}{Numeric. Amount of salt, in grams.} 20 | } 21 | \value{ 22 | Text printed to the console with a recipe. 23 | } 24 | \description{ 25 | Determine the hydration of a recipe, given its proportions of water, flour, 26 | starter, and salt. 27 | } 28 | \examples{ 29 | calculate_recipe(flour = 450, 30 | water = 320, 31 | starter = 100, 32 | starter_hydration = 1, 33 | salt = 8) 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020 Andrew Heiss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /R/starter.R: -------------------------------------------------------------------------------- 1 | #' Determine starter feeding amounts based on ratios 2 | #' 3 | #' Generate measurements for feeding a starter based on starter:flour:water ratios like 1:4:4, 1:5:5, etc. 4 | #' 5 | #' 6 | #' @param starter Numeric. Ratio of starter. (e.g., 1 in a 1:4:4 ratio.) 7 | #' @param flour Numeric. Ratio of flour (e.g., 4 in a 1:4:4 ratio.) 8 | #' @param water Numeric. Ratio of water (e.g., 4 in a 1:4:4 ratio.) 9 | #' @param flour_g Amount of flour to use in the feeding 10 | #' 11 | #' @return Text printed to the console with feeding amounts. 12 | #' @export 13 | #' 14 | #' @examples 15 | #' feeding_from_ratio() 16 | #' 17 | #' # A 1:5:5 feeding with 50g of flour 18 | #' feeding_from_ratio( 19 | #' starter = 1, flour = 5, water = 5, 20 | #' flour_g = 50 21 | #' ) 22 | feeding_from_ratio <- function(starter = 1, flour = 4, water = 4, flour_g = 100) { 23 | pct <- starter / flour 24 | 25 | flour_amount <- flour_g 26 | water_amount <- flour_g * (water / flour) 27 | starter_amount <- flour_g * pct 28 | 29 | glue::glue( 30 | "{round(starter, 1)}:{round(flour, 1)}:{round(water, 1)}", 31 | "---", 32 | "{round(flour_amount, 1)}g flour", 33 | "{round(water_amount, 1)}g water", 34 | "{round(starter_amount, 1)}g starter", 35 | .sep = "\n" 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /man/build_recipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dough.R 3 | \name{build_recipe} 4 | \alias{build_recipe} 5 | \title{Build a sourdough recipe from final weight} 6 | \usage{ 7 | build_recipe( 8 | final_weight, 9 | hydration = 0.7, 10 | pct_starter = 0.25, 11 | starter_hydration = 1, 12 | pct_salt = 0.02 13 | ) 14 | } 15 | \arguments{ 16 | \item{final_weight}{Numeric. Target weight for dough, in grams. This won't be 17 | completely accurate because salt is added to the final mass. See Details 18 | for more information.} 19 | 20 | \item{hydration}{Numeric. Hydration for dough, in percent. Defaults to 0.7 (70\%).} 21 | 22 | \item{pct_starter}{Numeric. Percentage of starter for dough. Defaults to 0.25 (25\%).} 23 | 24 | \item{starter_hydration}{Numeric. Level of hydration of starter. Defaults to 1 (100\%).} 25 | 26 | \item{pct_salt}{Numeric. Percentage of salt for dough. Defaults to 0.02 (2\%)} 27 | } 28 | \value{ 29 | Text printed to the console with a recipe. 30 | } 31 | \description{ 32 | Generate a recipe based on the final target weight of the loaf dough. 33 | } 34 | \details{ 35 | Salt is currently just added on as extra because I couldn't figure out the 36 | math for it :shrug: (i.e. specifying a 800g final loaf will result in 809g 37 | with 2\% salt because it just adds it to the 800g) 38 | } 39 | \examples{ 40 | build_recipe(final_weight = 1000) 41 | 42 | build_recipe(final_weight = 880, 43 | hydration = 0.74, 44 | pct_starter = 0.2, 45 | starter_hydration = 1, 46 | pct_salt = 0.02) 47 | } 48 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # sourrr 17 | 18 | 19 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 20 | [![CRAN status](https://www.r-pkg.org/badges/version/sourrr)](https://CRAN.R-project.org/package=sourrr) 21 | 22 | 23 | Use {sourrr} to generate sourdough bread recipes using [baker's percentages](https://www.theperfectloaf.com/reference/introduction-to-bakers-percentages/). 24 | 25 | ## Installation 26 | 27 | You can install the development version of {sourrr} from github with: 28 | 29 | ``` r 30 | # install.packages("remotes") 31 | remotes::install_github("andrewheiss/sourrr") 32 | ``` 33 | 34 | ## Example 35 | 36 | Suppose you want to bake a 75% hydration loaf that weighs 900 grams using 100% hydration sourdough starter (i.e. you feed it a 1:1 ratio of flour and water). Use the `build_recipe()` function to generate the appropriate recipe: 37 | 38 | ```{r} 39 | library(sourrr) 40 | build_recipe(final_weight = 900, hydration = 0.75) 41 | ``` 42 | 43 | You can set all the arguments: 44 | 45 | ```{r} 46 | build_recipe(final_weight = 900, hydration = 0.90, 47 | pct_starter = 0.25, starter_hydration = 1, 48 | pct_salt = 0.02) 49 | ``` 50 | 51 | For now, it's not 100% correct because I add the salt to the final loaf weight. The math is too tricky and I can't figure it out right now with, like, the ongoing global pandemic :shrug:. 52 | 53 | If you have an existing recipe, you can use the `calculate_recipe()` function to determine the hydration level: 54 | 55 | ```{r} 56 | calculate_recipe(flour = 450, water = 320, 57 | starter = 100, starter_hydration = 1, salt = 8) 58 | ``` 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # sourrr 5 | 6 | 7 | 8 | [![Lifecycle: 9 | experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 10 | [![CRAN 11 | status](https://www.r-pkg.org/badges/version/sourrr)](https://CRAN.R-project.org/package=sourrr) 12 | 13 | 14 | Use {sourrr} to generate sourdough bread recipes using [baker’s 15 | percentages](https://www.theperfectloaf.com/reference/introduction-to-bakers-percentages/). 16 | 17 | ## Installation 18 | 19 | You can install the development version of {sourrr} from github with: 20 | 21 | ``` r 22 | # install.packages("remotes") 23 | remotes::install_github("andrewheiss/sourrr") 24 | ``` 25 | 26 | ## Example 27 | 28 | Suppose you want to bake a 75% hydration loaf that weighs 900 grams 29 | using 100% hydration sourdough starter (i.e. you feed it a 1:1 ratio of 30 | flour and water). Use the `build_recipe()` function to generate the 31 | appropriate recipe: 32 | 33 | ``` r 34 | library(sourrr) 35 | build_recipe(final_weight = 900, hydration = 0.75) 36 | #> 450g flour (514.3g total; 64.3g from starter) 37 | #> 321g water (385.7g total; 64.3g from starter) 38 | #> 129g starter (25%; 100% hydration) 39 | #> 10g salt (2%) 40 | #> --- 41 | #> 75% hydration 42 | #> 910g final loaf 43 | ``` 44 | 45 | You can set all the arguments: 46 | 47 | ``` r 48 | build_recipe(final_weight = 900, hydration = 0.90, 49 | pct_starter = 0.25, starter_hydration = 1, 50 | pct_salt = 0.02) 51 | #> 414g flour (473.7g total; 59.2g from starter) 52 | #> 367g water (426.3g total; 59.2g from starter) 53 | #> 118g starter (25%; 100% hydration) 54 | #> 9g salt (2%) 55 | #> --- 56 | #> 90% hydration 57 | #> 909g final loaf 58 | ``` 59 | 60 | For now, it’s not 100% correct because I add the salt to the final loaf 61 | weight. The math is too tricky and I can’t figure it out right now with, 62 | like, the ongoing global pandemic :shrug:. 63 | 64 | If you have an existing recipe, you can use the `calculate_recipe()` 65 | function to determine the hydration level: 66 | 67 | ``` r 68 | calculate_recipe(flour = 450, water = 320, 69 | starter = 100, starter_hydration = 1, salt = 8) 70 | #> 450g flour (500g total; 50g from starter) 71 | #> 320g water (370g total; 50g from starter) 72 | #> 100g starter (20%; 100% hydration) 73 | #> 8g salt (1.6%) 74 | #> --- 75 | #> 74% hydration 76 | #> 878g final loaf 77 | ``` 78 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Getting help with sourrr 2 | 3 | Thanks for using sourrr! 4 | Before filing an issue, there are a few places to explore and pieces to put together to make the process as smooth as possible. 5 | 6 | ## Make a reprex 7 | 8 | Start by making a minimal **repr**oducible **ex**ample using the [reprex](https://reprex.tidyverse.org/) package. 9 | If you haven't heard of or used reprex before, you're in for a treat! 10 | Seriously, reprex will make all of your R-question-asking endeavors easier (which is a pretty insane ROI for the five to ten minutes it'll take you to learn what it's all about). 11 | For additional reprex pointers, check out the [Get help!](https://www.tidyverse.org/help/) section of the tidyverse site. 12 | 13 | ## Where to ask? 14 | 15 | Armed with your reprex, the next step is to figure out [where to ask](https://www.tidyverse.org/help/#where-to-ask). 16 | 17 | * If it's a question: start with [community.rstudio.com](https://community.rstudio.com/), and/or StackOverflow. There are more people there to answer questions. 18 | 19 | * If it's a bug: you're in the right place, [file an issue](https://github.com//issues/new). 20 | 21 | * If you're not sure: let the community help you figure it out! 22 | If your problem _is_ a bug or a feature request, you can easily return here and report it. 23 | 24 | Before opening a new issue, be sure to [search issues and pull requests](https://github.com//issues) to make sure the bug hasn't been reported and/or already fixed in the development version. 25 | By default, the search will be pre-populated with `is:issue is:open`. 26 | You can [edit the qualifiers](https://help.github.com/articles/searching-issues-and-pull-requests/) (e.g. `is:pr`, `is:closed`) as needed. 27 | For example, you'd simply remove `is:open` to search _all_ issues in the repo, open or closed. 28 | 29 | ## What happens next? 30 | 31 | To be as efficient as possible, development of tidyverse packages tends to be very bursty, so you shouldn't worry if you don't get an immediate response. 32 | Typically we don't look at a repo until a sufficient quantity of issues accumulates, then there’s a burst of intense activity as we focus our efforts. 33 | That makes development more efficient because it avoids expensive context switching between problems, at the cost of taking longer to get back to you. 34 | This process makes a good reprex particularly important because it might be multiple months between your initial report and when we start working on it. 35 | If we can’t reproduce the bug, we can’t fix it! 36 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to sourrr 2 | 3 | This outlines how to propose a change to sourrr. 4 | For more detailed info about contributing to this, and other tidyverse packages, please see the 5 | [**development contributing guide**](https://rstd.io/tidy-contrib). 6 | 7 | ## Fixing typos 8 | 9 | You can fix typos, spelling mistakes, or grammatical errors in the documentation directly using the GitHub web interface, as long as the changes are made in the _source_ file. 10 | This generally means you'll need to edit [roxygen2 comments](https://roxygen2.r-lib.org/articles/roxygen2.html) in an `.R`, not a `.Rd` file. 11 | You can find the `.R` file that generates the `.Rd` by reading the comment in the first line. 12 | 13 | ## Bigger changes 14 | 15 | If you want to make a bigger change, it's a good idea to first file an issue and make sure someone from the team agrees that it’s needed. 16 | If you’ve found a bug, please file an issue that illustrates the bug with a minimal 17 | [reprex](https://www.tidyverse.org/help/#reprex) (this will also help you write a unit test, if needed). 18 | 19 | ### Pull request process 20 | 21 | * Fork the package and clone onto your computer. If you haven't done this before, we recommend using `usethis::create_from_github("", fork = TRUE)`. 22 | 23 | * Install all development dependences with `devtools::install_dev_deps()`, and then make sure the package passes R CMD check by running `devtools::check()`. 24 | If R CMD check doesn't pass cleanly, it's a good idea to ask for help before continuing. 25 | * Create a Git branch for your pull request (PR). We recommend using `usethis::pr_init("brief-description-of-change")`. 26 | 27 | * Make your changes, commit to git, and then create a PR by running `usethis::pr_push()`, and following the prompts in your browser. 28 | The title of your PR should briefly describe the change. 29 | The body of your PR should contain `Fixes #issue-number`. 30 | 31 | * For user-facing changes, add a bullet to the top of `NEWS.md` (i.e. just below the first header). Follow the style described in . 32 | 33 | ### Code style 34 | 35 | * New code should follow the tidyverse [style guide](https://style.tidyverse.org). 36 | You can use the [styler](https://CRAN.R-project.org/package=styler) package to apply these styles, but please don't restyle code that has nothing to do with your PR. 37 | 38 | * We use [roxygen2](https://cran.r-project.org/package=roxygen2), with [Markdown syntax](https://cran.r-project.org/web/packages/roxygen2/vignettes/rd-formatting.html), for documentation. 39 | 40 | * We use [testthat](https://cran.r-project.org/package=testthat) for unit tests. 41 | Contributions with test cases included are easier to accept. 42 | 43 | ## Code of Conduct 44 | 45 | Please note that the sourrr project is released with a 46 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this 47 | project you agree to abide by its terms. 48 | -------------------------------------------------------------------------------- /R/dough.R: -------------------------------------------------------------------------------- 1 | #' Build a sourdough recipe from final weight 2 | #' 3 | #' Generate a recipe based on the final target weight of the loaf dough. 4 | #' 5 | #' Salt is currently just added on as extra because I couldn't figure out the 6 | #' math for it :shrug: (i.e. specifying a 800g final loaf will result in 809g 7 | #' with 2% salt because it just adds it to the 800g) 8 | #' 9 | #' @param final_weight Numeric. Target weight for dough, in grams. This won't be 10 | #' completely accurate because salt is added to the final mass. See Details 11 | #' for more information. 12 | #' @param hydration Numeric. Hydration for dough, in percent. Defaults to 0.7 (70%). 13 | #' @param pct_starter Numeric. Percentage of starter for dough. Defaults to 0.25 (25%). 14 | #' @param starter_hydration Numeric. Level of hydration of starter. Defaults to 1 (100%). 15 | #' @param pct_salt Numeric. Percentage of salt for dough. Defaults to 0.02 (2%) 16 | #' 17 | #' @return Text printed to the console with a recipe. 18 | #' @export 19 | #' 20 | #' @examples 21 | #' build_recipe(final_weight = 1000) 22 | #' 23 | #' build_recipe(final_weight = 880, 24 | #' hydration = 0.74, 25 | #' pct_starter = 0.2, 26 | #' starter_hydration = 1, 27 | #' pct_salt = 0.02) 28 | build_recipe <- function(final_weight, hydration = 0.7, 29 | pct_starter = 0.25, starter_hydration = 1, 30 | pct_salt = 0.02) { 31 | total_water <- (final_weight * hydration) / (1 + hydration) 32 | total_flour <- final_weight - total_water 33 | total_salt <- total_flour * pct_salt 34 | 35 | total_starter <- total_flour * pct_starter 36 | total_starter_water <- (total_starter * starter_hydration) / (1 + starter_hydration) 37 | total_starter_flour <- (total_starter * starter_hydration) / (1 + starter_hydration) 38 | 39 | final_water <- total_water - total_starter_water 40 | final_flour <- total_flour - total_starter_flour 41 | 42 | final_hydration <- total_water / total_flour 43 | 44 | actual_final_weight <- total_water + total_flour + total_salt 45 | 46 | format_recipe(flour = final_flour, total_flour = total_flour, 47 | water = final_water, total_water = total_water, 48 | starter = total_starter, starter_hydration = starter_hydration, 49 | starter_flour = total_starter_flour, starter_water = total_starter_water, 50 | pct_starter = pct_starter, salt = total_salt, pct_salt = pct_salt, 51 | final_hydration = final_hydration, final_weight = actual_final_weight) 52 | } 53 | 54 | 55 | #' Calculate the hydration of a given recipe 56 | #' 57 | #' Determine the hydration of a recipe, given its proportions of water, flour, 58 | #' starter, and salt. 59 | #' 60 | #' @param flour Numeric. Amount of flour, in grams. 61 | #' @param water Numeric. Amount of water, in grams. 62 | #' @param starter Numeric. Amount of starter, in grams. 63 | #' @param starter_hydration Numeric. Level of hydration of starter, in percent 64 | #' (1 = 100%) 65 | #' @param salt Numeric. Amount of salt, in grams. 66 | #' 67 | #' @return Text printed to the console with a recipe. 68 | #' @export 69 | #' 70 | #' @examples 71 | #' calculate_recipe(flour = 450, 72 | #' water = 320, 73 | #' starter = 100, 74 | #' starter_hydration = 1, 75 | #' salt = 8) 76 | calculate_recipe <- function(flour, water, starter, starter_hydration, salt) { 77 | starter_water <- (starter * starter_hydration) / (1 + starter_hydration) 78 | starter_flour <- (starter * starter_hydration) / (1 + starter_hydration) 79 | 80 | total_water <- water + starter_water 81 | total_flour <- flour + starter_flour 82 | 83 | final_hydration <- total_water / total_flour 84 | pct_salt <- salt / total_flour 85 | pct_starter <- starter / total_flour 86 | 87 | actual_final_weight <- total_water + total_flour + salt 88 | 89 | format_recipe(flour = flour, total_flour = total_flour, water = water, total_water = total_water, 90 | starter = starter, starter_hydration = starter_hydration, 91 | starter_flour = starter_flour, starter_water = starter_water, 92 | pct_starter = pct_starter, salt = salt, pct_salt = pct_salt, 93 | final_hydration = final_hydration, final_weight = actual_final_weight) 94 | } 95 | 96 | 97 | #' Make a recipe pretty 98 | #' 99 | #' @keywords internal 100 | #' @noRd 101 | format_recipe <- function(flour, total_flour, water, total_water, 102 | starter, starter_hydration, starter_flour, 103 | starter_water, pct_starter, salt, pct_salt, 104 | final_hydration, final_weight) { 105 | glue::glue("{round(flour, 0)}g flour ({round(total_flour, 1)}g total; ", 106 | "{round(starter_water, 1)}g from starter)\n", 107 | "{round(water, 0)}g water ({round(total_water, 1)}g total; ", 108 | "{round(starter_flour, 1)}g from starter)\n", 109 | "{round(starter, 0)}g starter ({round(pct_starter * 100, 1)}%; ", 110 | "{round(starter_hydration * 100, 1)}% hydration)\n", 111 | "{round(salt, 0)}g salt ({round(pct_salt * 100, 1)}%)\n---\n", 112 | "{round(final_hydration * 100, 1)}% hydration\n", 113 | "{round(final_weight, 0)}g final loaf") 114 | } 115 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards 42 | of acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies 54 | when an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail 56 | address, posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at [INSERT CONTACT 63 | METHOD]. All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, 118 | available at https://www.contributor-covenant.org/version/2/0/ 119 | code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at https:// 128 | www.contributor-covenant.org/translations. 129 | --------------------------------------------------------------------------------