beer_recipe 0.3.0 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59854fd37ab30f4018a7971da8615e820886e166
4
- data.tar.gz: a023d8a675a187fcc9acd681e680f0c0e6b11485
3
+ metadata.gz: b8e2bb78a9f64806b377bd0e0222c07028f82777
4
+ data.tar.gz: 378bcc1bf96f2f0f4ceeca9c89120fef04a24cc0
5
5
  SHA512:
6
- metadata.gz: d7c4041ab2c42bb4d54a99efca92d62496ce8a5f55741772f49b87e25c77fa2348c775445b1002f1ce7cba751960f85fee4a36a767e5a9e86944552e5e7c2e16
7
- data.tar.gz: 19112e1ac28a6043fdbb0198d672f6df39161b3504dc9ffd9f0c6f4eb7b58cfce04c64dc257c245e6e4013f7f5f1a7cd7acb0a788e784f6e5ff9557b588fd0e4
6
+ metadata.gz: 346bea94b4361255d09db38fc337af63e0d80423a2f843a7438f12996824a41e3e6468acd560446171dec311234473973fec6cbb8f5d3262a936c593be67af87
7
+ data.tar.gz: 8d7c6e320d56f637e38e1b02c6c0ac32280be731c22b2d9a3b78c86f32c7e3038e198474ddd31adc46d9704e70eda885a30d71bea53f38affe63f343ec9752d7
@@ -9,7 +9,7 @@ doc = <<DOCOPT
9
9
  Beer Recipe
10
10
 
11
11
  Usage:
12
- #{__FILE__} [--file <beer.xml>] [--format=<format>] [--template=<path>]
12
+ #{__FILE__} [--file <beer.xml>] [--format=<format>] [--template=<path>] [--language=<file>]
13
13
  #{__FILE__} -h | --help
14
14
  #{__FILE__} --version
15
15
 
@@ -19,6 +19,7 @@ Options:
19
19
  --format=<format> Output format: text|html [default: html].
20
20
  --template=<path> Path to ERB html template file to use instead of default.
21
21
  --file=<beerxml> Path to beerxml file to read (default STDIN).
22
+ --language=<file> Path to language file (defaults to locales/en.yml)
22
23
 
23
24
  DOCOPT
24
25
 
@@ -1,6 +1,7 @@
1
1
  require 'nrb/beerxml'
2
2
  require 'erb'
3
3
  require 'pathname'
4
+ require 'i18n'
4
5
 
5
6
  module BeerRecipe
6
7
  end
@@ -8,6 +9,7 @@ end
8
9
  require 'beer_recipe/version'
9
10
  require 'beer_recipe/errors'
10
11
  require 'beer_recipe/reader'
12
+ require 'beer_recipe/formula'
11
13
  require 'beer_recipe/wrapper'
12
14
  require 'beer_recipe/mash_wrapper'
13
15
  require 'beer_recipe/mash_step_wrapper'
@@ -1,4 +1,6 @@
1
1
  module BeerRecipe
2
2
  class BeerRecipeError < StandardError; end
3
+ class ParseError < BeerRecipeError; end
4
+ class FormatError < BeerRecipeError; end
3
5
  class NotImplentedError < BeerRecipeError; end
4
6
  end
@@ -1,11 +1,10 @@
1
1
  class BeerRecipe::FermentableWrapper < BeerRecipe::Wrapper
2
2
  def formatted_amount
3
- "#{'%.2f' % amount} kg"
3
+ "#{'%.2f' % amount}"
4
4
  end
5
5
 
6
- #@type="Grain", @yield=82.5, @color=5.91,
7
6
  def formatted_color
8
- "#{'%.0f' % color} EBC"
7
+ "#{'%.0f' % color_ebc}"
9
8
  end
10
9
 
11
10
  def color_ebc
@@ -13,16 +12,35 @@ class BeerRecipe::FermentableWrapper < BeerRecipe::Wrapper
13
12
  end
14
13
 
15
14
  def color_srm
16
- color / 1.97
15
+ @color_srm ||= BeerRecipe::Formula.new.ebc_to_srm(color_ebc)
17
16
  end
18
17
 
19
- def color_class
20
- "srm#{'%.0f' % color_srm}"
18
+ def srm_in_batch
19
+ BeerRecipe::Formula.new.mcu_to_srm(mcu)
21
20
  end
22
21
 
23
22
  def mcu
24
- # MCU = (weight kg * lovibond * 2.205) / (volume * 0.264)
25
- (amount * color_srm * 2.205) / (@recipe.batch_size * 0.264)
23
+ @mcu ||= BeerRecipe::Formula.new.mcu(amount, color_srm, @recipe.batch_size)
26
24
  end
25
+
26
+ def color_class
27
+ c = color_srm.to_i
28
+ if c > 40
29
+ 'srm-max'
30
+ elsif c < 1
31
+ 'srm-min'
32
+ else
33
+ "srm#{c}"
34
+ end
35
+ end
36
+
37
+ def color_hex
38
+ "#%02x%02x%02x" % BeerRecipe::Formula.new.srm_to_rgb(color_srm)
39
+ end
40
+
41
+ def amount_percent
42
+ amount / @recipe.total_grains * 100
43
+ end
44
+
27
45
  end
28
46
 
@@ -0,0 +1,53 @@
1
+ module BeerRecipe
2
+ class Formula
3
+ def ebc_to_srm(ebc)
4
+ ebc / 1.97
5
+ end
6
+
7
+ def srm_to_ebc(srm)
8
+ srm * 1.97
9
+ end
10
+
11
+ def mcu_to_srm(mcu)
12
+ # SRM color = 1.4922 * (MCU ** 0.6859)
13
+ srm = 1.4922 * (mcu ** 0.6859)
14
+ if srm > 8
15
+ srm
16
+ else
17
+ mcu
18
+ end
19
+ end
20
+
21
+ def srm_to_rgb(srm)
22
+ r = [255, [0, 255 * (0.975 ** srm)].max].min
23
+ g = [255, [0, 245 * (0.88 ** srm)].max].min
24
+ b = [255, [0, 220 * (0.7 ** srm)].max].min
25
+ [r, g, b]
26
+ end
27
+
28
+ def mcu(amount, srm, volume)
29
+ # MCU = (weight kg * lovibond * 2.205) / (volume * 0.264)
30
+ (amount * srm * 2.205) / (volume * 0.264)
31
+ end
32
+
33
+ def sg_to_abv(og, fg)
34
+ if og > 0 && fg > 0
35
+ ( (76.08 * (og - fg) / (1.775 - og) ) * (fg / 0.794) )
36
+ else
37
+ 0
38
+ end
39
+ end
40
+
41
+ def calories(serving_size, abv, og, fg)
42
+ r = real_extract(og, fg)
43
+ ( 6.9 * ( 0.79 * ( abv / fg ) ) + 4 * ( r - 0.1 ) ) * fg * ( serving_size / 100 )
44
+ end
45
+
46
+ def real_extract(og, fg)
47
+ p = (-205.35 * (og ** 2)) + (668.72 * og) - 463.37
48
+ q = (-205.35 * (fg ** 2)) + (668.72 * fg) - 463.37
49
+ (0.1808 * p) + (0.8192 * q)
50
+ end
51
+ end
52
+ end
53
+
@@ -10,8 +10,12 @@ class BeerRecipe::HopWrapper < BeerRecipe::Wrapper
10
10
  ( @record.alpha * amount * boil_time * 3 ) / @recipe.batch_size
11
11
  end
12
12
 
13
+ def dryhop?
14
+ @record.use == 'Dry Hop'
15
+ end
16
+
13
17
  def boil_time
14
- if @record.use == 'Dry Hop'
18
+ if dryhop?
15
19
  0
16
20
  else
17
21
  @record.time / 60
@@ -19,19 +23,32 @@ class BeerRecipe::HopWrapper < BeerRecipe::Wrapper
19
23
  end
20
24
 
21
25
  def formatted_time
22
- if @record.use == 'Dry Hop'
23
- "#{'%.0f' % (@record.time / 1440)} days"
26
+ if dryhop?
27
+ "#{'%.0f' % (@record.time / 1440)}"
24
28
  else
25
- "#{'%.0f' % @record.time} min"
29
+ "#{'%.0f' % @record.time}"
30
+ end
31
+ end
32
+
33
+ def time_unit
34
+ if dryhop?
35
+ 'days'
36
+ else
37
+ 'min'
26
38
  end
27
39
  end
28
40
 
29
41
  def formatted_amount
30
- "#{'%.0f' % amount} gr"
42
+ "#{'%.0f' % amount}"
31
43
  end
32
44
 
33
45
  def formatted_ibu
34
- "#{'%.2f' % ibu} IBU"
46
+ "#{'%.1f' % ibu}"
47
+ end
48
+
49
+ def amount_percent
50
+ amount / @recipe.total_hops * 100
35
51
  end
52
+
36
53
  end
37
54
 
@@ -1,10 +1,18 @@
1
1
  class BeerRecipe::Mash_stepWrapper < BeerRecipe::Wrapper
2
2
  def formatted_step_temp
3
- "#{'%.0f' % step_temp}°C"
3
+ "#{'%.0f' % step_temp}"
4
+ end
5
+
6
+ def formatted_ramp_time
7
+ "#{'%.0f' % ramp_time}"
4
8
  end
5
9
 
6
10
  def formatted_step_time
7
- "#{'%.0f' % step_time} min"
11
+ "#{'%.0f' % step_time}"
12
+ end
13
+
14
+ def formatted_infuse_amount
15
+ "#{'%.2f' % infuse_amount}"
8
16
  end
9
17
  end
10
18
 
@@ -10,5 +10,9 @@ class BeerRecipe::MashWrapper < BeerRecipe::Wrapper
10
10
  BeerRecipe::Wrapper.wrap(step, @recipe)
11
11
  end
12
12
  end
13
+
14
+ def total_mash_time
15
+ steps.map { |s| s.step_time }.reduce :+
16
+ end
13
17
  end
14
18
 
@@ -5,6 +5,11 @@ class BeerRecipe::Reader
5
5
  @options = options
6
6
  @options[:recipe_wrapper] ||= BeerRecipe::RecipeWrapper
7
7
  @options[:formatter] ||= BeerRecipe::HtmlFormatter
8
+ setup_translation
9
+ end
10
+
11
+ def setup_translation
12
+ I18n.backend.store_translations(:en, YAML.load(File.read(@options[:language])))
8
13
  end
9
14
 
10
15
  def self.parse_options(options)
@@ -16,19 +21,25 @@ class BeerRecipe::Reader
16
21
  end
17
22
  opts[:template] = options['--template'] if options['--template']
18
23
  opts[:file] = options['--file'] if options['--file']
24
+ opts[:language] = options['--language'] || 'locales/en.yml'
19
25
  opts
20
26
  end
21
27
 
22
28
  def read
23
29
  @parser ||= NRB::BeerXML::Parser.new
24
- @beerxml = parser.parse @options.fetch(:file, STDIN)
30
+ @beerxml = @parser.parse @options.fetch(:file, STDIN)
25
31
  self
26
32
  end
27
33
 
28
34
  def parse
35
+ raise BeerRecipe::ParseError if @beerxml.nil?
29
36
  @beerxml.records.each do |record|
30
37
  recipe = @options[:recipe_wrapper].new(record)
31
- @options[:formatter].new(@options).format(recipe).output
38
+ begin
39
+ @options[:formatter].new(@options).format(recipe).output
40
+ rescue NoMethodError => e
41
+ raise BeerRecipe::FormatError.new e
42
+ end
32
43
  end
33
44
  end
34
45
  end
@@ -16,7 +16,7 @@ class BeerRecipe::RecipeWrapper < BeerRecipe::Wrapper
16
16
 
17
17
  def method_missing(method, *args, &block)
18
18
  if SETS.include?(method)
19
- @sets[method] ||= BeerRecipe::Wrapper.set(recipe, method)
19
+ @sets[method] ||= BeerRecipe::Wrapper.set(self, recipe.send(method))
20
20
  else
21
21
  @record.send(method, *args, &block)
22
22
  end
@@ -30,19 +30,20 @@ class BeerRecipe::RecipeWrapper < BeerRecipe::Wrapper
30
30
  @mash ||= BeerRecipe::MashWrapper.new(recipe.mash, self)
31
31
  end
32
32
 
33
+ def has_final_values?
34
+ og > 0 && fg > 0 && abv > 0
35
+ end
36
+
37
+ def file_name
38
+ I18n.transliterate(name.downcase.gsub(/\s+/, '_')).gsub(/[^a-z0-9_]/, '')
39
+ end
40
+
33
41
  def style_code
34
42
  "#{recipe.style.category_number} #{recipe.style.style_letter}"
35
43
  end
36
44
 
37
45
  def abv
38
- return @abv if @abv
39
- og = recipe.og
40
- fg = recipe.fg
41
- @abv = if og > 0 && fg > 0
42
- ( (76.08 * (og - fg) / (1.775 - og) ) * (fg / 0.794) )
43
- else
44
- 0
45
- end
46
+ @abv ||= BeerRecipe::Formula.new.sg_to_abv(recipe.og, recipe.fg)
46
47
  end
47
48
 
48
49
  def ibu
@@ -71,43 +72,77 @@ class BeerRecipe::RecipeWrapper < BeerRecipe::Wrapper
71
72
  end
72
73
 
73
74
  def color_srm
74
- # SRM color = 1.4922 * (MCU ** 0.6859)
75
- mcu = color_mcu
76
- srm = 1.4922 * (mcu ** 0.6859)
77
- if srm > 8
78
- srm
79
- else
80
- mcu
81
- end
75
+ @color_srm ||= BeerRecipe::Formula.new.mcu_to_srm(color_mcu)
82
76
  end
83
77
 
84
78
  def color_ebc
85
- color_srm * 1.97
79
+ @color_eb ||= BeerRecipe::Formula.new.srm_to_ebc(color_srm)
86
80
  end
87
81
 
88
82
  def color_class
89
- "srm#{'%.0f' % color}"
83
+ c = color_srm.to_i
84
+ if c > 40
85
+ 'srm-max'
86
+ elsif c < 1
87
+ 'srm-min'
88
+ else
89
+ "srm#{c}"
90
+ end
91
+ end
92
+
93
+ def color_hex
94
+ "#%02x%02x%02x" % BeerRecipe::Formula.new.srm_to_rgb(color_srm)
90
95
  end
91
96
 
92
97
  def formatted_color
93
- "#{'%.0f' % color} °L"
98
+ "#{'%.0f' % color_ebc} EBC"
94
99
  end
95
100
 
96
101
  def total_grains
97
- total_grains = 0
102
+ return @total_grains if @total_grains
103
+ @total_grains = 0
98
104
  fermentables.each do |f|
99
- total_grains += f.amount
105
+ @total_grains += f.amount
100
106
  end
101
- total_grains
107
+ @total_grains
102
108
  end
103
109
 
104
110
  def total_hops
105
- hop_weight = 0
111
+ return @hop_weight if @hop_weight
112
+ @hop_weight = 0
106
113
  hops.each do |hop|
107
- hop_weight += hop.amount
114
+ @hop_weight += hop.amount
108
115
  end
109
- hop_weight
116
+ @hop_weight
110
117
  end
111
118
 
119
+ def total_time
120
+ boil_time + mash.total_mash_time
121
+ end
122
+
123
+ def total_time_period
124
+ "PT#{'%0.f' % total_time}M"
125
+ end
126
+
127
+ def boil_time_period
128
+ "PT#{'%0.f' % boil_time}M"
129
+ end
130
+
131
+ def serving_size
132
+ 1000
133
+ end
134
+
135
+ # Returns calories per liter
136
+ def calories
137
+ @calories ||= if has_final_values?
138
+ BeerRecipe::Formula.new.calories(serving_size, abv, og, fg)
139
+ else
140
+ 0
141
+ end
142
+ end
143
+
144
+ def real_extract
145
+ @real_extract ||= BeerRecipe::Formula.new.real_extract(og, fg)
146
+ end
112
147
  end
113
148
 
@@ -1,3 +1,3 @@
1
1
  module BeerRecipe
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.3'
3
3
  end
@@ -17,7 +17,7 @@ class BeerRecipe::Wrapper
17
17
  end
18
18
 
19
19
  def self.set(recipe, set)
20
- recipe.send(set).map { |record| self.wrap(record, recipe) }
20
+ set.map { |record| self.wrap(record, recipe) }
21
21
  end
22
22
 
23
23
  def self.wrap(record, recipe)
@@ -0,0 +1,79 @@
1
+ ---
2
+ information: Information
3
+ style: Style
4
+ type: Type
5
+ grains: Grains
6
+ kilograms: kg
7
+ hops: Hops
8
+ grams: gr
9
+ equipment: Equipment
10
+ brew_date: Brew Date
11
+ brewer: Brewer
12
+ assisted_by: assisted by %{assistant}
13
+ notes: Notes
14
+ recipe_details: Recipe Details
15
+ batch_size: Batch Size
16
+ boil_time: Boil Time
17
+ ibu: IBU
18
+ og: OG
19
+ fg: FG
20
+ abv: ABV
21
+ calories_per_liter: Calories / L
22
+ color: Color
23
+ liter_abbr: L
24
+ degrees: '°C'
25
+ minutes: min
26
+ sg: SG
27
+ percent: '%'
28
+ percent_sign: '%'
29
+ ebc: EBC
30
+ fermentables: Fermentables
31
+ recipe_details: Recipe Details
32
+ fermentable_name: Name
33
+ fermentable_amount: Amount
34
+ fermentable_type: Type
35
+ fermentable_color: Color
36
+ Grain: Grain
37
+ Dry Extract: Dry Extract
38
+ Sugar: Sugar
39
+ Adjunct: Adjunct
40
+ hops_name: Name
41
+ hops_amount: Amount
42
+ hops_time: Time
43
+ hops_use: Use
44
+ hops_form: Form
45
+ hops_alpha: Alpha
46
+ hops_ibu: IBU
47
+ Pellet: Pellet
48
+ Leaf: Leaf
49
+ Plug: Plug
50
+ days: days
51
+ min: min
52
+ Dry Hop: Dry Hop
53
+ Boil: Boil
54
+ Aroma: Aroma
55
+ yeasts: Yeasts
56
+ yeast_name: Name
57
+ yeast_product_id: Product ID
58
+ yeast_lab: Lab
59
+ yeast_form: Form
60
+ yeast_attenuation: Attenuation
61
+ yeast_temperature: Temperature
62
+ Dry: Dry
63
+ Liquid: Liquid
64
+ mash: Mash
65
+ mash_name: Name
66
+ mash_type: Type
67
+ mash_infuse_amount: Infuse Amount
68
+ mash_step_temp: Step Temp
69
+ mash_ramp_time: Ramp Time
70
+ mash_step_time: Step Time
71
+ Temperature: Temperature
72
+ Infusion: Infusion
73
+ Mash In: Mash In
74
+ Protein Rest: Protein Rest
75
+ Saccharification: Saccharification
76
+ Low Saccharification: Low Saccharification
77
+ High Saccharification: High Saccharification
78
+ Mash Out: Mash Out
79
+ generated_by: Generated by