gravitheque 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|