brewser 0.1.0 → 0.2.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.
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