toji 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example/koji_recipe.rb +34 -0
- data/example/make_koji.rb +18 -0
- data/example/rice_recipe.rb +28 -0
- data/example/three_step_mashing_recipe.rb +65 -0
- data/lib/toji.rb +12 -0
- data/lib/toji/graph.rb +6 -0
- data/lib/toji/graph/ab.rb +75 -0
- data/lib/toji/ingredient.rb +8 -0
- data/lib/toji/ingredient/koji.rb +19 -0
- data/lib/toji/ingredient/koji/actual.rb +21 -0
- data/lib/toji/ingredient/koji/actual_fermentable.rb +15 -0
- data/lib/toji/ingredient/koji/base.rb +26 -0
- data/lib/toji/ingredient/koji/expected.rb +51 -0
- data/lib/toji/ingredient/koji/expected_fermentable.rb +15 -0
- data/lib/toji/ingredient/rice.rb +19 -0
- data/lib/toji/ingredient/rice/actual.rb +18 -0
- data/lib/toji/ingredient/rice/actual_steamable.rb +27 -0
- data/lib/toji/ingredient/rice/base.rb +40 -0
- data/lib/toji/ingredient/rice/expected.rb +46 -0
- data/lib/toji/ingredient/rice/expected_steamable.rb +29 -0
- data/lib/toji/ingredient/yeast.rb +9 -0
- data/lib/toji/ingredient/yeast/base.rb +21 -0
- data/lib/toji/ingredient/yeast/red_star.rb +30 -0
- data/lib/toji/progress.rb +11 -0
- data/lib/toji/progress/job.rb +165 -0
- data/lib/toji/progress/job_accessor.rb +13 -0
- data/lib/toji/progress/jobs.rb +142 -0
- data/lib/toji/progress/make_koji.rb +100 -0
- data/lib/toji/progress/moromi.rb +276 -0
- data/lib/toji/progress/shubo.rb +158 -0
- data/lib/toji/recipe.rb +9 -0
- data/lib/toji/recipe/koji_rate.rb +16 -0
- data/lib/toji/recipe/rice_rate.rb +10 -0
- data/lib/toji/recipe/rice_rate/base.rb +21 -0
- data/lib/toji/recipe/rice_rate/cooked.rb +61 -0
- data/lib/toji/recipe/rice_rate/steamed.rb +42 -0
- data/lib/toji/recipe/step.rb +65 -0
- data/lib/toji/recipe/three_step_mashing.rb +87 -0
- data/lib/toji/version.rb +3 -0
- data/toji.gemspec +36 -0
- metadata +178 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
module Toji
|
2
|
+
module Progress
|
3
|
+
class Shubo
|
4
|
+
extend JobAccessor
|
5
|
+
|
6
|
+
TEMPLATES = {
|
7
|
+
default: [
|
8
|
+
Job.new(
|
9
|
+
elapsed_time: 0 * Job::DAY,
|
10
|
+
id: :mizukoji,
|
11
|
+
after_temp: 12.0,
|
12
|
+
acid: 13.0,
|
13
|
+
),
|
14
|
+
Job.new(
|
15
|
+
elapsed_time: 0 * Job::DAY + 1 * Job::HOUR,
|
16
|
+
id: :shikomi,
|
17
|
+
after_temp: 20.0,
|
18
|
+
acid: 13.0,
|
19
|
+
),
|
20
|
+
Job.new(
|
21
|
+
elapsed_time: 1 * Job::DAY,
|
22
|
+
id: :utase,
|
23
|
+
after_temp: 14.0,
|
24
|
+
baume: 15.0,
|
25
|
+
acid: 13.0,
|
26
|
+
),
|
27
|
+
Job.new(
|
28
|
+
elapsed_time: 2 * Job::DAY,
|
29
|
+
before_temp: 8.0,
|
30
|
+
after_temp: 11.0,
|
31
|
+
baume: 16.0,
|
32
|
+
acid: 13.0,
|
33
|
+
warming: Job::WARM_DAKI,
|
34
|
+
),
|
35
|
+
Job.new(
|
36
|
+
elapsed_time: 3 * Job::DAY,
|
37
|
+
before_temp: 10.0,
|
38
|
+
after_temp: 13.0,
|
39
|
+
baume: 16.5,
|
40
|
+
acid: 13.5,
|
41
|
+
warming: Job::WARM_DAKI,
|
42
|
+
),
|
43
|
+
Job.new(
|
44
|
+
elapsed_time: 4 * Job::DAY,
|
45
|
+
before_temp: 12.0,
|
46
|
+
after_temp: 15.0,
|
47
|
+
baume: 17.0,
|
48
|
+
acid: 13.5,
|
49
|
+
warming: Job::WARM_DAKI,
|
50
|
+
),
|
51
|
+
Job.new(
|
52
|
+
elapsed_time: 5 * Job::DAY,
|
53
|
+
after_temp: 14.0,
|
54
|
+
baume: 17.0,
|
55
|
+
acid: 14.0,
|
56
|
+
warming: Job::WARM_ANKA,
|
57
|
+
),
|
58
|
+
Job.new(
|
59
|
+
elapsed_time: 6 * Job::DAY,
|
60
|
+
after_temp: 20.0,
|
61
|
+
baume: 17.0,
|
62
|
+
acid: 14.5,
|
63
|
+
warming: Job::WARM_ANKA,
|
64
|
+
),
|
65
|
+
Job.new(
|
66
|
+
elapsed_time: 7 * Job::DAY,
|
67
|
+
after_temp: 20.0,
|
68
|
+
baume: 14.0,
|
69
|
+
acid: 15.5,
|
70
|
+
),
|
71
|
+
Job.new(
|
72
|
+
elapsed_time: 8 * Job::DAY,
|
73
|
+
after_temp: 20.0,
|
74
|
+
baume: 12.0,
|
75
|
+
acid: 16.0,
|
76
|
+
),
|
77
|
+
Job.new(
|
78
|
+
elapsed_time: 9 * Job::DAY,
|
79
|
+
id: :wake,
|
80
|
+
after_temp: 20.0,
|
81
|
+
baume: 9.0,
|
82
|
+
acid: 16.0,
|
83
|
+
),
|
84
|
+
Job.new(
|
85
|
+
elapsed_time: 10 * Job::DAY,
|
86
|
+
after_temp: 15.0,
|
87
|
+
baume: 8.0,
|
88
|
+
acid: 16.5,
|
89
|
+
),
|
90
|
+
Job.new(
|
91
|
+
elapsed_time: 11 * Job::DAY,
|
92
|
+
after_temp: 12.0,
|
93
|
+
baume: 7.0,
|
94
|
+
acid: 17.0,
|
95
|
+
),
|
96
|
+
Job.new(
|
97
|
+
elapsed_time: 12 * Job::DAY,
|
98
|
+
after_temp: 10.0,
|
99
|
+
baume: 6.0,
|
100
|
+
acid: 17.5,
|
101
|
+
),
|
102
|
+
Job.new(
|
103
|
+
elapsed_time: 13 * Job::DAY,
|
104
|
+
after_temp: 9.0,
|
105
|
+
baume: 5.0,
|
106
|
+
acid: 17.5,
|
107
|
+
),
|
108
|
+
],
|
109
|
+
}
|
110
|
+
|
111
|
+
|
112
|
+
job_reader :mizukoji
|
113
|
+
job_reader :shikomi
|
114
|
+
job_reader :utase
|
115
|
+
job_reader :wake
|
116
|
+
|
117
|
+
def initialize(jobs=[])
|
118
|
+
@jobs = Jobs.new(jobs)
|
119
|
+
end
|
120
|
+
|
121
|
+
def add(job)
|
122
|
+
@jobs << job
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def jobs
|
127
|
+
@jobs.to_a
|
128
|
+
end
|
129
|
+
|
130
|
+
def days
|
131
|
+
(jobs.last.elapsed_time.to_f / Job::DAY).ceil + 1
|
132
|
+
end
|
133
|
+
|
134
|
+
def plot_data
|
135
|
+
@jobs.plot_data
|
136
|
+
end
|
137
|
+
|
138
|
+
def plot
|
139
|
+
_days = days
|
140
|
+
|
141
|
+
Plotly::Plot.new(
|
142
|
+
data: plot_data,
|
143
|
+
layout: {
|
144
|
+
xaxis: {
|
145
|
+
dtick: 24*60*60,
|
146
|
+
tickvals: _days.times.map{|d| d*Job::DAY},
|
147
|
+
ticktext: (1.._days).to_a
|
148
|
+
}
|
149
|
+
}
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.template(key=:default)
|
154
|
+
new(TEMPLATES[key])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
data/lib/toji/recipe.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
class KojiRate
|
4
|
+
attr_reader :tanekoji_rate
|
5
|
+
attr_reader :dekoji_rate
|
6
|
+
|
7
|
+
def initialize(tanekoji_rate, dekoji_rate)
|
8
|
+
@tanekoji_rate = tanekoji_rate
|
9
|
+
@dekoji_rate = dekoji_rate
|
10
|
+
end
|
11
|
+
|
12
|
+
DEFAULT = new(0.001, 0.18)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
module RiceRate
|
4
|
+
module Base
|
5
|
+
# 浸漬米吸水率
|
6
|
+
attr_reader :soaked_rate
|
7
|
+
|
8
|
+
# 蒸し前浸漬米吸水率
|
9
|
+
# 炊飯の場合は水を追加
|
10
|
+
# 蒸しの場合は一晩経って蒸発した分を削減
|
11
|
+
attr_reader :before_steaming_rate
|
12
|
+
|
13
|
+
# 蒸米吸水率
|
14
|
+
attr_reader :steamed_rate
|
15
|
+
|
16
|
+
# 放冷後蒸米吸水率
|
17
|
+
attr_reader :cooled_rate
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
module RiceRate
|
4
|
+
class Cooked
|
5
|
+
include Base
|
6
|
+
|
7
|
+
# 炊飯器を使う場合
|
8
|
+
# 白米の60%の水で炊くと蒸米吸水率はだいたい38%くらいになる
|
9
|
+
# 炊飯、放冷の過程で水分のうち36%程度は蒸発する
|
10
|
+
|
11
|
+
# 蒸す前
|
12
|
+
# ・白米(無洗米コシヒカリ) 85g
|
13
|
+
# ・水 50g
|
14
|
+
# ・浸漬米吸水率 58.82%
|
15
|
+
#
|
16
|
+
# 蒸した後
|
17
|
+
# ・蒸米 117.5g
|
18
|
+
# ・蒸米吸水率 38.24%
|
19
|
+
|
20
|
+
def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
21
|
+
@soaked_rate = soaked_rate
|
22
|
+
@before_steaming_rate = before_steaming_rate
|
23
|
+
@steamed_rate = steamed_rate
|
24
|
+
@cooled_rate = cooled_rate
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
DEFAULT = new(0.35, 0.5, 0.40, 0.36)
|
29
|
+
|
30
|
+
@@rates = [
|
31
|
+
#new(0.60, 0.380+0.1, 0.380),
|
32
|
+
#new(0.55, 0.360+0.1, 0.360),
|
33
|
+
#new(0.50, 0.335+0.1, 0.335),
|
34
|
+
new(0.35, 0.5, 0.40, 0.36),
|
35
|
+
new(0.33, 0.45, 0.40, 0.35),
|
36
|
+
]
|
37
|
+
|
38
|
+
def self.soaked_rate(val)
|
39
|
+
@@rates.select{|r| r.soaked_rate==val}.first
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.before_steaming_rate(val)
|
43
|
+
@@rates.select{|r| r.before_steaming_rate==val}.first
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.steamed_rate(val)
|
47
|
+
@@rates.select{|r| r.steamed_rate==val}.first
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.cooled_rate(val)
|
51
|
+
@@rates.select{|r| r.cooled_rate==val}.first
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def self.cooked(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
57
|
+
Cooked.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
module RiceRate
|
4
|
+
class Steamed
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
8
|
+
@soaked_rate = soaked_rate
|
9
|
+
@before_steaming_rate = before_steaming_rate
|
10
|
+
@steamed_rate = steamed_rate
|
11
|
+
@cooled_rate = cooled_rate
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_steaming_rate
|
15
|
+
@before_steaming_rate || begin
|
16
|
+
@soaked_rate - 0.04
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
DEFAULT = new(0.33, nil, 0.41, 0.33)
|
22
|
+
|
23
|
+
def self.soaked_rate(val)
|
24
|
+
@@rates.select{|r| r.soaked_rate==val}.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.steamed_rate(val)
|
28
|
+
@@rates.select{|r| r.steamed_rate==val}.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.cooled_rate(val)
|
32
|
+
@@rates.select{|r| r.cooled_rate==val}.first
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def self.steamed(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
38
|
+
Steamed.new(soaked_rate, before_steaming_rate, steamed_rate, cooled_rate)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
class Step
|
4
|
+
attr_reader :rice
|
5
|
+
attr_reader :koji
|
6
|
+
attr_reader :water
|
7
|
+
|
8
|
+
def initialize(rice, koji, water)
|
9
|
+
@rice = Ingredient::Rice::Expected.create(rice)
|
10
|
+
@koji = Ingredient::Koji::Expected.create(koji)
|
11
|
+
@water = water.to_f
|
12
|
+
end
|
13
|
+
|
14
|
+
# 総米
|
15
|
+
def rice_total
|
16
|
+
@rice.raw + @koji.raw
|
17
|
+
end
|
18
|
+
|
19
|
+
# 総重量
|
20
|
+
def weight_total
|
21
|
+
@rice.cooled + @koji.dekoji + @water
|
22
|
+
end
|
23
|
+
|
24
|
+
# 麹歩合
|
25
|
+
# 原料白米に占める麹米の割合
|
26
|
+
# 留め仕込みまでの総米重量の20〜22%が標準である
|
27
|
+
# なお、留め仕込みまでの麹歩合が20%を下回ると蒸米の溶解糖化に影響が出るので注意がいる
|
28
|
+
# 出典: 酒造教本 P95
|
29
|
+
def koji_rate
|
30
|
+
@koji.raw / rice_total
|
31
|
+
end
|
32
|
+
|
33
|
+
# 汲水歩合
|
34
|
+
#
|
35
|
+
# 酒母: 110%が標準、高温糖化酒母は150〜180%
|
36
|
+
# 添: 85〜100%の範囲で90%が標準、湧き進め型は歩合が高い
|
37
|
+
# 仲: 120%が標準
|
38
|
+
# 留: 130〜150%の範囲
|
39
|
+
# 全体: 留までの総米に対し120〜130%、標準は125%、高級酒は130〜145%である
|
40
|
+
#
|
41
|
+
# 出典: 酒造教本 P96
|
42
|
+
def water_rate
|
43
|
+
@water / rice_total
|
44
|
+
end
|
45
|
+
|
46
|
+
def +(other)
|
47
|
+
if Step===other
|
48
|
+
self.class.new(@rice + other.rice, @koji + other.koji, @water + other.water)
|
49
|
+
else
|
50
|
+
x, y = other.coerce(self)
|
51
|
+
x + y
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def *(other)
|
56
|
+
if Integer===other || Float===other
|
57
|
+
self.class.new(@rice * other, @koji * other, @water * other)
|
58
|
+
else
|
59
|
+
x, y = other.coerce(self)
|
60
|
+
x * y
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Toji
|
2
|
+
module Recipe
|
3
|
+
class ThreeStepMashing
|
4
|
+
|
5
|
+
STEP_NAMES = ["酒母", "初添", "仲添", "留添"]
|
6
|
+
|
7
|
+
@@template = [
|
8
|
+
Step.new(93, 47, 170),
|
9
|
+
Step.new(217, 99, 270),
|
10
|
+
Step.new(423, 143, 670),
|
11
|
+
Step.new(813, 165, 1330),
|
12
|
+
]
|
13
|
+
#@@template = [
|
14
|
+
# Step.new(45, 20, 70),
|
15
|
+
# Step.new(100, 40, 130),
|
16
|
+
# Step.new(215, 60, 330),
|
17
|
+
# Step.new(360, 80, 630),
|
18
|
+
#]
|
19
|
+
|
20
|
+
attr_reader :yeast
|
21
|
+
attr_reader :steps
|
22
|
+
|
23
|
+
def initialize(yeast_cls, total, template=@@template)
|
24
|
+
@total = total
|
25
|
+
rate = total / template.map(&:weight_total).sum
|
26
|
+
|
27
|
+
@yeast = yeast_cls.new(total)
|
28
|
+
|
29
|
+
@steps = template.map {|step|
|
30
|
+
step * rate
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# 内容量の累計
|
35
|
+
def cumulative_weight_totals
|
36
|
+
total = @steps.map(&:weight_total)
|
37
|
+
|
38
|
+
total.map.with_index {|x,i|
|
39
|
+
total[0..i].inject(:+)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# 総米の累計
|
44
|
+
def cumulative_rice_totals
|
45
|
+
rice_total = @steps.map(&:rice_total)
|
46
|
+
|
47
|
+
rice_total.map.with_index {|x,i|
|
48
|
+
rice_total[0..i].inject(&:+)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
# 酒母歩合の累計
|
53
|
+
def cumulative_shubo_rates
|
54
|
+
rice_total = @steps.map(&:rice_total)
|
55
|
+
shubo = rice_total[0]
|
56
|
+
|
57
|
+
rice_total.map.with_index {|x,i|
|
58
|
+
shubo / rice_total[0..i].inject(&:+)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# 酒母歩合
|
63
|
+
#
|
64
|
+
# 7%が標準である
|
65
|
+
# 汲水歩合が大きい高温糖化酒母では6%程度である
|
66
|
+
#
|
67
|
+
# 出典: 酒造教本 P96
|
68
|
+
def shubo_rate
|
69
|
+
cumulative_shubo_rates.last
|
70
|
+
end
|
71
|
+
|
72
|
+
# 白米比率
|
73
|
+
#
|
74
|
+
# 発酵型と白米比率
|
75
|
+
# 発酵の型 酒母 添 仲 留 特徴
|
76
|
+
# 湧き進め型 1 2 4 6 短期醪、辛口、軽快な酒質
|
77
|
+
# 湧き抑え型 1 2 4 7 長期醪、甘口、おだやかな酒質
|
78
|
+
#
|
79
|
+
# 出典: 酒造教本 P95
|
80
|
+
def rice_rates
|
81
|
+
@steps.map {|step|
|
82
|
+
step.rice_total / @steps[0].rice_total
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|