gravitheque 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -0
- data/Gemfile +6 -3
- data/Guardfile +3 -3
- data/README.md +10 -311
- data/bin/test +38 -1
- data/gravitheque.gemspec +1 -1
- data/lib/alcohol.rb +35 -0
- data/lib/calories.rb +52 -0
- data/lib/hops.rb +140 -0
- data/lib/mash.rb +57 -0
- data/lib/numbers.rb +201 -0
- data/lib/yeast.rb +50 -0
- data/test/helper.rb +3 -0
- data/test/test_alcohol.rb +14 -0
- data/test/test_calories.rb +18 -0
- data/test/test_hops.rb +68 -0
- data/test/test_mash.rb +30 -0
- data/test/test_numbers.rb +68 -0
- data/test/test_yeast.rb +47 -0
- metadata +22 -34
- data/lib/calculators.rb +0 -6
- data/lib/calculators/alcohol.rb +0 -71
- data/lib/calculators/calories.rb +0 -115
- data/lib/calculators/hops.rb +0 -257
- data/lib/calculators/mash.rb +0 -82
- data/lib/calculators/yeast.rb +0 -71
- data/lib/conversions.rb +0 -8
- data/lib/conversions/extract.rb +0 -99
- data/lib/conversions/mass.rb +0 -47
- data/lib/conversions/temperature.rb +0 -25
- data/lib/conversions/volume.rb +0 -47
- data/lib/gravitheque.rb +0 -2
- data/test/calculators/test_alcohol.rb +0 -58
- data/test/calculators/test_calories.rb +0 -89
- data/test/calculators/test_hops.rb +0 -257
- data/test/calculators/test_mash.rb +0 -71
- data/test/calculators/test_yeast.rb +0 -149
- data/test/conversions/test_extract.rb +0 -32
- data/test/conversions/test_mass.rb +0 -22
- data/test/conversions/test_temperature.rb +0 -18
- data/test/conversions/test_volume.rb +0 -22
- data/test/test_helper.rb +0 -25
data/lib/calories.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Calculate how many calories a beer contains
|
2
|
+
module Calories
|
3
|
+
|
4
|
+
# Calculates calories from alcohol
|
5
|
+
#
|
6
|
+
# @note
|
7
|
+
# Extract must be measured in specific gravity
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Calories.from_alcohol 1.055, 1.01
|
11
|
+
#
|
12
|
+
# @param [Float] original original gravity
|
13
|
+
# @param [Float] terminal terminal gravity
|
14
|
+
# @return [Fixnum] calories from alcohol
|
15
|
+
def self.from_alcohol original, terminal
|
16
|
+
((1881.22 * terminal) *
|
17
|
+
((original - terminal) / (1.775 - original))).round
|
18
|
+
end
|
19
|
+
|
20
|
+
# Calculates calories from extract
|
21
|
+
#
|
22
|
+
# @note
|
23
|
+
# Extract must be measured in specific gravity
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# Calories.from_extract 1.055, 1.01
|
27
|
+
#
|
28
|
+
# @param [Float] original original gravity
|
29
|
+
# @param [Float] terminal terminal gravity
|
30
|
+
# @return [Fixnum] calories from extract
|
31
|
+
def self.from_extract original, terminal
|
32
|
+
(3550.0 * terminal *
|
33
|
+
((0.1808 * original) + (0.8192 * terminal) - 1.0004)).round
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calculates calories per serving; i.e. 500ml
|
37
|
+
#
|
38
|
+
# @note
|
39
|
+
# Extract must be measured in specific gravity
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# Calories.per_serving 1.055, 1.01
|
43
|
+
#
|
44
|
+
# @param [Float] original original gravity
|
45
|
+
# @param [Float] terminal terminal gravity
|
46
|
+
# @return [Fixnum] calories per serving
|
47
|
+
def self.per_serving original, terminal
|
48
|
+
(from_alcohol original, terminal) +
|
49
|
+
(from_extract original, terminal)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/hops.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Various hop calculations
|
2
|
+
module Hops
|
3
|
+
|
4
|
+
# Calculates hop utilization.
|
5
|
+
#
|
6
|
+
# @note
|
7
|
+
# Extract must be measured in specific gravity
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Hops.utilization 20, 1.055
|
11
|
+
#
|
12
|
+
# @param [Fixnum] time remaining boil time
|
13
|
+
# @param [Float] extract specific gravity of wort
|
14
|
+
# @return [Float] hop utilization
|
15
|
+
def self.utilization time, extract
|
16
|
+
((extract_adjustment extract) *
|
17
|
+
((time_adjustment time) / 4.15)).round 2
|
18
|
+
end
|
19
|
+
|
20
|
+
# Calculate boil time adjustment
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# Hops.time_adjustment 60
|
24
|
+
#
|
25
|
+
# @param [Fixnum] time remaining boil time
|
26
|
+
# @return [Float] boil time adjustment
|
27
|
+
def self.time_adjustment time
|
28
|
+
(1 - Math.exp(-0.04 * time)).round 2
|
29
|
+
end
|
30
|
+
|
31
|
+
# Calculate extract adjustment
|
32
|
+
#
|
33
|
+
# @note
|
34
|
+
# Extract must be measured in specific gravity
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# Hops.extract_adjustment 1.065
|
38
|
+
#
|
39
|
+
# @param [Float] extract specific gravity of wort
|
40
|
+
# @return [Float] extract adjustment
|
41
|
+
def self.extract_adjustment extract
|
42
|
+
(1.65 * 0.000125 ** (extract - 1)).round 2
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates IBUs for hop addition
|
46
|
+
#
|
47
|
+
# @note
|
48
|
+
# Extract must be measured specific gravity, mass in grams and volume in liters
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# Hops.ibus({
|
52
|
+
# alpha: 12.4,
|
53
|
+
# extract: 1.055,
|
54
|
+
# mass: 56,
|
55
|
+
# time: 90,
|
56
|
+
# volume: 20
|
57
|
+
# })
|
58
|
+
#
|
59
|
+
# @param [Hash] data data required to calculate IBUs; `alpha`, `extract`, `mass`, `time`, `volume`
|
60
|
+
# @return [Fixnum] IBUs from hop addition
|
61
|
+
def self.ibus data
|
62
|
+
(extract_adjustment(data[:extract]) *
|
63
|
+
time_adjustment(data[:time]) * (data[:alpha] / 100) *
|
64
|
+
data[:mass] * 1000 / (data[:volume] * 4.15)).round
|
65
|
+
end
|
66
|
+
|
67
|
+
# Calculates hop mass required to achieve specific IBUs.
|
68
|
+
#
|
69
|
+
# @note
|
70
|
+
# Extract must be measured specific gravity, mass in grams and volume in liters
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# Hops.hop_mass_required({
|
74
|
+
# alpha: 1.05,
|
75
|
+
# extract: 16,
|
76
|
+
# ibus: 25,
|
77
|
+
# time: 60,
|
78
|
+
# volume: 20
|
79
|
+
# })
|
80
|
+
#
|
81
|
+
# @param [Hash] data data required to calculate hop mass required; `alpha`, `extract`, `ibus`, `time`, `volume`
|
82
|
+
# @return [Float] hop mass required to achieve specific IBUs
|
83
|
+
def self.mass_required data
|
84
|
+
((data[:volume] * data[:ibus]) /
|
85
|
+
(utilization(data[:time], data[:extract]) *
|
86
|
+
data[:alpha] * 10)).round
|
87
|
+
end
|
88
|
+
|
89
|
+
# Calculate milliliters of HopShot required to achieve specific IBUs.
|
90
|
+
#
|
91
|
+
# @note
|
92
|
+
# Extract must be measured specific gravity, mass in grams and volume in liters
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# Hops.hopshot_required({
|
96
|
+
# alpha: 13.5,
|
97
|
+
# extract: 1.05,
|
98
|
+
# ibus: 25,
|
99
|
+
# time: 60,
|
100
|
+
# volume: 20
|
101
|
+
# })
|
102
|
+
#
|
103
|
+
# @param [Hash] data data required to calculate HopShot required; `alpha`, `extract`, `ibus`, `time`, `volume`
|
104
|
+
# @return [Float] milliliters of HopShot required to achieve specific IBUs
|
105
|
+
def self.hopshot_required data
|
106
|
+
unadjusted_amount = (data[:ibus] / 10.0) * (data[:volume] / 19.0)
|
107
|
+
|
108
|
+
hopshot_required = unadjusted_amount +
|
109
|
+
(unadjusted_amount * hopshot_extract_adjustment(data[:extract]))
|
110
|
+
|
111
|
+
if data[:time] >= 90
|
112
|
+
hopshot_required = hopshot_required - (unadjusted_amount * 0.1)
|
113
|
+
end
|
114
|
+
|
115
|
+
hopshot_required.round 1
|
116
|
+
end
|
117
|
+
|
118
|
+
# Calculate extract adjustment for HopShot
|
119
|
+
#
|
120
|
+
# @note
|
121
|
+
# Extract must be measured in specific gravity
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# Hops.hopshot_extract_adjustment 1.065
|
125
|
+
#
|
126
|
+
# @param [Float] extract specific gravity of wort
|
127
|
+
# @return [Float] HopShot extract adjustment
|
128
|
+
def self.hopshot_extract_adjustment extract
|
129
|
+
if extract >= 1.08 && extract < 1.1
|
130
|
+
0.1
|
131
|
+
elsif extract >= 1.1 && extract < 1.15
|
132
|
+
0.2
|
133
|
+
elsif extract >= 1.15
|
134
|
+
0.3
|
135
|
+
else
|
136
|
+
0
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
data/lib/mash.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Various mash calculations
|
2
|
+
module Mash
|
3
|
+
|
4
|
+
# Calculates strike water temperature
|
5
|
+
#
|
6
|
+
# @note
|
7
|
+
# Temperatures must be measured in Celsius, ratio is liters per kilograms
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# Mash.strike_water({
|
11
|
+
# initial: 20, # temperature of grain; i.e. ambient temperature
|
12
|
+
# target: 60, # target mash temperature
|
13
|
+
# ratio: 1.25 # water to grain ratio; e.g. 1.25L/kg
|
14
|
+
# })
|
15
|
+
#
|
16
|
+
# Mash.strike_water({
|
17
|
+
# initial: 20,
|
18
|
+
# target: 60,
|
19
|
+
# ratio: 1.25,
|
20
|
+
# adjustment: 1.015 # calculated temperature is multiplied by the adjustment to compensate for heat lost to the mash tun
|
21
|
+
# })
|
22
|
+
#
|
23
|
+
# @param [Hash] data data required to calculate strike temperature; `initial`, `target`, `ratio` and optionally `adjustment`
|
24
|
+
# @return [Fixnum] strike water temperature
|
25
|
+
def self.strike_temperature data
|
26
|
+
adjustment = data[:adjustment] || 1
|
27
|
+
target = data[:target]
|
28
|
+
|
29
|
+
(((0.41 / data[:ratio]) *
|
30
|
+
(target - data[:initial]) +
|
31
|
+
target) * adjustment).round
|
32
|
+
end
|
33
|
+
|
34
|
+
# Calculates volume of boiling water needed to increase mash temperature
|
35
|
+
#
|
36
|
+
# @note
|
37
|
+
# Temperatures must be measured in Celsius, mass in kilograms and volume in liters
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# Mash.infusion_volume({
|
41
|
+
# initial: 40, # current mash temperature
|
42
|
+
# target: 60, # target mash temperature
|
43
|
+
# mass: 4, # grain mass in mash
|
44
|
+
# volume: 6 # water volume in mash
|
45
|
+
# })
|
46
|
+
#
|
47
|
+
# @param [Hash] data data required to calculate strike temperature; `initial`, `target`, `mass`, `volume`
|
48
|
+
# @return [Float] liters of infusion water needed for step
|
49
|
+
def self.infusion_volume data
|
50
|
+
target = data[:target]
|
51
|
+
|
52
|
+
((target - data[:initial]) *
|
53
|
+
((0.41 * data[:mass]) + data[:volume]) /
|
54
|
+
(100 - target)).round 1
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/lib/numbers.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
# Methods for converting Fixnums between units
|
2
|
+
class Fixnum
|
3
|
+
|
4
|
+
# Covert Plato to specific gravity
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# 14.to_specific_gravity
|
8
|
+
#
|
9
|
+
# @return [Float] degrees Plato
|
10
|
+
def to_specific_gravity
|
11
|
+
self.to_f.to_specific_gravity
|
12
|
+
end
|
13
|
+
|
14
|
+
# Covert Fahrenheit to Celsius
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# 104.to_celsius
|
18
|
+
#
|
19
|
+
# @return [Fixnum] temperature in Celsius
|
20
|
+
def to_celsius
|
21
|
+
((self - 32) / 1.8).round
|
22
|
+
end
|
23
|
+
|
24
|
+
# Covert Celsius to Fahrenheit
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# 60.to_fahrenheit
|
28
|
+
#
|
29
|
+
# @return [Fixnum] temperature in Fahrenheit
|
30
|
+
def to_fahrenheit
|
31
|
+
((self * 1.8) + 32).round
|
32
|
+
end
|
33
|
+
|
34
|
+
# Covert gallons (or quarts) to liters
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# 5.to_liters
|
38
|
+
# 15.to_liters :quarts
|
39
|
+
#
|
40
|
+
# @return [Float] volume in liters
|
41
|
+
def to_liters unit = :gallons
|
42
|
+
self.to_f.to_liters unit
|
43
|
+
end
|
44
|
+
|
45
|
+
# Covert ounces to grams
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# 2.to_grams
|
49
|
+
#
|
50
|
+
# @return [Float] mass in grams
|
51
|
+
def to_grams
|
52
|
+
self.to_f.to_grams
|
53
|
+
end
|
54
|
+
|
55
|
+
# Covert grams to ounces
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# 56.to_ounces
|
59
|
+
#
|
60
|
+
# @return [Float] mass in ounces
|
61
|
+
def to_ounces
|
62
|
+
self.to_f.to_ounces
|
63
|
+
end
|
64
|
+
|
65
|
+
# Covert pounds to kilograms
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# 10.to_kilograms
|
69
|
+
#
|
70
|
+
# @return [Float] mass in kilograms
|
71
|
+
def to_kilograms
|
72
|
+
self.to_f.to_kilograms
|
73
|
+
end
|
74
|
+
|
75
|
+
# Covert kilograms to pounds
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# 10.to_pounds
|
79
|
+
#
|
80
|
+
# @return [Float] mass in pounds
|
81
|
+
def to_pounds
|
82
|
+
self.to_f.to_pounds
|
83
|
+
end
|
84
|
+
|
85
|
+
# Gravity correction based on sample temperature
|
86
|
+
#
|
87
|
+
# @note
|
88
|
+
# Temperature must be measured in Celsius
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# 40.gravity_correction
|
92
|
+
#
|
93
|
+
# @return [Float] mass in pounds
|
94
|
+
def gravity_correction
|
95
|
+
if self < 3.98
|
96
|
+
-0.000032692 * self - 0.000740644
|
97
|
+
elsif self < 50
|
98
|
+
-0.0008031922 - 0.0000473773 * self + 0.000007231263 * self * self - 0.00000003078278 * self * self * self
|
99
|
+
else
|
100
|
+
-0.005431719 + 0.0001963596 * self + 0.000002661056 * self * self
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# Methods for converting Floats between units
|
107
|
+
class Float
|
108
|
+
|
109
|
+
# Covert specific gravity to Plato
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# 1.055.to_plato
|
113
|
+
#
|
114
|
+
# @return [Float] degrees Plato
|
115
|
+
def to_plato
|
116
|
+
(-676.67 + 1286.4 * self - 800.47 * self * self + 190.74 * self * self * self).round 1
|
117
|
+
end
|
118
|
+
|
119
|
+
# Covert Plato to specific gravity
|
120
|
+
#
|
121
|
+
# @example
|
122
|
+
# 13.6.to_specific_gravity
|
123
|
+
#
|
124
|
+
# @return [Float] degrees Plato
|
125
|
+
def to_specific_gravity
|
126
|
+
(self / (258.6 - ((self / 258.2) * 227.1)) + 1.0).round 3
|
127
|
+
end
|
128
|
+
|
129
|
+
# Covert specific gravity to temperature corrected specific gravity
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# 1.055.to_temperature_corrected_specific_gravity 40
|
133
|
+
# 1.055.to_temperature_corrected_specific_gravity 110, :fahrenheit
|
134
|
+
#
|
135
|
+
# @param [Fixnum] temperature temperature of sample
|
136
|
+
# @param [Symbol] unit unit of temperature used; `:celsius` or `:fahrenheit`
|
137
|
+
# @return [Float] temperature corrected specific gravity
|
138
|
+
def to_temperature_corrected_specific_gravity temperature, unit = :celsius
|
139
|
+
temperature = temperature.to_celsius unless unit == :celsius
|
140
|
+
correction = temperature.gravity_correction
|
141
|
+
|
142
|
+
(self + correction).round 3
|
143
|
+
end
|
144
|
+
|
145
|
+
# Covert gallons (or quarts) to liters
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# 5.to_liters
|
149
|
+
# 15.to_liters :quarts
|
150
|
+
#
|
151
|
+
# @return [Float] volume in liters
|
152
|
+
def to_liters unit = :gallons
|
153
|
+
case unit
|
154
|
+
when :gallons
|
155
|
+
(self * 3.785412).round 1
|
156
|
+
when :quarts
|
157
|
+
(self * 0.946353).round 1
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Covert ounces to grams
|
162
|
+
#
|
163
|
+
# @example
|
164
|
+
# 5.5.to_grams
|
165
|
+
#
|
166
|
+
# @return [Float] mass in grams
|
167
|
+
def to_grams
|
168
|
+
(self * 28.3495231).round 1
|
169
|
+
end
|
170
|
+
|
171
|
+
# Covert grams to ounces
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# 100.5.to_ounces
|
175
|
+
#
|
176
|
+
# @return [Float] mass in ounces
|
177
|
+
def to_ounces
|
178
|
+
(self * 0.0352739619).round 1
|
179
|
+
end
|
180
|
+
|
181
|
+
# Covert pounds to kilograms
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# 15.5.to_kilograms
|
185
|
+
#
|
186
|
+
# @return [Float] mass in kilograms
|
187
|
+
def to_kilograms
|
188
|
+
(self * 0.453592).round 1
|
189
|
+
end
|
190
|
+
|
191
|
+
# Covert kilograms to pounds
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# 7.5.to_pounds
|
195
|
+
#
|
196
|
+
# @return [Float] mass in pounds
|
197
|
+
def to_pounds
|
198
|
+
(self * 2.204623).round 1
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|