brewscribe 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
@@ -1,3 +1,12 @@
1
+ ## 0.2.0, released 2012-04-13
2
+
3
+ * Added Brewscribe::Mash
4
+ * Added Brewscribe::Carbonation
5
+ * Added Brewscribe::Equipment
6
+ * Added Brewscribe::Style
7
+ * Brewscribe::Recipe now follows the type conversion system
8
+ * Brewscribe::Document now parses Style listings
9
+
1
10
  ## 0.1.0, released 2012-04-05
2
11
 
3
12
  * Added Brewscribe::Document to represent .bsmx file
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Brewscribe
1
+ # Brewscribe [![Build Status](https://secure.travis-ci.org/cadwallion/brewscribe.png)](http://travis-ci.org/cadwallion/brewscribe)
2
2
 
3
3
  Brewscribe is a Beersmith2 (.bsmx) file parser.
4
4
 
@@ -17,5 +17,6 @@ Gem::Specification.new do |gem|
17
17
  gem.add_development_dependency 'rspec'
18
18
  gem.add_development_dependency 'guard'
19
19
  gem.add_development_dependency 'guard-rspec'
20
+ gem.add_development_dependency 'rake'
20
21
  gem.add_dependency 'nokogiri'
21
22
  end
@@ -1,8 +1,12 @@
1
1
  require 'brewscribe/version'
2
+ require 'brewscribe/conversion'
2
3
  require 'brewscribe/document'
3
4
  require 'brewscribe/recipe'
4
5
  require 'brewscribe/ingredient_list'
5
- require 'brewscribe/conversion'
6
+ require 'brewscribe/mash'
7
+ require 'brewscribe/equipment'
8
+ require 'brewscribe/carbonation'
9
+ require 'brewscribe/style'
6
10
 
7
11
  module Brewscribe
8
12
  def self.import file
@@ -0,0 +1,23 @@
1
+ module Brewscribe
2
+ class Carbonation
3
+ attr_accessor :name, :temperature, :type, :primer_name, :carb_rate,
4
+ :notes, :last_modified
5
+
6
+ include Brewscribe::Conversion
7
+
8
+ TYPES = ['Bottle', 'Keg', 'Keg with priming agent']
9
+ KEY_CONVERSION = {
10
+ carb_rate: PERCENT_CONV,
11
+ temperature: FLOAT_CONV,
12
+ type: ->(t) { TYPES[t.to_i] },
13
+ last_modified: DATE_CONV
14
+ }
15
+
16
+ def self.from_data data
17
+ carb = new
18
+ carb.data_to_properties data
19
+
20
+ carb
21
+ end
22
+ end
23
+ end
@@ -1,9 +1,11 @@
1
1
  module Brewscribe
2
2
  class Document
3
- attr_reader :recipes, :raw_data, :hash
3
+ attr_reader :raw_data, :hash
4
+ attr_accessor :recipes, :styles
4
5
 
5
6
  def initialize options = {}
6
7
  @recipes = []
8
+ @styles = []
7
9
 
8
10
  if options[:file]
9
11
  @raw_data = File.read options[:file]
@@ -24,6 +26,12 @@ module Brewscribe
24
26
  parse_recipes Array @hash[:recipe]
25
27
  end
26
28
 
29
+ if @hash[:style].class == Hash
30
+ parse_styles[@hash[:style]]
31
+ else
32
+ parse_styles Array @hash[:style]
33
+ end
34
+
27
35
  self
28
36
  end
29
37
 
@@ -33,35 +41,43 @@ module Brewscribe
33
41
  end
34
42
  end
35
43
 
44
+ def parse_styles styles
45
+ styles.each do |style_hash|
46
+ @styles << Style.from_data(style_hash)
47
+ end
48
+ end
49
+
36
50
  def xml_node_to_hash node
37
- if node.element?
38
- if node.children.size > 0
39
- result_hash = {}
51
+ if node
52
+ if node.element?
53
+ if node.children.size > 0
54
+ result_hash = {}
40
55
 
41
- node.children.each do |child|
42
- result = xml_node_to_hash child
43
- property = clean_key child.name
44
- key = property.to_sym
56
+ node.children.each do |child|
57
+ result = xml_node_to_hash child
58
+ property = clean_key child.name
59
+ key = property.to_sym
45
60
 
46
- if child.name == 'text'
47
- return result if !child.next && !child.previous
48
- elsif result_hash[key]
49
- if result_hash[key].is_a? Array
50
- result_hash[key] << result
61
+ if child.name == 'text'
62
+ return result if !child.next && !child.previous
63
+ elsif result_hash[key]
64
+ if result_hash[key].is_a? Array
65
+ result_hash[key] << result
66
+ else
67
+ result_hash[key] = [result_hash[key]] << result
68
+ end
51
69
  else
52
- result_hash[key] = [result_hash[key]] << result
70
+ result_hash[key] = result
53
71
  end
54
- else
55
- result_hash[key] = result
56
72
  end
57
- end
58
73
 
59
- return result_hash
74
+ return result_hash
75
+ else
76
+ return nil
77
+ end
60
78
  else
61
- return nil
79
+ return node.content.to_s
62
80
  end
63
- else
64
- return node.content.to_s
65
81
  end
66
82
  end
67
83
 
@@ -0,0 +1,42 @@
1
+ module Brewscribe
2
+ class Equipment
3
+ attr_accessor :last_modified, :name, :mash_vol, :tun_mass, :boil_rate_flag,
4
+ :tun_specific_heat, :tun_deadspace, :tun_adj_deadspace, :calc_boil, :boil_vol,
5
+ :boil_time, :old_evap_rate, :equip_39, :boil_off, :trub_loss, :cool_pct,
6
+ :top_up_kettle, :batch_vol, :fermenter_loss, :top_up, :efficiency, :hop_util,
7
+ :notes
8
+
9
+ include Brewscribe::Conversion
10
+
11
+ KEY_CONVERSION = {
12
+ last_modified: DATE_CONV,
13
+ mash_vol: FLOAT_CONV,
14
+ tun_mass: FLOAT_CONV,
15
+ boil_rate_flag: BOOLEAN_CONV,
16
+ boil_time: FLOAT_CONV,
17
+ tun_specific_heat: FLOAT_CONV,
18
+ tun_deadspace: FLOAT_CONV,
19
+ tun_adj_deadspace: FLOAT_CONV,
20
+ calc_boil: BOOLEAN_CONV,
21
+ boil_vol: FLOAT_CONV,
22
+ old_evap_rate: PERCENT_CONV,
23
+ equip_39: BOOLEAN_CONV,
24
+ boil_off: FLOAT_CONV,
25
+ trub_loss: FLOAT_CONV,
26
+ cool_pct: PERCENT_CONV,
27
+ top_up_kettle: FLOAT_CONV,
28
+ batch_vol: FLOAT_CONV,
29
+ fermenter_loss: FLOAT_CONV,
30
+ top_up: FLOAT_CONV,
31
+ efficiency: PERCENT_CONV,
32
+ hop_util: PERCENT_CONV
33
+ }
34
+
35
+ def self.from_data data
36
+ equipment = new
37
+ equipment.data_to_properties data
38
+
39
+ equipment
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'brewscribe/mash/step'
2
+
3
+ module Brewscribe
4
+ class Mash
5
+ attr_accessor :last_modified, :name, :grain_weight, :grain_temp, :boil_temp,
6
+ :tun_temp, :ph, :sparge_temp, :batch, :batch_pct, :batch_even, :batch_drain,
7
+ :mash_39, :tun_deadspace, :biab_vol, :biab, :notes, :steps, :equip_adjust,
8
+ :tun_vol, :tun_mass, :tun_hc
9
+
10
+ include Brewscribe::Conversion
11
+
12
+ KEY_CONVERSION = {
13
+ last_modified: DATE_CONV,
14
+ grain_weight: FLOAT_CONV,
15
+ grain_temp: FLOAT_CONV,
16
+ boil_temp: FLOAT_CONV,
17
+ tun_temp: FLOAT_CONV,
18
+ ph: FLOAT_CONV,
19
+ sparge_temp: FLOAT_CONV,
20
+ batch: FLOAT_CONV,
21
+ batch_pct: PERCENT_CONV,
22
+ batch_even: BOOLEAN_CONV,
23
+ batch_drain: BOOLEAN_CONV,
24
+ mash_39: BOOLEAN_CONV,
25
+ tun_deadspace: FLOAT_CONV,
26
+ biab_vol: FLOAT_CONV,
27
+ biab: BOOLEAN_CONV,
28
+ equip_adjust: BOOLEAN_CONV,
29
+ tun_vol: FLOAT_CONV,
30
+ tun_mass: FLOAT_CONV,
31
+ tun_hc: FLOAT_CONV
32
+ }
33
+
34
+ def self.from_data data
35
+ mash = new
36
+ mash.data_to_properties data
37
+
38
+ if mash.steps[:data][:mashstep].class == Hash
39
+ steps = [mash.steps[:data][:mashstep]]
40
+ else
41
+ steps = mash.steps[:data][:mashstep]
42
+ end
43
+
44
+ mash.steps = []
45
+ steps.each do |step|
46
+ mash.steps << Brewscribe::Mash::Step.from_data(step)
47
+ end
48
+
49
+ mash
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ module Brewscribe
2
+ class Mash
3
+ class Step
4
+ attr_accessor :last_modified, :name, :type, :infusion, :step_temp, :step_time,
5
+ :rise_time, :tun_addition, :tun_hc, :tun_vol, :tun_temp, :tun_mass,
6
+ :start_temp, :grain_temp, :start_vol, :grain_weight, :infusion_temp,
7
+ :decoction_amt
8
+
9
+ include Brewscribe::Conversion
10
+
11
+ TYPES = ['Infusion', 'Decoction', 'Temperature']
12
+
13
+ KEY_CONVERSION = {
14
+ last_modified: DATE_CONV,
15
+ type: ->(t) { TYPES[t.to_i] },
16
+ infusion: FLOAT_CONV,
17
+ step_temp: FLOAT_CONV,
18
+ step_time: INT_CONV,
19
+ rise_time: INT_CONV,
20
+ tun_addition: FLOAT_CONV,
21
+ tun_vol: FLOAT_CONV,
22
+ tun_temp: FLOAT_CONV,
23
+ tun_mass: FLOAT_CONV,
24
+ start_temp: FLOAT_CONV,
25
+ grain_temp: FLOAT_CONV,
26
+ start_vol: FLOAT_CONV,
27
+ grain_weight: FLOAT_CONV,
28
+ infusion_temp: FLOAT_CONV,
29
+ decoction_amt: FLOAT_CONV,
30
+ tun_hc: FLOAT_CONV
31
+ }
32
+
33
+ def self.from_data data
34
+ step = new
35
+ step.data_to_properties data
36
+
37
+ step
38
+ end
39
+ end
40
+ end
41
+ end
@@ -9,16 +9,57 @@ module Brewscribe
9
9
  :image, :image_x, :image_y, :include_starter, :ingredients, :inv_date,
10
10
  :last_modified, :locked, :mash, :mash_ph, :name, :notes, :og_boil_measured,
11
11
  :og_measured, :og_primary, :og_secondary, :old_boil_vol, :old_efficiency,
12
- :old_type, :old_vol, :rating, :raw_data, :rebalance_scale, :running_gravity,
12
+ :old_type, :old_vol, :rating, :rebalance_scale, :running_gravity,
13
13
  :runoff_ph, :starter_size, :stir_plate, :style, :type, :version,
14
14
  :volume_measured
15
15
 
16
+ include Brewscribe::Conversion
17
+
18
+ TYPES = ['Extract', 'Partial Grain', 'All Grain']
19
+
20
+ KEY_CONVERSION = {
21
+ boil_vol_measured: FLOAT_CONV ,
22
+ type: ->(t) { TYPES[t.to_i] },
23
+ carb_vols: FLOAT_CONV,
24
+ date: DATE_CONV,
25
+ desired_ibu: FLOAT_CONV,
26
+ desired_color: FLOAT_CONV,
27
+ desired_og: FLOAT_CONV,
28
+ fg_measured: FLOAT_CONV,
29
+ final_vol_measured: FLOAT_CONV,
30
+ include_starter: BOOLEAN_CONV,
31
+ inv_date: DATE_CONV,
32
+ last_modified: DATE_CONV,
33
+ locked: BOOLEAN_CONV,
34
+ mash_ph: FLOAT_CONV,
35
+ og_boil_measured: FLOAT_CONV,
36
+ og_measured: FLOAT_CONV,
37
+ og_primary: FLOAT_CONV,
38
+ og_secondary: FLOAT_CONV,
39
+ old_boil_vol: FLOAT_CONV,
40
+ old_efficiency: PERCENT_CONV,
41
+ rating: FLOAT_CONV,
42
+ rebalance_scale: BOOLEAN_CONV,
43
+ running_gravity: FLOAT_CONV,
44
+ runoff_ph: FLOAT_CONV,
45
+ starter_size: FLOAT_CONV,
46
+ stir_plate: BOOLEAN_CONV,
47
+ version: FLOAT_CONV,
48
+ volume_measured: FLOAT_CONV
49
+ }
50
+
51
+ alias_method :carbonation, :carb
52
+
53
+
16
54
  def initialize data = {}
17
- data.keys.each do |key|
18
- self.send "#{key}=", data[key]
19
- end
55
+ data_to_properties data
20
56
 
57
+ # @TODO: Base Grain conversion
21
58
  self.ingredients = IngredientList.from_data self.ingredients[:data]
59
+ self.mash = Mash.from_data self.mash
60
+ self.equipment = Equipment.from_data self.equipment
61
+ self.carb = Carbonation.from_data self.carb
62
+ self.style = Style.from_data self.style
22
63
  end
23
64
  end
24
65
  end
@@ -0,0 +1,37 @@
1
+ module Brewscribe
2
+ class Style
3
+ attr_accessor :last_modified, :name, :category, :guide, :letter, :number,
4
+ :type, :min_og, :max_og, :min_fg, :max_fg, :min_ibu, :max_ibu, :min_carb,
5
+ :max_carb, :min_color, :max_color, :min_abv, :max_abv, :description,
6
+ :profile, :ingredients, :examples, :web_link
7
+
8
+ include Brewscribe::Conversion
9
+
10
+ TYPES = ['Ale', 'Lager', 'Mixed', 'Mead', 'Cider', 'Wheat']
11
+
12
+ KEY_CONVERSION = {
13
+ last_modified: DATE_CONV,
14
+ min_og: FLOAT_CONV,
15
+ max_og: FLOAT_CONV,
16
+ min_fg: FLOAT_CONV,
17
+ max_fg: FLOAT_CONV,
18
+ number: INT_CONV,
19
+ min_ibu: FLOAT_CONV,
20
+ max_ibu: FLOAT_CONV,
21
+ letter: ->(k) { Array('A'..'Z')[k.to_i-1] },
22
+ type: ->(k) { TYPES[k.to_i] },
23
+ min_abv: PERCENT_CONV,
24
+ max_abv: PERCENT_CONV,
25
+ min_color: FLOAT_CONV,
26
+ max_color: FLOAT_CONV,
27
+ examples: ->(k) { k.split(', ') }
28
+ }
29
+
30
+ def self.from_data data
31
+ style = new
32
+ style.data_to_properties data
33
+
34
+ style
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Brewscribe
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -8,16 +8,8 @@ describe Brewscribe do
8
8
  Brewscribe::Recipe.any_instance.stub(:parse_raw_data)
9
9
  end
10
10
 
11
- it 'should call #read on the passed IO object' do
12
- file = double()
13
- file.should_receive(:read)
14
- Brewscribe.import(file)
15
- end
16
-
17
11
  it 'should return a Document object' do
18
- file = double()
19
- file.stub(:read)
20
- recipe = Brewscribe.import(file)
12
+ recipe = Brewscribe.import(recipe_file)
21
13
  recipe.should be_a(Brewscribe::Document)
22
14
  end
23
15
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewscribe::Carbonation do
4
+ subject { Brewscribe::Carbonation.from_data RECIPE_HASH[:carb] }
5
+
6
+ describe '#from_data' do
7
+ it { subject.should be_a Brewscribe::Carbonation }
8
+
9
+ it { subject.carb_rate.should be_a Float }
10
+ it { subject.temperature.should be_a Float }
11
+ it { subject.last_modified.should be_a Date }
12
+
13
+
14
+ it 'should set the type to one of the TYPES' do
15
+ Brewscribe::Carbonation::TYPES.should include subject.type
16
+ end
17
+ end
18
+ end
@@ -4,10 +4,8 @@ describe Brewscribe::Document do
4
4
  let(:file) { File.open(File.dirname(__FILE__) + '/support/recipe.bsmx', 'r') }
5
5
  subject { Brewscribe::Document.new file: file }
6
6
 
7
- describe '#parse' do
7
+ describe '#parse_data' do
8
8
  it 'should add a Recipe to recipes when a Recipe entry is found' do
9
- subject.recipes.should have(0).recipes
10
- hash = subject.parse
11
9
  subject.recipes.should have(1).recipe
12
10
  subject.recipes[0].should be_a Brewscribe::Recipe
13
11
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewscribe::Equipment do
4
+ subject { Brewscribe::Equipment.from_data RECIPE_HASH[:equipment] }
5
+
6
+ it { subject.should be_a Brewscribe::Equipment }
7
+ it { subject.last_modified.should be_a Date }
8
+ it { subject.mash_vol.should be_a Float }
9
+ it { subject.tun_mass.should be_a Float }
10
+ it { subject.boil_time.should be_a Float }
11
+ it { subject.tun_specific_heat.should be_a Float }
12
+ it { subject.tun_deadspace.should be_a Float }
13
+ it { subject.tun_adj_deadspace.should be_a Float }
14
+ it { subject.boil_vol.should be_a Float }
15
+ it { subject.old_evap_rate.should be_a Float }
16
+ it { subject.boil_off.should be_a Float }
17
+ it { subject.trub_loss.should be_a Float }
18
+ it { subject.cool_pct.should be_a Float }
19
+ it { subject.top_up_kettle.should be_a Float }
20
+ it { subject.batch_vol.should be_a Float }
21
+ it { subject.fermenter_loss.should be_a Float }
22
+ it { subject.top_up.should be_a Float }
23
+ it { subject.efficiency.should be_a Float }
24
+ it { subject.hop_util.should be_a Float }
25
+
26
+ it 'boil_rate_flag should be a boolean' do
27
+ [true, false].should include subject.boil_rate_flag
28
+ end
29
+
30
+ it 'calc_boil should be a boolean' do
31
+ [true, false].should include subject.calc_boil
32
+ end
33
+
34
+ it 'equip_39 should be a boolean' do
35
+ [true, false].should include subject.equip_39
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewscribe::Mash do
4
+ describe '#from_data' do
5
+ let(:data) { RECIPE_HASH[:mash] }
6
+ subject { Brewscribe::Mash.from_data(data) }
7
+
8
+ it 'returns a Mash object with data preloaded' do
9
+ subject.should be_a Brewscribe::Mash
10
+ subject.name.should == RECIPE_HASH[:mash][:name]
11
+ end
12
+
13
+ it 'converts steps into an array of Mash::Step objects' do
14
+ subject.steps.should be_a Array
15
+ subject.steps[0].should be_a Brewscribe::Mash::Step
16
+ end
17
+
18
+ it { subject.grain_weight.should be_a Float }
19
+ it { subject.grain_temp.should be_a Float }
20
+ it { subject.boil_temp.should be_a Float }
21
+ it { subject.tun_temp.should be_a Float }
22
+ it { subject.sparge_temp.should be_a Float }
23
+ it { subject.ph.should be_a Float }
24
+ it { subject.batch.should be_a Float }
25
+ it { subject.batch_pct.should be_a Float }
26
+ it { subject.tun_deadspace.should be_a Float }
27
+ it { subject.biab_vol.should be_a Float }
28
+ it { subject.tun_vol.should be_a Float }
29
+ it { subject.tun_mass.should be_a Float }
30
+ it { subject.tun_hc.should be_a Float }
31
+ it { subject.last_modified.should be_a Date }
32
+
33
+ it 'batch_even should be a boolean' do
34
+ [true, false].should include subject.batch_even
35
+ end
36
+
37
+ it 'batch_drain should be a boolean' do
38
+ [true, false].should include subject.batch_drain
39
+ end
40
+
41
+ it 'mash_39 should be a boolean' do
42
+ [true, false].should include subject.mash_39
43
+ end
44
+
45
+ it 'biab should be a boolean' do
46
+ [true, false].should include subject.biab
47
+ end
48
+
49
+ it 'equip_adjust should be a boolean' do
50
+ [true, false].should include subject.equip_adjust
51
+ end
52
+ end
53
+ end
@@ -2,18 +2,73 @@ require 'spec_helper'
2
2
 
3
3
  describe Brewscribe::Recipe do
4
4
  let(:recipe_file) { File.read(File.dirname(__FILE__) + '/support/recipe.bsmx') }
5
- let(:data) { Brewscribe::Document.new(data: recipe_file).parse.hash[:recipe] }
5
+ let(:data) { Brewscribe::Document.new(data: recipe_file).hash[:recipe] }
6
6
  subject { Brewscribe::Recipe.new(data) }
7
7
  it 'should convert the recipe data into properties' do
8
8
  subject.brewer.should == 'CadBrew'
9
9
  end
10
10
 
11
- it 'should return a hash for nested recipe attributes' do
12
- subject.equipment.should be_a Hash
13
- subject.equipment[:name].should be_a String
11
+ it 'should convert equipment to an Equipment object' do
12
+ subject.equipment.should be_a Brewscribe::Equipment
13
+ end
14
+
15
+ it 'should convert mash into a Mash object' do
16
+ subject.mash.should be_a Brewscribe::Mash
17
+ end
18
+
19
+ it 'should convert style into a Style object' do
20
+ subject.style.should be_a Brewscribe::Style
21
+ end
22
+
23
+ it 'should convert carb into a Carbonation object' do
24
+ subject.carb.should be_a Brewscribe::Carbonation
25
+ subject.carbonation.should be_a Brewscribe::Carbonation
14
26
  end
15
27
 
16
28
  it 'should contain an IngredientsList' do
17
29
  subject.ingredients.should be_a Brewscribe::IngredientList
18
30
  end
31
+
32
+ it { subject.boil_vol_measured.should be_a Float }
33
+ it { subject.carb_vols.should be_a Float }
34
+ it { subject.desired_ibu.should be_a Float }
35
+ it { subject.desired_color.should be_a Float }
36
+ it { subject.desired_og.should be_a Float }
37
+ it { subject.fg_measured.should be_a Float }
38
+ it { subject.mash_ph.should be_a Float }
39
+ it { subject.og_boil_measured.should be_a Float }
40
+ it { subject.og_measured.should be_a Float }
41
+ it { subject.og_primary.should be_a Float }
42
+ it { subject.og_secondary.should be_a Float }
43
+ it { subject.old_boil_vol.should be_a Float }
44
+ it { subject.old_efficiency.should be_a Float }
45
+ it { subject.running_gravity.should be_a Float }
46
+ it { subject.runoff_ph.should be_a Float }
47
+ it { subject.starter_size.should be_a Float }
48
+ it { subject.rating.should be_a Float }
49
+ it { subject.version.should be_a Float }
50
+ it { subject.volume_measured.should be_a Float }
51
+ it { subject.date.should be_a Date }
52
+ it { subject.inv_date.should be_a Date }
53
+ it { subject.last_modified.should be_a Date }
54
+
55
+ it 'should set the type to one of the TYPES' do
56
+ Brewscribe::Recipe::TYPES.should include subject.type
57
+ end
58
+
59
+ it 'should set include_starter to boolean' do
60
+ [true, false].should include subject.include_starter
61
+ end
62
+
63
+ it 'should set locked to boolean' do
64
+ [true, false].should include subject.locked
65
+ end
66
+
67
+ it 'should set rebalance_scale to boolean' do
68
+ [true, false].should include subject.rebalance_scale
69
+ end
70
+
71
+ it 'should set stir_plate to boolean' do
72
+ [true, false].should include subject.stir_plate
73
+ end
19
74
  end
@@ -1,3 +1,4 @@
1
1
  $: << '../lib'
2
2
 
3
3
  require 'brewscribe'
4
+ require './spec/support/recipe_hash'
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewscribe::Mash::Step do
4
+ describe '#from_data' do
5
+ let(:mashstep) { RECIPE_HASH[:mash][:steps][:data][:mashstep] }
6
+ subject { Brewscribe::Mash::Step.from_data mashstep }
7
+
8
+ it { subject.should be_a Brewscribe::Mash::Step }
9
+ it { subject.last_modified.should be_a Date }
10
+ it { subject.infusion.should be_a Float }
11
+ it { subject.step_temp.should be_a Float }
12
+ it { subject.tun_addition.should be_a Float }
13
+ it { subject.tun_hc.should be_a Float }
14
+ it { subject.tun_vol.should be_a Float }
15
+ it { subject.tun_temp.should be_a Float }
16
+ it { subject.tun_mass.should be_a Float }
17
+ it { subject.start_temp.should be_a Float }
18
+ it { subject.grain_temp.should be_a Float }
19
+ it { subject.start_vol.should be_a Float }
20
+ it { subject.grain_weight.should be_a Float }
21
+ it { subject.infusion_temp.should be_a Float }
22
+ it { subject.decoction_amt.should be_a Float }
23
+ it { subject.step_time.should be_a Fixnum }
24
+ it { subject.rise_time.should be_a Fixnum }
25
+
26
+ it 'should set the type to one of the TYPES' do
27
+ Brewscribe::Mash::Step::TYPES.should include subject.type
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Brewscribe::Style do
4
+ subject { Brewscribe::Style.from_data RECIPE_HASH[:style] }
5
+
6
+ it { subject.should be_a Brewscribe::Style }
7
+ it { subject.last_modified.should be_a Date }
8
+ it { subject.min_og.should be_a Float }
9
+ it { subject.max_og.should be_a Float }
10
+ it { subject.min_fg.should be_a Float }
11
+ it { subject.max_fg.should be_a Float }
12
+ it { subject.min_ibu.should be_a Float }
13
+ it { subject.max_ibu.should be_a Float }
14
+ it { subject.min_abv.should be_a Float }
15
+ it { subject.max_abv.should be_a Float }
16
+ it { subject.min_color.should be_a Float }
17
+ it { subject.max_color.should be_a Float }
18
+ it { subject.number.should be_a Fixnum }
19
+ it { subject.letter.should == 'F' }
20
+
21
+ it 'examples should be an array of beer names' do
22
+ subject.examples.should be_a Array
23
+ subject.examples.should have(17).examples
24
+ end
25
+
26
+ it 'should set the type to one of the TYPES' do
27
+ Brewscribe::Style::TYPES.should include subject.type
28
+ end
29
+ end
@@ -0,0 +1,98 @@
1
+ RECIPE_HASH = {
2
+ equipment: {
3
+ :last_modified=>"1978-12-22", :name=>"Pot ( 4 Gal/15.1 L) - Extract",
4
+ :mash_vol=>"512.0000000", :tun_mass=>"64.0000000", :boil_rate_flag=>"1",
5
+ :tun_specific_heat=>"0.1200000", :tun_deadspace=>"0.0000000",
6
+ :tun_adj_deadspace=>"1", :calc_boil=>"1", :boil_vol=>"469.7600000",
7
+ :boil_time=>"60.0000000", :old_evap_rate=>"9.0000000", :equip_39=>"1",
8
+ :boil_off=>"37.1200000", :trub_loss=>"64.0000000", :cool_pct=>"4.0000000",
9
+ :top_up_kettle=>"0.0000000", :batch_vol=>"640.0000000",
10
+ :fermenter_loss=>"51.2000000", :top_up=>"288.0000000", :efficiency=>"72.0000000",
11
+ :hop_util=>"100.0000000",
12
+ :notes=>"Simple Brew Pot with a 4 Gallon Capacity - leaving a workable boil volume of around 3.25 gal. Used for extract or partial mash brewing."
13
+ },
14
+ mash: {
15
+ :last_modified=>"1978-12-22", :name=>"Single Infusion, Light Body, No Mash Out",
16
+ :grain_weight=>"72.0000000", :grain_temp=>"72.0000000",
17
+ :boil_temp=>"212.0000000", :tun_temp=>"72.0000000", :ph=>"5.4000000",
18
+ :sparge_temp=>"168.0000000", :batch=>"0", :batch_pct=>"100.0000000",
19
+ :batch_even=>"0", :batch_drain=>"0", :mash_39=>"1", :tun_deadspace=>"0.0000000",
20
+ :biab_vol=>"640.0000000", :biab=>"0",
21
+ :notes=>"Simple single infusion mash for use with most modern well modified grains (about 95% of the time).",
22
+ :steps=> {
23
+ :last_modified=>"2012-03-14",
24
+ :name=>"steps",
25
+ :type=>"7432",
26
+ :owndata=>"1",
27
+ :tid=>"7149",
28
+ :size=>"1",
29
+ :_xname=>"steps",
30
+ :allocinc=>"16",
31
+ :data=>{
32
+ :mashstep=>{
33
+ :last_modified=>"2012-03-12",
34
+ :name=>"Mash In",
35
+ :type=>"0",
36
+ :infusion=>"180.0000000",
37
+ :step_temp=>"148.0000000",
38
+ :step_time=>"75.0000000",
39
+ :rise_time=>"2.0000000",
40
+ :tun_addition=>"0.0000000",
41
+ :tun_hc=>"0.1200000",
42
+ :tun_vol=>"512.0000000",
43
+ :tun_temp=>"72.0000000",
44
+ :tun_mass=>"0.0000000",
45
+ :start_temp=>"0.0000000",
46
+ :grain_temp=>"72.0000000",
47
+ :start_vol=>"0.0000000",
48
+ :grain_weight=>"72.0000000",
49
+ :infusion_temp=>"159.0738063",
50
+ :decoction_amt=>"0.0000000"
51
+ }
52
+ },
53
+ :_texpanded=>"1"
54
+ },
55
+ :equip_adjust=>"0",
56
+ :tun_vol=>"512.0000000",
57
+ :tun_mass=>"64.0000000",
58
+ :tun_hc=>"0.1200000"
59
+ },
60
+ :carb => {
61
+ :last_modified=>"1978-12-30",
62
+ :name=>"Corn Sugar",
63
+ :temperature=>"70.0000000",
64
+ :type=>"0", :primer_name=>"Corn Sugar",
65
+ :carb_rate=>"100.0000000",
66
+ :notes=>"Use corn sugar for priming the beer"
67
+ },
68
+ :style => {
69
+ :last_modified=>"2012-03-12",
70
+ :name=>"Imperial Stout",
71
+ :category=>"Stout",
72
+ :guide=>"BJCP 2008",
73
+ :letter=>"6",
74
+ :number=>"13",
75
+ :type=>"0",
76
+ :min_og=>"1.0750000",
77
+ :max_og=>"1.1150000",
78
+ :min_fg=>"1.0180000",
79
+ :max_fg=>"1.0300000",
80
+ :min_ibu=>"50.0000000",
81
+ :max_ibu=>"90.0000000",
82
+ :min_carb=>"1.8000000",
83
+ :max_carb=>"2.6000000",
84
+ :min_color=>"30.0000000",
85
+ :max_color=>"40.0000000",
86
+ :min_abv=>"8.0000000",
87
+ :max_abv=>"12.0000000",
88
+ :description=>"An intensely flavored, big, dark ale. Roasty, fruity, and bittersweet, with a noticeable alcohol presence. Dark fruit flavors meld with roasty, burnt, or almost tarlike sensations. Like a black barleywine with every dimension of flavor coming into play. Brewed to high gravity and hopping level in England for export to the Baltic States and Russia. Said to be popular with the Russian Imperial Court. Today is even more popular with American craft brewers, who have extended the style with unique American characteristics. Variations exist, with English and American interpretations (predictably, the American versions have more bitterness, roasted character, and finishing hops, while the English varieties
89
+ reflect a more complex specialty malt character and a more forward ester profile). The wide range of allowable characteristics allow for maximum brewer creativity.",
90
+ :profile=>"Aroma: Rich and complex, with variable amounts of roasted grains, maltiness, fruity esters, hops, and alcohol. The roasted malt character can take on coffee, dark chocolate, or slightly burnt tones and can be light to moderately strong. The malt aroma can be subtle to rich and barleywine-like, depending on the gravity and grain bill. May optionally show a slight specialty malt character (e.g., caramel), but this should only add complexity and not dominate. Fruity esters may be low to moderately strong, and may take on a complex, dark fruit (e.g., plums, prunes, raisins) character. Hop aroma can be very low to quite aggressive, and may contain any hop variety. An alcohol character may be present, but shouldnt be sharp, hot or solventy. Aged versions may have a slight vinous or portlike quality, but shouldnt be sour. No diacetyl. The balance can vary with any of the aroma elements taking center stage. Not all possible aromas described need be present; many interpretations are possible. Aging affects the intensity, balance and smoothness of aromatics.\n Appearance: Color may range from very dark reddish-brown to jet black. Opaque. Deep tan to dark brown head. Generally has a well-formed head, although head retention may be low to moderate. High alcohol and viscosity may be visible in legs when beer is swirled in a glass.\n
91
+ Flavor: Rich, deep, complex and frequently quite intense, with variable amounts of roasted malt/grains, maltiness, fruity esters, hop bitterness and flavor, and alcohol. Medium to aggressively high bitterness. Medium-low to high hop flavor (any variety). Moderate to aggressively high roasted malt/grain flavors can suggest bittersweet or unsweetened chocolate, cocoa, and/or strong coffee. A slightly burnt grain, burnt currant or tarry character may be evident. Fruity esters may be low to intense, and can take on a dark fruit character (raisins, plums, or prunes). Malt backbone can be balanced and supportive to rich and barleywine-like, and may optionally show some supporting caramel, bready or toasty flavors. Alcohol strength
92
+ should be evident, but not hot, sharp, or solventy. No diacetyl. The palate and finish can vary from relatively dry to moderately sweet, usually with some lingering roastiness, hop bitterness and warming character. The balance and intensity of flavors can be affected by aging, with some flavors becoming more subdued over time and some aged, vinous or port-like qualities developing.\n
93
+ Mouthfeel: Full to very full-bodied and chewy, with a velvety, luscious texture (although the body may decline with long conditioning). Gentle smooth warmth from alcohol should be present and noticeable. Should not be syrupy and underattenuated. Carbonation may be low to moderate, depending on age and conditioning.",
94
+ :ingredients=>"Well-modified pale malt, with generous quantities of roasted malts and/or grain. May have a complex grain bill using virtually any variety of malt. Any type of hops may be used. Alkaline water balances the abundance of acidic roasted grain in the grist. American or English ale yeast.",
95
+ :examples=>"Three Floyds Dark Lord, Bells Expedition Stout, North Coast Old Rasputin Imperial Stout, Stone Imperial Stout, Samuel Smith Imperial Stout, Scotch Irish Tsarina Katarina Imperial Stout, Thirsty Dog Siberian Night, Deschutes The Abyss, Great Divide Yeti, Southampton Russian Imperial Stout, Rogue Imperial Stout, Bear Republic Big Bear Black Stout, Great Lakes Blackout Stout, Avery The Czar, Founders Imperial Stout, Victory Storm King, Brooklyn Black Chocolate Stout",
96
+ :web_link=>"http://www.bjcp.org"
97
+ }
98
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brewscribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-06 00:00:00.000000000 Z
12
+ date: 2012-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70355888747600 !ruby/object:Gem::Requirement
16
+ requirement: &70204025869360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70355888747600
24
+ version_requirements: *70204025869360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: guard
27
- requirement: &70355888746600 !ruby/object:Gem::Requirement
27
+ requirement: &70204025868780 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70355888746600
35
+ version_requirements: *70204025868780
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: guard-rspec
38
- requirement: &70355888744860 !ruby/object:Gem::Requirement
38
+ requirement: &70204025868180 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,21 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70355888744860
46
+ version_requirements: *70204025868180
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70204025867440 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70204025867440
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: nokogiri
49
- requirement: &70355888731820 !ruby/object:Gem::Requirement
60
+ requirement: &70204025866940 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ! '>='
@@ -54,7 +65,7 @@ dependencies:
54
65
  version: '0'
55
66
  type: :runtime
56
67
  prerelease: false
57
- version_requirements: *70355888731820
68
+ version_requirements: *70204025866940
58
69
  description:
59
70
  email:
60
71
  - cadwallion@gmail.com
@@ -64,6 +75,7 @@ extra_rdoc_files: []
64
75
  files:
65
76
  - .gitignore
66
77
  - .rspec
78
+ - .travis.yml
67
79
  - CHANGELOG.md
68
80
  - Gemfile
69
81
  - Guardfile
@@ -72,23 +84,34 @@ files:
72
84
  - Rakefile
73
85
  - brewscribe.gemspec
74
86
  - lib/brewscribe.rb
87
+ - lib/brewscribe/carbonation.rb
75
88
  - lib/brewscribe/conversion.rb
76
89
  - lib/brewscribe/document.rb
90
+ - lib/brewscribe/equipment.rb
77
91
  - lib/brewscribe/grain.rb
78
92
  - lib/brewscribe/hops.rb
79
93
  - lib/brewscribe/ingredient_list.rb
94
+ - lib/brewscribe/mash.rb
95
+ - lib/brewscribe/mash/step.rb
80
96
  - lib/brewscribe/recipe.rb
97
+ - lib/brewscribe/style.rb
81
98
  - lib/brewscribe/version.rb
82
99
  - lib/brewscribe/yeast.rb
83
100
  - spec/brewscribe_spec.rb
101
+ - spec/carbonation_spec.rb
84
102
  - spec/conversion_spec.rb
85
103
  - spec/document_spec.rb
104
+ - spec/equipment_spec.rb
86
105
  - spec/grain_spec.rb
87
106
  - spec/hops_spec.rb
88
107
  - spec/ingredient_list_spec.rb
108
+ - spec/mash_spec.rb
89
109
  - spec/recipe_spec.rb
90
110
  - spec/spec_helper.rb
111
+ - spec/step_spec.rb
112
+ - spec/style_spec.rb
91
113
  - spec/support/recipe.bsmx
114
+ - spec/support/recipe_hash.rb
92
115
  - spec/yeast_spec.rb
93
116
  homepage: ''
94
117
  licenses: []
@@ -104,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
127
  version: '0'
105
128
  segments:
106
129
  - 0
107
- hash: -1660558714471080415
130
+ hash: 1408493112336442463
108
131
  required_rubygems_version: !ruby/object:Gem::Requirement
109
132
  none: false
110
133
  requirements:
@@ -113,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
136
  version: '0'
114
137
  segments:
115
138
  - 0
116
- hash: -1660558714471080415
139
+ hash: 1408493112336442463
117
140
  requirements: []
118
141
  rubyforge_project:
119
142
  rubygems_version: 1.8.17
@@ -122,12 +145,18 @@ specification_version: 3
122
145
  summary: A Beersmith (.bsmx) file parser
123
146
  test_files:
124
147
  - spec/brewscribe_spec.rb
148
+ - spec/carbonation_spec.rb
125
149
  - spec/conversion_spec.rb
126
150
  - spec/document_spec.rb
151
+ - spec/equipment_spec.rb
127
152
  - spec/grain_spec.rb
128
153
  - spec/hops_spec.rb
129
154
  - spec/ingredient_list_spec.rb
155
+ - spec/mash_spec.rb
130
156
  - spec/recipe_spec.rb
131
157
  - spec/spec_helper.rb
158
+ - spec/step_spec.rb
159
+ - spec/style_spec.rb
132
160
  - spec/support/recipe.bsmx
161
+ - spec/support/recipe_hash.rb
133
162
  - spec/yeast_spec.rb