toji 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|