├── .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 | [](https://www.tidyverse.org/lifecycle/#experimental)
20 | [](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 | [](https://www.tidyverse.org/lifecycle/#experimental)
10 | [](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 |
--------------------------------------------------------------------------------