brewser 0.1.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.
@@ -0,0 +1,50 @@
1
+ module Brewser
2
+ class Recipe < Model
3
+ belongs_to :batch
4
+
5
+ property :date_created, Date
6
+ property :name, String, :required => true
7
+ property :description, String, :length => 65535
8
+
9
+ property :type, String, :set => ['Ale', 'Lager', 'Wheat', 'Cider', 'Mead', 'Hybrid']
10
+ property :method, String, :set => ['Extract', 'Partial Mash', 'All Grain'], :required => true
11
+
12
+ has 1, :style
13
+ validates_presence_of :style
14
+
15
+ property :brewer, String
16
+ property :recipe_volume, Volume, :required => true
17
+ property :boil_volume, Volume, :required => true
18
+ property :boil_time, Time, :required => true
19
+ property :recipe_efficiency, Float
20
+ validates_presence_of :recipe_efficiency, :if => proc { |t| t.method != 'Extract' }
21
+
22
+ has n, :hops
23
+ has n, :fermentables
24
+ has n, :additives
25
+ has n, :yeasts
26
+
27
+ has 1, :mash_schedule
28
+ has 1, :fermentation_schedule
29
+ has 1, :water_profile
30
+
31
+ property :asst_brewer, String
32
+ property :taste_notes, String, :length => 65535
33
+ property :taste_rating, Float
34
+
35
+ property :estimated_og, Float
36
+ property :estimated_fg, Float
37
+ property :estimated_color, Float
38
+ property :estimated_ibu, Float
39
+ property :estimated_abv, Float
40
+
41
+ property :source, String, :length => 65535
42
+ property :url, String, :length => 65535
43
+
44
+ property :carbonation_level, Float
45
+
46
+ validates_presence_of :mash_schedule, :if => proc { |t| t.method != 'Extract' }
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,29 @@
1
+ module Brewser
2
+ class Style < Brewser::Model
3
+ belongs_to :recipe
4
+
5
+ property :name, String, :required => true
6
+ property :category, String, :required => true
7
+ property :category_number, String, :required => true
8
+ property :style_letter, String, :required => true
9
+ property :style_guide, String, :required => true
10
+ property :type, String, :set => %w(Lager Ale Mead Wheat Mixed Cider)
11
+ property :og_min, Float
12
+ property :og_max, Float
13
+ property :fg_min, Float
14
+ property :fg_max, Float
15
+ property :ibu_min, Float
16
+ property :ibu_max, Float
17
+ property :color_min, Float
18
+ property :color_max, Float
19
+
20
+ property :carb_min, Float
21
+ property :carb_max, Float
22
+ property :abv_min, Float
23
+ property :abv_max, Float
24
+ property :notes, String, :length => 65535
25
+ property :profile, String, :length => 65535
26
+ property :ingredients, String, :length => 65535
27
+ property :examples, String, :length => 65535
28
+ end
29
+ end
@@ -0,0 +1,102 @@
1
+ require 'ruby-units'
2
+
3
+ module Brewser::Model::Units
4
+
5
+ class Units < DataMapper::Property::String
6
+
7
+ # Declare this property as custom
8
+ def custom?
9
+ true
10
+ end
11
+
12
+ # Compare the values kind (mass, volume, etc) to the object classes kind_of
13
+ #
14
+ # @param [Unit] value value to be checked
15
+ # @return [Boolean] returns true if value is of the correct kind
16
+ def valid_kind?(value)
17
+ value.kind == kind_of
18
+ end
19
+
20
+ #
21
+ def primitive?(value)
22
+ value.is_a?(Unit) && valid_kind?(value)
23
+ end
24
+
25
+ def load(value)
26
+ return if value.nil?
27
+ if !value.u.unitless?
28
+ raise(ArgumentError, "#{value.inspect} is not a #{kind_of}") unless value.u.kind == kind_of
29
+ value.u
30
+ else
31
+ "#{value} #{base_unit}".u
32
+ end
33
+ end
34
+
35
+ def dump(value)
36
+ return if value.nil?
37
+ value.u.to_s
38
+ end
39
+
40
+ def typecast_to_primitive(value)
41
+ load(value)
42
+ end
43
+ end
44
+
45
+ # Represents a weight. Default scale is kilograms, since that's what
46
+ # BeerXML uses. But can handle conversion/display of other units.
47
+ class Weight < Units
48
+ def kind_of
49
+ :mass
50
+ end
51
+ def base_unit
52
+ 'kg'
53
+ end
54
+ end
55
+
56
+ # A volume, in liters.
57
+ class Volume < Units
58
+ def kind_of
59
+ :volume
60
+ end
61
+ def base_unit
62
+ 'l'
63
+ end
64
+ end
65
+
66
+ class WeightOrVolume < Units
67
+
68
+ def valid_kind?(value)
69
+ value.kind == :mass || value.kind == :volume
70
+ end
71
+ def base_unit
72
+ end
73
+ end
74
+
75
+ # A temperature, in deg C.
76
+ class Temperature < Units
77
+ def kind_of
78
+ :temperature
79
+ end
80
+ def base_unit
81
+ 'dC'
82
+ end
83
+ end
84
+
85
+ # A time, in minutes.
86
+ class Time < Units
87
+ def kind_of
88
+ :time
89
+ end
90
+ def base_unit
91
+ 'min'
92
+ end
93
+ end
94
+
95
+ # Time, but in days.
96
+ class TimeInDays < Time
97
+ def base_unit
98
+ 'days'
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,16 @@
1
+ module Brewser
2
+ class WaterProfile < Model
3
+ belongs_to :recipe
4
+
5
+ property :name, String, :required => true
6
+
7
+ property :calcium, Float, :required => true
8
+ property :magnesium, Float, :required => true
9
+ property :sodium, Float, :required => true
10
+ property :chloride, Float, :required => true
11
+ property :sulfates, Float, :required => true
12
+ property :bicarbonate, Float
13
+ property :alkalinity, Float
14
+ property :ph, Float
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ module Brewser
2
+ class Yeast < Model
3
+ belongs_to :recipe
4
+
5
+ property :name, String, :required => true
6
+ property :description, String, :length => 65535
7
+
8
+ property :type, String, :set => ['Ale', 'Lager', 'Wheat', 'Wine', 'Champagne'], :required => true
9
+
10
+ property :supplier, String
11
+ property :catalog, String
12
+
13
+ property :min_temperature, Temperature
14
+ property :max_temperature, Temperature
15
+ property :flocculation, String, :set => ['Low', 'Medium', 'High', 'Very High']
16
+ property :attenuation, Float
17
+ property :best_for, String
18
+ property :max_reuse, Integer
19
+
20
+ # Recipes
21
+ property :add_to_secondary?, Boolean
22
+ property :form, String, :set => ['Liquid', 'Dry', 'Slant', 'Culture'], :required => true
23
+ property :amount, WeightOrVolume, :required => true
24
+
25
+ # These are applicable only in Batches
26
+ property :times_cultured, Integer
27
+
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'ruby-units'
2
+
3
+ Unit.redefine!('celsius') do |celsius|
4
+ celsius.aliases = %w{degC dC C celsius centigrade}
5
+ end
6
+
7
+ Unit.redefine!('fahrenheit') do |fahrenheit|
8
+ fahrenheit.aliases = %w{degF dF F fahrenheit}
9
+ end
10
+
11
+ Unit.redefine!('gram') do |gram|
12
+ gram.aliases = %w{g gm gram grams gramme grammes}
13
+ end
14
+
15
+ Unit.define('barrel') do |barrel|
16
+ barrel.definition = Unit('31 gal')
17
+ barrel.aliases = %w{bbl bbls barrel barrels}
18
+ end
19
+
20
+ Unit.define('keg') do |keg|
21
+ keg.definition = Unit('1/2 barrel')
22
+ keg.aliases = %w{keg kegs}
23
+ end
24
+
25
+ # Add convienience method
26
+ class Unit
27
+ # returns the scalar value convert to other units
28
+ def scalar_in(other)
29
+ to(other).scalar.to_f
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Brewser
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,39 @@
1
+ require "spec_helper"
2
+
3
+ describe "Basic spec" do
4
+
5
+ it "should be able to identifiy json as BrewSON" do
6
+ Brewser.identify('{"test":"json"}').should == BrewSON
7
+ end
8
+
9
+ it "should be able to identifiy xml as BeerXML" do
10
+ Brewser.identify(read_file('beerxmlv1/recipes.xml')).should == BeerXML
11
+ end
12
+
13
+ it "should be able to identifiy xml as BeerXML2" do
14
+ Brewser.identify(read_file('beerxmlv2/belgian_white.xml')).should == BeerXML2
15
+ end
16
+
17
+ it "should be able to identify text as ProMash Txt" do
18
+ Brewser.identify(read_file('promash/PumpkinAle.txt')).should == ProMashTxt
19
+ end
20
+
21
+ it "should raise an error if it cannot identify content" do
22
+ Brewser.identify("Some unknown content").should == ProMashRec
23
+ end
24
+
25
+ context "BeerXML v1" do
26
+
27
+ it "should extract multiple recipes" do
28
+ recipes = Brewser.parse(read_file('beerxmlv1/recipes.xml'))
29
+ recipes.count.should == 4
30
+ recipes.first.class.should == BeerXML::Recipe
31
+ end
32
+
33
+ it "should raise an error when encountered" do
34
+ lambda { Brewser.parse(read_file('beerxmlv1/broken.xml')) }.should raise_error
35
+ end
36
+
37
+ end
38
+ end
39
+
@@ -0,0 +1,159 @@
1
+ require "spec_helper"
2
+
3
+ describe "BeerXML tests" do
4
+
5
+ context "BeerXML v1" do
6
+
7
+ before :each do
8
+ @recipe = Brewser.parse(read_file('beerxmlv1/recipes.xml')).first
9
+ end
10
+
11
+ it "should deserialize the base recipe data" do
12
+ @recipe.name.should == "Burton Ale"
13
+ @recipe.brewer.should == "Brad Smith"
14
+ @recipe.method.should == "All Grain"
15
+ @recipe.type.should == "Ale"
16
+
17
+ @recipe.recipe_volume.class.should == Unit
18
+ @recipe.recipe_volume.kind.should == :volume
19
+ @recipe.recipe_volume.scalar_in('gal').should be_within(0.01).of(5)
20
+
21
+ @recipe.boil_volume.class.should == Unit
22
+ @recipe.boil_volume.kind.should == :volume
23
+ @recipe.boil_volume.scalar_in('gal').should be_within(0.01).of(5.5)
24
+
25
+ @recipe.boil_time.class.should == Unit
26
+ @recipe.boil_time.kind.should == :time
27
+ @recipe.boil_time.should == "60 min".u
28
+
29
+ @recipe.recipe_efficiency.should == 72.0
30
+
31
+ @recipe.estimated_og.should == 1.056
32
+ @recipe.estimated_fg.should == 1.015
33
+ @recipe.estimated_color.should == 7.0
34
+ @recipe.estimated_ibu.should == 32.4
35
+ @recipe.estimated_abv.should == 5.3
36
+ end
37
+
38
+ it "should deserialize the hop data" do
39
+ @recipe.hops.count.should == 4
40
+ h=@recipe.hops[1]
41
+ h.name.should == "Northern Brewer"
42
+ h.origin.should == "Germany"
43
+ h.alpha_acids.should == 7.5
44
+ h.amount.should === "0.50 oz".u
45
+ h.added_when.should == "Boil"
46
+ h.time.should === "60 min".u
47
+ h.description.should_not be_nil
48
+ h.type.should == "Both"
49
+ h.form.should == "Pellet"
50
+ h.beta_acids.should == 4.0
51
+ h.storageability.should == 35.0
52
+ end
53
+
54
+ it "should deserialize the fermentable data" do
55
+ @recipe.fermentables.count.should == 3
56
+ f=@recipe.fermentables[0]
57
+ f.name.should == "Pale Malt (2 Row) UK"
58
+ f.origin.should == "United Kingdom"
59
+ f.type.should == "Grain"
60
+ f.description.should_not be_nil
61
+ f.amount.should === "8 lb".u
62
+ f.potential.should == 1.036
63
+ f.color.should == 2.5
64
+ f.moisture.should == 4.0
65
+ f.diastatic_power.should == 45.0
66
+ f.protein.should == 10.1
67
+ f.late_addition.should == false
68
+ f.recommend_mash.should == false
69
+ f.max_in_batch.should == 100.0
70
+ f.ibu_gal_per_lb.should == 0.0
71
+ end
72
+
73
+ it "should deserialize the additive data" do
74
+ @recipe.additives.count.should == 2
75
+ a=@recipe.additives[0]
76
+ a.name.should == "Irish Moss"
77
+ a.type.should == "Fining"
78
+ a.description.should_not be_nil
79
+ a.added_when.should == "Boil"
80
+ a.amount.should == "0.25 tsp".u
81
+ a.time.should == "10 min".u
82
+ end
83
+
84
+ it "should deserialize the yeast data" do
85
+ @recipe.yeasts.count.should == 1
86
+ y=@recipe.yeasts.first
87
+ y.name.should == "Burton Ale"
88
+ y.type.should == "Ale"
89
+ y.form.should == "Liquid"
90
+ y.supplier.should == "White Labs"
91
+ y.catalog.should == "WLP023"
92
+ y.description.should_not be_nil
93
+ y.best_for.should_not be_nil
94
+ y.min_temperature.should == "68 dF".u
95
+ y.max_temperature.should == "73 dF".u
96
+ y.amount.should == "35 ml".u
97
+ y.add_to_secondary?.should == false
98
+ y.flocculation.should == "Medium"
99
+ y.attenuation.should == 72.0
100
+ y.max_reuse.should == 5
101
+ y.times_cultured.should == 0
102
+ end
103
+
104
+ it "should deserialize the mash schedule data" do
105
+ m=@recipe.mash_schedule
106
+ m.name.should == "Single Infusion, Full Body"
107
+ m.grain_temp.should == "72 dF".u
108
+ m.sparge_temp.should == "168 dF".u
109
+ end
110
+
111
+ it "should deserialize the mash step data" do
112
+ m=@recipe.mash_schedule
113
+ m.mash_steps.count.should == 2
114
+ s=m.mash_steps[0]
115
+ # @TODO Fix this
116
+ s.index.should == 1
117
+ s.name.should == "Mash In"
118
+ s.type.should == "Infusion"
119
+ s.description.should_not be_nil
120
+ s.rest_time.should == "45 min".u
121
+ s.rest_temperature.should == "70 dC".u
122
+ s.infusion_volume.should == "11.25 qt".u
123
+ s.infusion_temperature.should == "170.5 dF".u
124
+ s.water_to_grain_ratio.should == 1.25
125
+ end
126
+
127
+ it "should deserialize the fermentation step data" do
128
+ f=@recipe.fermentation_schedule
129
+ f.fermentation_steps.count.should == 3
130
+ s=f.fermentation_steps[0]
131
+ s.index.should == 1
132
+ s.name.should == "Primary"
133
+ s.time.should == "4 days".u
134
+ s.temperature.should == "68 dF".u
135
+ end
136
+
137
+ it "should deserialize the water profile data" do
138
+ w=@recipe.water_profile
139
+ w.name.should == "Burton On Trent, UK"
140
+ w.description.should == "Distinctive pale ales strongly hopped. Very hard water accentuates the hops flavor.\nExample: Bass Ale"
141
+ w.calcium.should == 295.0
142
+ w.bicarbonate.should == 300.0
143
+ w.sulfates.should == 725.0
144
+ w.chloride.should == 25.0
145
+ w.sodium.should == 55.0
146
+ w.magnesium.should == 45.0
147
+ w.ph.should ==8.0
148
+ end
149
+
150
+ it "should deserialize the style data" do
151
+ s=@recipe.style
152
+ s.name.should == "English Pale Ale"
153
+ s.category.should == "Bitter & English Pale Ale"
154
+ s.style_letter.should == "A"
155
+ s.style_guide.should == "BJCP 1999"
156
+ s.type.should == "Ale"
157
+ end
158
+ end
159
+ end