brewser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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