toji 1.2.0 → 1.3.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.
@@ -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