brewser 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,7 +3,7 @@ brewser
3
3
 
4
4
  Brewser is a ruby library for parsing and generating serialized brewing data
5
5
 
6
- Currently brewser is very early in development but will eventually support the following input formats:
6
+ Currently brewser is early in development but will eventually support the following input formats:
7
7
  * [BeerXML (v1, v2)](http://beerxml.org)
8
8
  * BrewSON - Brewser Recipe and Batches in JSON
9
9
  * [ProMash (.rec and text exports)](http://www.promash.com)
@@ -11,3 +11,27 @@ Currently brewser is very early in development but will eventually support the f
11
11
  Input files are deserialized into a common object model for consumption. Brewser supports these output formats:
12
12
  * BeerXML
13
13
  * BrewSON
14
+
15
+ # Status
16
+
17
+ Currently, brewser can import BeerXML v1, ProMash .rec files as well as ProMash Recipe Reports (Text files). Each
18
+ file format contains different levels of details. As a result there is no uniformity amongst the various format. BeerXML v1
19
+ is at this time the most complete serialized format.
20
+
21
+ # Installation
22
+
23
+ Just add brewser to your Gemfile
24
+
25
+ gem 'brewser'
26
+
27
+ and run bundle install
28
+
29
+ # Using
30
+
31
+ Brewser will attempt to identify the input file by content and delegate processing to the correct engine. ProMash .rec files are not easy to identify, as a result this is the last engine attempted and it will try to decode any file you give it. This is likely to result in garbage if the file is not a properly formatted .rec file.
32
+
33
+ To load a BeerXML v1 file:
34
+
35
+ Brewser.parse(File.read("samples/beerxmlv1/recipes.xml"))
36
+
37
+ This will return an array of BeerXML::Recipe objects.
@@ -18,8 +18,8 @@ class BeerXML < Brewser::Engine
18
18
  ("BeerXML::#{inner.node_name.downcase.camelcase}".constantize).from_xml(inner)
19
19
  end
20
20
  return cleanup(objects)
21
- # rescue
22
- # raise Error, "BeerXML engine encountered an issue and can not continue"
21
+ rescue
22
+ raise Error, "BeerXML engine encountered an issue and can not continue"
23
23
  end
24
24
  end
25
25
 
@@ -117,7 +117,6 @@ class BeerXML::Fermentable < Brewser::Fermentable
117
117
  def cleanup
118
118
  self.amount = display_amount.present? ? display_amount.u : "#{uncast_amount} kg".u
119
119
  self.potential = uncast_potential.present? ? uncast_potential.to_f : 1+(46*(yield_percent/100))/1000
120
- self.ppg = (potential-1)*1000
121
120
  end
122
121
 
123
122
  end
@@ -10,9 +10,7 @@ class BeerXML2 < Brewser::Engine
10
10
  end
11
11
 
12
12
  def deserialize(string_or_io)
13
- results = parse_xml(string_or_io)
14
- puts results.inspect
15
- return results
13
+ parse_xml(string_or_io)
16
14
  end
17
15
 
18
16
  def serialize(brewser_model)
@@ -7,10 +7,19 @@ class BrewSON < Brewser::Engine
7
7
  end
8
8
 
9
9
  def deserialize(string_or_io)
10
+ begin
11
+ JSON.parse(string_or_io)
12
+ rescue
13
+ raise Error, "BrewSON engine encountered an issue and can not continue"
14
+ end
10
15
  end
11
16
 
12
17
  def serialize(brewser_model)
13
- brewser_model.deep_json
18
+ begin
19
+ JSON.generate(brewser_model)
20
+ rescue
21
+ raise Error, "BrewSON engine encountered an issue and can not continue"
22
+ end
14
23
  end
15
24
 
16
25
  end
@@ -32,7 +32,8 @@ class ProMashRec < Brewser::Engine
32
32
  float :humulene
33
33
  float :caryphylene
34
34
  int8 :type
35
- int8 :form
35
+ bit4 :form
36
+ bit4 :unknown
36
37
  float :storage_factor
37
38
  string :taste_notes, :length => 155, :trim_padding => true
38
39
  string :origin, :length => 55, :trim_padding => true
@@ -110,26 +111,29 @@ class ProMashRec < Brewser::Engine
110
111
  skip length: 8
111
112
  end
112
113
 
113
- class MashStep < RecEntry
114
- string :name, :length => 255, :trim_padding => true
115
- int8 :type
116
- int32 :start_temp
117
- int32 :stop_temp
118
- int32 :step_temp
119
- int32 :rest_time
120
- int32 :step_time
121
- float :thickness
122
- float :amount
123
- skip length: 8
124
- end
125
-
126
- class MashSchedule < RecEntry
127
- int32 :_steps_count
128
- int32 :grain_temp
129
- skip length: 4
130
- array :mash_steps, :type => :mash_step, :read_until => lambda { index == 50 }
131
- string :name, :length => 256, :trim_padding => true
132
- end
114
+ # class MashStep < RecEntry
115
+ # string :name, :length => 255, :trim_padding => true
116
+ # int8 :type
117
+ # int32 :start_temp
118
+ # int32 :stop_temp
119
+ # int32 :step_temp
120
+ # int32 :rest_time
121
+ # int32 :step_time
122
+ # float :thickness
123
+ # float :amount
124
+ # skip length: 8
125
+ # end
126
+ #
127
+ # class MashSchedule < RecEntry
128
+ # count_bytes_remaining :bytes_remaining
129
+ # string :skipped, :length => lambda { bytes_remaining - 14888 }
130
+ # int32 :_steps_count
131
+ # int32 :grain_temp
132
+ # skip length: 4
133
+ # array :mash_steps, :type => :mash_step, :read_until => lambda { index == 50 }
134
+ # string :name, :length => 255, :trim_padding => true
135
+ # int8 :end_byte
136
+ # end
133
137
 
134
138
  class SimpleSchedule < RecEntry
135
139
  int32 :num_steps
@@ -194,9 +198,9 @@ class ProMashRec < Brewser::Engine
194
198
  yeast :yeast
195
199
  water_profile :water_profile
196
200
  simple_schedule :simple_schedule
197
- string :notes, :length => 4029, :trim_padding => true
198
- string :awards, :length => 3693, :trim_padding => true
199
- mash_schedule :mash_schedule
201
+ string :notes, :length => 4028, :trim_padding => true
202
+ string :awards, :length => 2096, :trim_padding => true
203
+ #mash_schedule :mash_schedule
200
204
  end
201
205
  end
202
206
 
@@ -206,7 +210,7 @@ end
206
210
  class ProMashRec::Hop < Brewser::Hop
207
211
 
208
212
  @@hop_types = {0 => "Both", 1 => "Bittering", 2 => "Aroma"}
209
- @@hop_forms = {1 => "Whole", 3 => "Whole", 21 => "Pellet", 23 => "Pellet", 11 => "Plug"}
213
+ @@hop_forms = {0 => "Whole", 1 => "Plug", 2 => "Pellet"}
210
214
 
211
215
  # Pellet 21, 23
212
216
  def from_promash(hop,time)
@@ -282,7 +286,7 @@ end
282
286
  class ProMashRec::Yeast < Brewser::Yeast
283
287
 
284
288
  def from_promash(y)
285
- self.name = y.name.split("\x00")[0]
289
+ self.name = y.name.split("\x00")[0].strip
286
290
  self.supplier = y.supplier
287
291
  self.catalog = y.catalog
288
292
  self.attenuation = (y.aa_high + y.aa_low)/2
@@ -297,6 +301,8 @@ end
297
301
 
298
302
  class ProMashRec::MashStep < Brewser::MashStep
299
303
 
304
+ @@step_types = { 0 => "Infusion", 1 => "Direct", 2 => "Decoction" }
305
+
300
306
  def from_simple(idx, name, rest_temp, rest_time)
301
307
  self.index = idx
302
308
  self.name = name
@@ -306,11 +312,29 @@ class ProMashRec::MashStep < Brewser::MashStep
306
312
  return self
307
313
  end
308
314
 
315
+ def from_promash(step,idx=nil)
316
+ self.name = step.name
317
+ self.index = idx
318
+ self.type = @@step_types[step.type]
319
+ self.ramp_time = "#{step.step_time} min".u
320
+ self.rest_temperature = "#{step.start_temp} dF".u
321
+ self.rest_time = "#{step.rest_time} min".u
322
+ self.infusion_temperature = "#{step.step_temp} dF".u unless step.step_temp.blank?
323
+ self.infusion_amount = "#{step.amount} qts".u unless step.amount.blank?
324
+
325
+ return self
326
+ end
327
+
309
328
  end
310
329
 
311
330
  class ProMashRec::MashSchedule < Brewser::MashSchedule
312
331
 
313
332
  def from_promash(mash)
333
+ self.name = mash.name
334
+ self.grain_temp = "#{mash.grain_temp} dF".u
335
+ mash.steps.each do |step, idx|
336
+ self.mash_steps.push ProMashRec::MashStep.new.from_promash(step,idx)
337
+ end
314
338
 
315
339
  return self
316
340
  end
@@ -410,14 +434,10 @@ class ProMashRec::Recipe < Brewser::Recipe
410
434
  end
411
435
  self.yeasts.push ProMashRec::Yeast.new.from_promash(rec.yeast)
412
436
  self.water_profile = ProMashRec::WaterProfile.new.from_promash(rec.water_profile)
413
- if rec.simple_schedule.num_steps > 0
414
- self.mash_schedule = ProMashRec::SimpleSchedule.new.from_promash(rec.simple_schedule)
415
- else
416
- self.mash_schedule = ProMashRec::MashSchedule.new.from_promash(rec.mash_schedule)
417
- end
437
+ self.mash_schedule = ProMashRec::SimpleSchedule.new.from_promash(rec.simple_schedule)
418
438
  self.description = rec.notes
419
439
  self.type = self.style.type
420
- #puts rec.snapshot
440
+
421
441
  return self
422
442
  end
423
443
 
@@ -10,5 +10,31 @@ module Brewser
10
10
  property :amount, WeightOrVolume, :required => true
11
11
 
12
12
  property :use_for, String
13
+
14
+ def self.json_create(o)
15
+ a = self.new
16
+ a.name = o['name']
17
+ a.description = o['description']
18
+ a.type = o['type']
19
+ a.added_when = o['added_when']
20
+ a.time = o['time'].u unless o['time'].blank?
21
+ a.amount = o['amount'].u unless o['amount'].blank?
22
+ a.use_for = o['use_for']
23
+
24
+ return a
25
+ end
26
+
27
+ def as_json(options={})
28
+ {
29
+ JSON.create_id => "Brewser::Additive",
30
+ 'name' => name,
31
+ 'description' => description,
32
+ 'type' => type,
33
+ 'added_when' => added_when,
34
+ 'time' => time.to_s, 'amount' => amount.to_s,
35
+ 'use_for' => use_for
36
+ }
37
+ end
38
+
13
39
  end
14
40
  end
@@ -1,8 +1,11 @@
1
1
  module Brewser
2
-
2
+
3
3
  class Model
4
4
  require 'dm-core'
5
5
  require 'dm-validations'
6
+ require 'dm-serializer'
7
+
8
+ require "msgpack"
6
9
  require 'active_support/inflector'
7
10
 
8
11
  include DataMapper::Resource
@@ -14,21 +17,6 @@ module Brewser
14
17
  def self.auto_migrate_down!(rep);end
15
18
  def self.auto_migrate_up!(rep);end
16
19
  def self.auto_upgrade!(rep);end
17
-
18
- def deep_json
19
- h = {}
20
- instance_variables.each do |e|
21
- key = e[1..-1]
22
- next if ["roxml_references", "_persistence_state", "_key"].include? key
23
- o = instance_variable_get e.to_sym
24
- h[key] = (o.respond_to? :deep_json) ? o.deep_json : o;
25
- end
26
- h
27
- end
28
-
29
- def to_json *a
30
- deep_json.to_json *a
31
- end
32
20
 
33
21
  def as_brewson
34
22
  BrewSON.serialize(self)
@@ -10,7 +10,6 @@ module Brewser
10
10
  property :type, String, :set => ['Grain', 'Sugar', 'Extract', 'Dry Extract', 'Adjunct'], :required => true
11
11
  property :yield_percent, Float
12
12
  property :potential, Float, :required => true
13
- property :ppg, Integer
14
13
 
15
14
  property :color, Float, :required => true
16
15
 
@@ -24,5 +23,47 @@ module Brewser
24
23
  property :max_in_batch, Float
25
24
  property :recommend_mash?, Boolean
26
25
  property :ibu_gal_per_lb, Float
26
+
27
+ def ppg
28
+ return 0 if potential.blank?
29
+ (potential-1)*1000
30
+ end
31
+
32
+ def self.json_create(o)
33
+ a = self.new
34
+ a.name = o['name']
35
+ a.origin = o['origin']
36
+ a.supplier = o['supplier']
37
+ a.description = o['description']
38
+ a.type = o['type']
39
+ a.potential = o['potential']
40
+ a.color = o['color']
41
+ a.amount = o['amount'].u unless o['amount'].blank?
42
+ a.late_addition = o['added_late']
43
+ a.coarse_fine_diff = o['coarse_fine_diff']
44
+ a.moisture = o['moisture']
45
+ a.diastatic_power = o['diastatic_power']
46
+ a.protein = o['protein']
47
+ a.max_in_batch = o['max_in_batch']
48
+ a.origin = o['origin']
49
+ a.recommend_mash = o['recommend_mash']
50
+ a.ibu_gal_per_lb = o['ibu_gal_per_lb']
51
+
52
+ return a
53
+ end
54
+
55
+ def as_json(options={})
56
+ {
57
+ JSON.create_id => "Brewser::Fermentable",
58
+ 'name' => name, 'origin' => origin,
59
+ 'supplier' => supplier, 'description' => description,
60
+ 'type' => type, 'ppg' => ppg, 'potential' => potential,
61
+ 'color' => color, 'amount' => amount.to_s, 'added_late' => late_addition?,
62
+ 'coarse_fine_diff' => coarse_fine_diff, 'moisture' => moisture,
63
+ 'diastatic_power' => diastatic_power, 'protein' => protein, 'max_in_batch' => max_in_batch,
64
+ 'recommend_mash' => recommend_mash?, 'ibu_gal_per_lb' => ibu_gal_per_lb
65
+ }
66
+ end
67
+
27
68
  end
28
69
  end
@@ -3,5 +3,22 @@ module Brewser
3
3
  belongs_to :recipe
4
4
 
5
5
  has n, :fermentation_steps
6
+
7
+ def self.json_create(o)
8
+ a = self.new
9
+ o['fermentation_steps'].each do |step|
10
+ a.fermentation_steps.push step
11
+ end unless o['fermentation_steps'].nil?
12
+
13
+ return a
14
+ end
15
+
16
+ def as_json(options={})
17
+ {
18
+ JSON.create_id => "Brewser::FermentationSchedule",
19
+ 'fermentation_steps' => fermentation_steps.to_a
20
+ }
21
+ end
22
+
6
23
  end
7
24
  end
@@ -8,5 +8,26 @@ module Brewser
8
8
  property :index, Integer
9
9
  property :time, TimeInDays
10
10
  property :temperature, Temperature
11
+
12
+ def self.json_create(o)
13
+ a = self.new
14
+ a.name = o['name']
15
+ a.purpose = o['purpose']
16
+ a.index = o['index']
17
+ a.time = o['time'].u unless o['time'].blank?
18
+ a.temperature = o['temperature'].u unless o['temperature'].blank?
19
+
20
+ return a
21
+ end
22
+
23
+ def as_json(options={})
24
+ {
25
+ JSON.create_id=> "Brewser::FermentationStep",
26
+ 'name' => name, 'purpose' => purpose,
27
+ 'index' => index,
28
+ 'time' => time.to_s, 'temperature' => temperature.to_s
29
+ }
30
+ end
31
+
11
32
  end
12
33
  end
@@ -23,5 +23,49 @@ module Brewser
23
23
  property :myrcene, Float
24
24
  property :farnsene, Float
25
25
  property :total_oil, Float
26
+
27
+ def self.json_create(o)
28
+ a = self.new
29
+ a.name = o['name']
30
+ a.origin = o['origin']
31
+ a.description = o['description']
32
+ a.type = o['type']
33
+ a.alpha_acids = o['alpha_acids']
34
+ a.beta_acids = o['beta_acids']
35
+ a.added_when = o['added_when']
36
+ a.time = o['time'].u unless o['time'].blank?
37
+ a.amount = o['amount'].u unless o['amount'].blank?
38
+ a.form = o['form']
39
+ a.storageability = o['storageability']
40
+ a.substitutes = o['substitutes']
41
+ a.humulene = o['humulene']
42
+ a.caryophyllene = o['caryophyllene']
43
+ a.cohumulone = o['cohumulone']
44
+ a.myrcene = o['myrcene']
45
+ a.farnsene = o['farnsene']
46
+ a.total_oil = o['total_oil']
47
+
48
+ return a
49
+ end
50
+
51
+ def as_json(options={})
52
+ {
53
+ JSON.create_id=> "Brewser::Hop",
54
+ 'name' => name,
55
+ 'origin' => origin,
56
+ 'description' => description,
57
+ 'type' => type,
58
+ 'alpha_acids' => alpha_acids,
59
+ 'beta_acids' => beta_acids,
60
+ 'added_when' => added_when,
61
+ 'time' => time.to_s, 'amount' => amount.to_s,
62
+ 'form' => form, 'storageability' => storageability,
63
+ 'substitutes' => substitutes,
64
+ 'humulene' => humulene, 'caryophyllene' => caryophyllene,
65
+ 'cohumulone' => cohumulone, 'myrcene' => myrcene,
66
+ 'farnsene' => farnsene, 'total_oil' => total_oil
67
+ }
68
+ end
69
+
26
70
  end
27
71
  end