toji 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,8 +4,34 @@ module Toji
4
4
  include Enumerable
5
5
  extend StateAccessor
6
6
 
7
+ REQUIRED_KEYS = [
8
+ :time,
9
+ :elapsed_time,
10
+ :day,
11
+ :day_label,
12
+ :display_time,
13
+ ].freeze
14
+
15
+ OPTIONAL_KEYS = [
16
+ :moromi_day,
17
+ :mark,
18
+ :temps,
19
+ :preset_temp,
20
+ :room_temp,
21
+ :room_psychrometry,
22
+ :baume,
23
+ :nihonshudo,
24
+ :display_baume,
25
+ :acid,
26
+ :amino_acid,
27
+ :alcohol,
28
+ :bmd,
29
+ :warming,
30
+ :warmings,
31
+ :note,
32
+ ].freeze
33
+
7
34
  attr_reader :day_offset
8
- attr_accessor :day_labels
9
35
 
10
36
  def initialize(records, date_line)
11
37
  @date_line = date_line
@@ -14,8 +40,8 @@ module Toji
14
40
 
15
41
  def records=(records)
16
42
  records = records.map{|r| StateRecord.create(r)}
17
- min_time = records.select{|j| j.time}.map(&:time).sort.first
18
- @states = records.map{|j| State.new(j.elapsed_time, j, self)}
43
+ min_time = records.select{|r| r.time}.map(&:time).sort.first
44
+ @states = records.map{|r| State.new(r.elapsed_time, r, self)}
19
45
 
20
46
  # time
21
47
  if min_time
@@ -40,11 +66,6 @@ module Toji
40
66
  end
41
67
  @day_offset += ((24 - @date_line) % 24) * HOUR
42
68
 
43
- # day_label
44
- @states.each {|s|
45
- s.day_label = s.day + 1
46
- }
47
-
48
69
  # mark hash
49
70
  @hash = {}
50
71
  @states.select{|s| s.record.mark}.each {|s|
@@ -60,6 +81,10 @@ module Toji
60
81
  days.times.map(&:succ)
61
82
  end
62
83
 
84
+ def moromi_tome_day
85
+ nil
86
+ end
87
+
63
88
  def [](mark)
64
89
  @hash[mark]
65
90
  end
@@ -72,6 +97,14 @@ module Toji
72
97
  @states.each(&block)
73
98
  end
74
99
 
100
+ def has_keys
101
+ result = REQUIRED_KEYS.clone
102
+
103
+ result += OPTIONAL_KEYS.select {|k|
104
+ @states.find {|s| s.send(k).present?}
105
+ }
106
+ end
107
+
75
108
  def self.create(records, date_line: 0)
76
109
  if Base===records
77
110
  records
@@ -10,13 +10,9 @@ module Toji
10
10
  @enable_annotations = enable_annotations
11
11
  end
12
12
 
13
- def has_keys
14
- @data.map(&:has_keys).flatten.uniq
15
- end
16
-
17
13
  def plot_data(keys=nil)
18
14
  if !keys
19
- keys = has_keys
15
+ keys = @data.has_keys
20
16
  end
21
17
 
22
18
  result = []
@@ -76,15 +72,16 @@ module Toji
76
72
 
77
73
  def table_data(keys=nil)
78
74
  if !keys
79
- keys = has_keys
75
+ keys = @data.has_keys
80
76
  keys.delete(:elapsed_time)
81
77
  keys.delete(:time)
82
78
  keys.delete(:day)
83
79
  keys.delete(:moromi_day)
84
80
  keys.delete(:baume)
85
81
  keys.delete(:nihonshudo)
82
+ keys.delete(:warming)
86
83
  else
87
- keys &= has_keys
84
+ keys &= @data.has_keys
88
85
  end
89
86
 
90
87
  cells = @data.map {|s|
@@ -7,7 +7,6 @@ module Toji
7
7
 
8
8
  attr_accessor :elapsed_time
9
9
  attr_accessor :time
10
- attr_accessor :day_label
11
10
  attr_reader :record
12
11
  attr_reader :brew
13
12
 
@@ -23,23 +22,12 @@ module Toji
23
22
  def_delegators :@record, :warmings
24
23
  def_delegators :@record, :note
25
24
 
26
- def_delegators :@record, :has_keys
27
-
28
25
  def initialize(elapsed_time, record, brew)
29
26
  @elapsed_time = elapsed_time
30
27
  @record = record
31
28
  @brew = brew
32
29
  end
33
30
 
34
- def has_keys
35
- result = [:elapsed_time, :time, :day, :day_label, :display_time]
36
- keys = [:moromi_day, :display_baume, :bmd] + StateRecord::KEYS
37
-
38
- result += keys.select {|k|
39
- !!send(k)
40
- }
41
- end
42
-
43
31
  def day
44
32
  ((elapsed_time_with_offset.to_f + 1) / DAY).ceil
45
33
  end
@@ -16,6 +16,15 @@ module Toji
16
16
  @tanekoji = tanekoji
17
17
  @dekoji = dekoji
18
18
  end
19
+
20
+ def *(other)
21
+ if Integer===other || Float===other
22
+ Actual.new(raw * other, soaked * other, steaming_water * other, steamed * other, cooled * other, tanekoji * other, dekoji * other)
23
+ else
24
+ x, y = other.coerce(self)
25
+ x * y
26
+ end
27
+ end
19
28
  end
20
29
  end
21
30
  end
@@ -21,6 +21,15 @@ module Toji
21
21
  # 出典: 酒造教本 P67
22
22
  attr_reader :dekoji_rate
23
23
  attr_reader :dekoji
24
+
25
+ def +(other)
26
+ if Base===other
27
+ Actual.new(raw + other.raw, soaked + other.soaked, steaming_water + other.steaming_water, steamed + other.steamed, cooled + other.cooled, tanekoji + other.tanekoji, dekoji + other.dekoji)
28
+ else
29
+ x, y = other.coerce(self)
30
+ x + y
31
+ end
32
+ end
24
33
  end
25
34
  end
26
35
  end
@@ -21,6 +21,10 @@ module Toji
21
21
  @dekoji_rate = koji_rate.dekoji_rate
22
22
  end
23
23
 
24
+ def round(ndigit=0, half: :up)
25
+ self.class.new(@raw.round(ndigit, half: half), rice_rate: @rice_rate, koji_rate: @koji_rate)
26
+ end
27
+
24
28
  def self.create(x)
25
29
  if self===x
26
30
  x
@@ -29,15 +33,6 @@ module Toji
29
33
  end
30
34
  end
31
35
 
32
- def +(other)
33
- if Base===other
34
- Actual.new(raw + other.raw, soaked + other.soaked, steaming_water + other.steaming_water, steamed + other.steamed, cooled + other.cooled, tanekoji + other.tanekoji, dekoji + other.dekoji)
35
- else
36
- x, y = other.coerce(self)
37
- x + y
38
- end
39
- end
40
-
41
36
  def *(other)
42
37
  if Integer===other || Float===other
43
38
  Expected.new(@raw * other, rice_rate: @rice_rate, koji_rate: @koji_rate)
@@ -2,7 +2,7 @@ module Toji
2
2
  module Recipe
3
3
  module Ingredient
4
4
  module Rice
5
- class ActualRice
5
+ class Actual
6
6
  include Base
7
7
  include ActualSteamable
8
8
 
@@ -13,6 +13,15 @@ module Toji
13
13
  @steamed = steamed
14
14
  @cooled = cooled
15
15
  end
16
+
17
+ def *(other)
18
+ if Integer===other || Float===other
19
+ Actual.new(raw * other, soaked * other, steaming_water * other, steamed * other, cooled * other)
20
+ else
21
+ x, y = other.coerce(self)
22
+ x * y
23
+ end
24
+ end
16
25
  end
17
26
  end
18
27
  end
@@ -35,6 +35,15 @@ module Toji
35
35
  # 出典: 酒造教本 P59
36
36
  attr_reader :cooled_rate
37
37
  attr_reader :cooled
38
+
39
+ def +(other)
40
+ if Base===other
41
+ Actual.new(raw + other.raw, soaked + other.soaked, steaming_water + other.steaming_water, steamed + other.steamed, cooled + other.cooled)
42
+ else
43
+ x, y = other.coerce(self)
44
+ x + y
45
+ end
46
+ end
38
47
  end
39
48
  end
40
49
  end
@@ -16,13 +16,8 @@ module Toji
16
16
  @cooled_rate = rice_rate.cooled_rate
17
17
  end
18
18
 
19
- def +(other)
20
- if Base===other
21
- Actual.new(raw + other.raw, soaked + other.soaked, steaming_water + other.steaming_water, steamed + other.steamed, cooled + other.cooled)
22
- else
23
- x, y = other.coerce(self)
24
- x + y
25
- end
19
+ def round(ndigit=0, half: :up)
20
+ self.class.new(@raw.round(ndigit, half: half), rice_rate: @rice_rate)
26
21
  end
27
22
 
28
23
  def *(other)
@@ -1,7 +1,6 @@
1
1
  require 'toji/recipe/ingredient/rice'
2
2
  require 'toji/recipe/ingredient/koji'
3
3
  require 'toji/recipe/ingredient/yeast'
4
- require 'toji/recipe/ingredient/lactic_acid'
5
4
 
6
5
  module Toji
7
6
  module Recipe
@@ -1,14 +1,20 @@
1
1
  module Toji
2
2
  module Recipe
3
3
  class Step
4
+ attr_reader :name
4
5
  attr_reader :rice
5
6
  attr_reader :koji
6
7
  attr_reader :water
8
+ attr_reader :lactic_acid
9
+ attr_reader :alcohol
7
10
 
8
- def initialize(rice, koji, water)
11
+ def initialize(name:, rice: 0, koji: 0, water: 0, lactic_acid: 0, alcohol: 0)
12
+ @name = name
9
13
  @rice = Ingredient::Rice::Expected.create(rice)
10
14
  @koji = Ingredient::Koji::Expected.create(koji)
11
15
  @water = water.to_f
16
+ @lactic_acid = lactic_acid.to_f
17
+ @alcohol = alcohol.to_f
12
18
  end
13
19
 
14
20
  # 総米
@@ -18,7 +24,7 @@ module Toji
18
24
 
19
25
  # 総重量
20
26
  def weight_total
21
- @rice.cooled + @koji.dekoji + @water
27
+ @rice.cooled + @koji.dekoji + @water + @lactic_acid + @alcohol
22
28
  end
23
29
 
24
30
  # 麹歩合
@@ -43,9 +49,31 @@ module Toji
43
49
  @water / rice_total
44
50
  end
45
51
 
52
+ def round(ndigit=0, acid_ndigit=nil, half: :up)
53
+ if !acid_ndigit
54
+ acid_ndigit = ndigit + 3
55
+ end
56
+
57
+ self.class.new(
58
+ name: @name,
59
+ rice: @rice.round(ndigit, half: half),
60
+ koji: @koji.round(ndigit, half: half),
61
+ water: @water.round(ndigit, half: half),
62
+ lactic_acid: @lactic_acid.round(acid_ndigit, half: half),
63
+ alcohol: @alcohol.round(ndigit, half: half)
64
+ )
65
+ end
66
+
46
67
  def +(other)
47
68
  if Step===other
48
- self.class.new(@rice + other.rice, @koji + other.koji, @water + other.water)
69
+ self.class.new(
70
+ name: nil,
71
+ rice: @rice + other.rice,
72
+ koji: @koji + other.koji,
73
+ water: @water + other.water,
74
+ lactic_acid: @lactic_acid + other.lactic_acid,
75
+ alcohol: @alcohol + other.alcohol
76
+ )
49
77
  else
50
78
  x, y = other.coerce(self)
51
79
  x + y
@@ -54,7 +82,14 @@ module Toji
54
82
 
55
83
  def *(other)
56
84
  if Integer===other || Float===other
57
- self.class.new(@rice * other, @koji * other, @water * other)
85
+ self.class.new(
86
+ name: @name,
87
+ rice: @rice * other,
88
+ koji: @koji * other,
89
+ water: @water * other,
90
+ lactic_acid: @lactic_acid * other,
91
+ alcohol: @alcohol * other
92
+ )
58
93
  else
59
94
  x, y = other.coerce(self)
60
95
  x * y
@@ -2,43 +2,45 @@ module Toji
2
2
  module Recipe
3
3
  class ThreeStepMashing
4
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 :lactic_acid
22
5
  attr_reader :steps
6
+ attr_reader :yeast
7
+
8
+ attr_accessor :moto_days
9
+ attr_accessor :odori_days
23
10
 
24
- def initialize(yeast_rate, lactic_acid_rate, total, template=@@template)
25
- @total = total
26
- rate = total / template.map(&:weight_total).sum
11
+ def initialize(steps, yeast_rate, moto_days: 1, odori_days: 1)
12
+ @steps = steps
27
13
 
28
- @yeast = Ingredient::Yeast.new(total, rate: yeast_rate)
29
- @lactic_acid = Ingredient::LacticAcid.new(total, rate: lactic_acid_rate)
14
+ @yeast_rate = yeast_rate
15
+ weight_total = @steps.map(&:weight_total).sum
16
+ @yeast = Ingredient::Yeast.new(weight_total, rate: yeast_rate)
30
17
 
31
- @steps = template.map {|step|
18
+ @moto_days = moto_days
19
+ @odori_days = odori_days
20
+ end
21
+
22
+ def scale(rice_total, yeast_rate=@yeast_rate)
23
+ rate = rice_total / @steps.map(&:rice_total).sum
24
+ new_steps = @steps.map {|step|
32
25
  step * rate
33
26
  }
27
+
28
+ self.class.new(new_steps, yeast_rate, moto_days: @moto_days, odori_days: @odori_days)
29
+ end
30
+
31
+ def round(ndigit=0, half: :up)
32
+ new_steps = @steps.map {|step|
33
+ step.round(ndigit, half: half)
34
+ }
35
+ self.class.new(new_steps, @yeast_rate, moto_days: @moto_days, odori_days: @odori_days)
34
36
  end
35
37
 
36
38
  # 内容量の累計
37
39
  def cumulative_weight_totals
38
- total = @steps.map(&:weight_total)
40
+ weight_total = @steps.map(&:weight_total)
39
41
 
40
- total.map.with_index {|x,i|
41
- total[0..i].inject(:+)
42
+ weight_total.map.with_index {|x,i|
43
+ weight_total[0..i].inject(:+)
42
44
  }
43
45
  end
44
46
 
@@ -84,6 +86,179 @@ module Toji
84
86
  step.rice_total / @steps[0].rice_total
85
87
  }
86
88
  end
89
+
90
+ def table_data
91
+ headers = [""] + @steps.map(&:name) + [:total]
92
+ keys = [:rice_total, :rice, :koji, :alcohol, :water, :lactic_acid]
93
+
94
+ cells = [keys]
95
+ cells += @steps.map {|step|
96
+ [step.rice_total, step.rice, step.koji, step.alcohol, step.water, step.lactic_acid]
97
+ }
98
+ cells << keys.map {|key|
99
+ @steps.map(&key).compact.sum
100
+ }
101
+
102
+ cells = cells.map {|cell|
103
+ cell.map {|c|
104
+ if Ingredient::Rice::Base===c
105
+ c = c.raw
106
+ end
107
+
108
+ case c
109
+ when NilClass
110
+ ""
111
+ when 0
112
+ ""
113
+ else
114
+ c
115
+ end
116
+ }
117
+ }
118
+
119
+ {header: headers, cells: cells}
120
+ end
121
+
122
+ def table
123
+ data = table_data
124
+
125
+ Plotly::Plot.new(
126
+ data: [{
127
+ type: :table,
128
+ header: {
129
+ values: data[:header]
130
+ },
131
+ cells: {
132
+ values: data[:cells]
133
+ },
134
+ }],
135
+ layout: {
136
+ }
137
+ )
138
+ end
139
+
140
+
141
+ # 乳酸は汲水100L当たり比重1.21の乳酸(90%乳酸と称される)を650〜720ml添加する。
142
+ # ここでは間をとって685mlとする
143
+ #
144
+ # 出典: 酒造教本 P38
145
+ TEMPLATES = {
146
+ # 酒造教本による標準型仕込配合
147
+ # 出典: 酒造教本 P97
148
+ sokujo_textbook: new(
149
+ [
150
+ Step.new(
151
+ name: :moto,
152
+ rice: 45,
153
+ koji: 20,
154
+ water: 70,
155
+ lactic_acid: 70*6.85/1000
156
+ ),
157
+ Step.new(
158
+ name: :soe,
159
+ rice: 100,
160
+ koji: 40,
161
+ water: 130
162
+ ),
163
+ Step.new(
164
+ name: :naka,
165
+ rice: 215,
166
+ koji: 60,
167
+ water: 330
168
+ ),
169
+ Step.new(
170
+ name: :tome,
171
+ rice: 360,
172
+ koji: 80,
173
+ water: 630
174
+ ),
175
+ Step.new(
176
+ name: :yodan,
177
+ rice: 80,
178
+ water: 120
179
+ ),
180
+ ],
181
+ YeastRate::RED_STAR,
182
+ moto_days: 14,
183
+ odori_days: 1,
184
+ ),
185
+ # 灘における仕込配合の平均値
186
+ # 出典: http://www.nada-ken.com/main/jp/index_shi/234.html
187
+ sokujo_nada: new(
188
+ [
189
+ Step.new(
190
+ name: :moto,
191
+ rice: 93,
192
+ koji: 47,
193
+ water: 170,
194
+ lactic_acid: 170*6.85/1000
195
+ ),
196
+ Step.new(
197
+ name: :soe,
198
+ rice: 217,
199
+ koji: 99,
200
+ water: 270
201
+ ),
202
+ Step.new(
203
+ name: :naka,
204
+ rice: 423,
205
+ koji: 143,
206
+ water: 670
207
+ ),
208
+ Step.new(
209
+ name: :tome,
210
+ rice: 813,
211
+ koji: 165,
212
+ water: 1330
213
+ ),
214
+ Step.new(
215
+ name: :alcohol,
216
+ alcohol: 900
217
+ ),
218
+ ],
219
+ YeastRate::RED_STAR,
220
+ moto_days: 14,
221
+ odori_days: 1,
222
+ ),
223
+ # 簡易酒母省略仕込
224
+ # 出典: https://www.jstage.jst.go.jp/article/jbrewsocjapan1915/60/11/60_11_999/_article/-char/ja/
225
+ simple_sokujo_himeno: new(
226
+ [
227
+ Step.new(
228
+ name: :moto,
229
+ rice: 0,
230
+ koji: 70,
231
+ water: 245,
232
+ lactic_acid: 1.6
233
+ ),
234
+ Step.new(
235
+ name: :soe,
236
+ rice: 130,
237
+ koji: 0,
238
+ water: 0
239
+ ),
240
+ Step.new(
241
+ name: :naka,
242
+ rice: 300,
243
+ koji: 100,
244
+ water: 400
245
+ ),
246
+ Step.new(
247
+ name: :tome,
248
+ rice: 490,
249
+ koji: 110,
250
+ water: 800
251
+ ),
252
+ Step.new(
253
+ name: :yodan,
254
+ water: 255
255
+ ),
256
+ ],
257
+ YeastRate::RED_STAR,
258
+ moto_days: 1,
259
+ odori_days: 2,
260
+ ),
261
+ }.freeze
87
262
  end
88
263
  end
89
264
  end
data/lib/toji/recipe.rb CHANGED
@@ -2,7 +2,6 @@ require 'toji/recipe/ingredient'
2
2
  require 'toji/recipe/rice_rate'
3
3
  require 'toji/recipe/koji_rate'
4
4
  require 'toji/recipe/yeast_rate'
5
- require 'toji/recipe/lactic_acid_rate'
6
5
  require 'toji/recipe/step'
7
6
  require 'toji/recipe/three_step_mashing'
8
7