brewscribe 0.0.1 → 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.
- data/CHANGELOG.md +12 -0
- data/README.md +12 -3
- data/lib/brewscribe.rb +3 -2
- data/lib/brewscribe/conversion.rb +1 -0
- data/lib/brewscribe/document.rb +77 -0
- data/lib/brewscribe/recipe.rb +14 -66
- data/lib/brewscribe/version.rb +1 -1
- data/spec/brewscribe_spec.rb +2 -2
- data/spec/document_spec.rb +34 -0
- data/spec/recipe_spec.rb +10 -34
- metadata +16 -12
data/CHANGELOG.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
## 0.1.0, released 2012-04-05
|
2
|
+
|
3
|
+
* Added Brewscribe::Document to represent .bsmx file
|
4
|
+
* Moved XML to Hash conversion to Brewscribe::Document, leaving Brewscribe::Recipe
|
5
|
+
to work based on the resulting Hash.
|
6
|
+
* Brewscribe::Recipe#create\_accessors removed in favor of explicit attr\_accessors
|
7
|
+
* Brewscribe#import now returns a Brewscribe::Document
|
8
|
+
|
9
|
+
## 0.0.1, released 2012-03-23
|
10
|
+
|
11
|
+
* Added Brewscribe.import sugar to Brewscribe::Recipe.new
|
12
|
+
* Added IngredientList parsing of Brewscribe::Recipe
|
data/README.md
CHANGED
@@ -9,8 +9,9 @@ Brewscribe is a Beersmith2 (.bsmx) file parser.
|
|
9
9
|
## Usage
|
10
10
|
|
11
11
|
To start, you can import your .bsmx file with `Brewscribe.import(file)` where file
|
12
|
-
is any object that responds to `#read`. This will return
|
13
|
-
|
12
|
+
is any object that responds to `#read`. This will return a `Brewscribe::Document`
|
13
|
+
object, containing all the parsed information within the file. Currently, only Recipe
|
14
|
+
objects will be parsed.
|
14
15
|
|
15
16
|
By default, Brewscribe will set a text property for each attribute of the recipe, and
|
16
17
|
if it has a parser object it will attempt to further parse the data.
|
@@ -18,7 +19,8 @@ if it has a parser object it will attempt to further parse the data.
|
|
18
19
|
An example of this is found in `Brewscribe::IngredientList`:
|
19
20
|
|
20
21
|
```
|
21
|
-
|
22
|
+
document = Brewscribe.import File.read './spec/support/recipe.bsmx'
|
23
|
+
recipe = document.recipes.first
|
22
24
|
recipe.ingredients.class # => Brewscribe::IngredientList
|
23
25
|
recipe.ingredients.grains.class # => Array
|
24
26
|
recipe.ingredients.grains.first.class # => Brewscribe::Grain
|
@@ -35,6 +37,13 @@ I <3 Contributions.
|
|
35
37
|
4. Push to the branch (`git push origin my-new-feature`)
|
36
38
|
5. Create new Pull Request
|
37
39
|
|
40
|
+
## TODO
|
41
|
+
|
42
|
+
* More detailed Brewscribe::Recipe parsing. Mash info especially
|
43
|
+
* Beersmith Style parsing
|
44
|
+
* Writing back to .bsmx
|
45
|
+
* More documentation
|
46
|
+
|
38
47
|
## Author
|
39
48
|
|
40
49
|
Created by [Andrew Nordman](https://github.com/cadwallion/).
|
data/lib/brewscribe.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'brewscribe/version'
|
2
|
+
require 'brewscribe/document'
|
2
3
|
require 'brewscribe/recipe'
|
3
4
|
require 'brewscribe/ingredient_list'
|
4
5
|
require 'brewscribe/conversion'
|
@@ -6,6 +7,6 @@ require 'brewscribe/conversion'
|
|
6
7
|
module Brewscribe
|
7
8
|
def self.import file
|
8
9
|
data = file.read
|
9
|
-
|
10
|
+
Document.new(data: data)
|
10
11
|
end
|
11
12
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Brewscribe
|
2
|
+
class Document
|
3
|
+
attr_reader :recipes, :raw_data, :hash
|
4
|
+
|
5
|
+
def initialize options = {}
|
6
|
+
@recipes = []
|
7
|
+
|
8
|
+
if options[:file]
|
9
|
+
@raw_data = File.read options[:file]
|
10
|
+
elsif options[:data]
|
11
|
+
@raw_data = options[:data]
|
12
|
+
end
|
13
|
+
|
14
|
+
parse_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_data
|
18
|
+
@xml = Nokogiri::XML(@raw_data).xpath('/Selections/Data')
|
19
|
+
@hash = xml_node_to_hash @xml.first
|
20
|
+
|
21
|
+
if @hash[:recipe].class == Hash
|
22
|
+
parse_recipes [@hash[:recipe]]
|
23
|
+
else
|
24
|
+
parse_recipes Array @hash[:recipe]
|
25
|
+
end
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_recipes recipes
|
31
|
+
recipes.each do |recipe_hash|
|
32
|
+
@recipes << Recipe.new(recipe_hash)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def xml_node_to_hash node
|
37
|
+
if node.element?
|
38
|
+
if node.children.size > 0
|
39
|
+
result_hash = {}
|
40
|
+
|
41
|
+
node.children.each do |child|
|
42
|
+
result = xml_node_to_hash child
|
43
|
+
property = clean_key child.name
|
44
|
+
key = property.to_sym
|
45
|
+
|
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
|
51
|
+
else
|
52
|
+
result_hash[key] = [result_hash[key]] << result
|
53
|
+
end
|
54
|
+
else
|
55
|
+
result_hash[key] = result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
return result_hash
|
60
|
+
else
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
else
|
64
|
+
return node.content.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def clean_key key
|
69
|
+
extracted = key.to_s.match(/(F_(\w{1,2}_)?)?(_MOD_|.+)/)[3]
|
70
|
+
if extracted == '_MOD_'
|
71
|
+
return 'last_modified'
|
72
|
+
else
|
73
|
+
extracted.downcase
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/brewscribe/recipe.rb
CHANGED
@@ -3,74 +3,22 @@ require 'nokogiri'
|
|
3
3
|
module Brewscribe
|
4
4
|
class Recipe
|
5
5
|
attr_reader :raw_data, :hash
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
def create_recipe_accessors
|
22
|
-
@hash.keys.each do |key|
|
23
|
-
self.class.module_eval do
|
24
|
-
attr_accessor key
|
25
|
-
end
|
26
|
-
|
27
|
-
self.send "#{key}=", @hash[key]
|
6
|
+
attr_accessor :age, :asst_brewer, :base_grain, :boil_vol_measured, :brewer,
|
7
|
+
:carb, :carb_vols, :color_adj_string, :date, :description, :desired_color,
|
8
|
+
:desired_ibu, :desired_og, :equipment, :fg_measured, :final_vol_measured,
|
9
|
+
:image, :image_x, :image_y, :include_starter, :ingredients, :inv_date,
|
10
|
+
:last_modified, :locked, :mash, :mash_ph, :name, :notes, :og_boil_measured,
|
11
|
+
:og_measured, :og_primary, :og_secondary, :old_boil_vol, :old_efficiency,
|
12
|
+
:old_type, :old_vol, :rating, :raw_data, :rebalance_scale, :running_gravity,
|
13
|
+
:runoff_ph, :starter_size, :stir_plate, :style, :type, :version,
|
14
|
+
:volume_measured
|
15
|
+
|
16
|
+
def initialize data = {}
|
17
|
+
data.keys.each do |key|
|
18
|
+
self.send "#{key}=", data[key]
|
28
19
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def clean_key key
|
32
|
-
extracted = key.to_s.match(/(F_(\w{1,2}_)?)?(_MOD_|.+)/)[3]
|
33
|
-
if extracted == '_MOD_'
|
34
|
-
return 'last_modified'
|
35
|
-
else
|
36
|
-
extracted.downcase
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def parse_ingredients
|
41
|
-
self.ingredients = IngredientList.from_data(self.ingredients[:data])
|
42
|
-
end
|
43
|
-
|
44
|
-
def xml_node_to_hash node
|
45
|
-
if node.element?
|
46
|
-
if node.children.size > 0
|
47
|
-
result_hash = {}
|
48
|
-
|
49
|
-
node.children.each do |child|
|
50
|
-
result = xml_node_to_hash child
|
51
|
-
property = clean_key child.name
|
52
|
-
key = property.to_sym
|
53
|
-
|
54
|
-
if child.name == 'text'
|
55
|
-
return result if !child.next && !child.previous
|
56
|
-
elsif result_hash[key]
|
57
|
-
if result_hash[key].is_a? Array
|
58
|
-
result_hash[key] << result
|
59
|
-
else
|
60
|
-
result_hash[key] = [result_hash[key]] << result
|
61
|
-
end
|
62
|
-
else
|
63
|
-
result_hash[key] = result
|
64
|
-
end
|
65
|
-
end
|
66
20
|
|
67
|
-
|
68
|
-
else
|
69
|
-
return nil
|
70
|
-
end
|
71
|
-
else
|
72
|
-
return node.content.to_s
|
73
|
-
end
|
21
|
+
self.ingredients = IngredientList.from_data self.ingredients[:data]
|
74
22
|
end
|
75
23
|
end
|
76
24
|
end
|
data/lib/brewscribe/version.rb
CHANGED
data/spec/brewscribe_spec.rb
CHANGED
@@ -14,11 +14,11 @@ describe Brewscribe do
|
|
14
14
|
Brewscribe.import(file)
|
15
15
|
end
|
16
16
|
|
17
|
-
it 'should return a
|
17
|
+
it 'should return a Document object' do
|
18
18
|
file = double()
|
19
19
|
file.stub(:read)
|
20
20
|
recipe = Brewscribe.import(file)
|
21
|
-
recipe.should be_a(Brewscribe::
|
21
|
+
recipe.should be_a(Brewscribe::Document)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brewscribe::Document do
|
4
|
+
let(:file) { File.open(File.dirname(__FILE__) + '/support/recipe.bsmx', 'r') }
|
5
|
+
subject { Brewscribe::Document.new file: file }
|
6
|
+
|
7
|
+
describe '#parse' do
|
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
|
+
subject.recipes.should have(1).recipe
|
12
|
+
subject.recipes[0].should be_a Brewscribe::Recipe
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#clean_key' do
|
17
|
+
it 'converts an ugly key into something more readable' do
|
18
|
+
subject.clean_key("F_Y_BREW_DATE").should == 'brew_date'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'converts _MOD_ key to last_modified' do
|
22
|
+
subject.clean_key('_MOD_').should == 'last_modified'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#xml_node_to_hash' do
|
27
|
+
it 'converts xml to a corresponding hash' do
|
28
|
+
xml = Nokogiri::XML('<foo><bar><baz>1</baz></bar><bazaar>2</bazaar></foo>')
|
29
|
+
actual = subject.xml_node_to_hash(xml.root)
|
30
|
+
expected = { bar: { baz: '1' }, bazaar: '2' }
|
31
|
+
actual.should == expected
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/recipe_spec.rb
CHANGED
@@ -1,43 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Brewscribe::Recipe do
|
4
|
-
let(:recipe_file) { File.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
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
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '#clean_key' do
|
18
|
-
it 'converts an ugly key into something more readable' do
|
19
|
-
subject.clean_key("F_Y_BREW_DATE").should == 'brew_date'
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'converts _MOD_ key to last_modified' do
|
23
|
-
subject.clean_key('_MOD_').should == 'last_modified'
|
24
|
-
end
|
4
|
+
let(:recipe_file) { File.read(File.dirname(__FILE__) + '/support/recipe.bsmx') }
|
5
|
+
let(:data) { Brewscribe::Document.new(data: recipe_file).parse.hash[:recipe] }
|
6
|
+
subject { Brewscribe::Recipe.new(data) }
|
7
|
+
it 'should convert the recipe data into properties' do
|
8
|
+
subject.brewer.should == 'CadBrew'
|
25
9
|
end
|
26
10
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
actual = subject.xml_node_to_hash(xml.root)
|
31
|
-
expected = { bar: { baz: '1' }, bazaar: '2' }
|
32
|
-
actual.should == expected
|
33
|
-
end
|
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
|
34
14
|
end
|
35
15
|
|
36
|
-
|
37
|
-
|
38
|
-
subject.ingredients = subject.hash[:ingredients]
|
39
|
-
subject.parse_ingredients
|
40
|
-
subject.ingredients.should be_a Brewscribe::IngredientList
|
41
|
-
end
|
16
|
+
it 'should contain an IngredientsList' do
|
17
|
+
subject.ingredients.should be_a Brewscribe::IngredientList
|
42
18
|
end
|
43
19
|
end
|
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.0
|
4
|
+
version: 0.1.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-
|
12
|
+
date: 2012-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70355888747600 !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: *
|
24
|
+
version_requirements: *70355888747600
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: guard
|
27
|
-
requirement: &
|
27
|
+
requirement: &70355888746600 !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: *
|
35
|
+
version_requirements: *70355888746600
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: guard-rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70355888744860 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70355888744860
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: nokogiri
|
49
|
-
requirement: &
|
49
|
+
requirement: &70355888731820 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70355888731820
|
58
58
|
description:
|
59
59
|
email:
|
60
60
|
- cadwallion@gmail.com
|
@@ -64,6 +64,7 @@ extra_rdoc_files: []
|
|
64
64
|
files:
|
65
65
|
- .gitignore
|
66
66
|
- .rspec
|
67
|
+
- CHANGELOG.md
|
67
68
|
- Gemfile
|
68
69
|
- Guardfile
|
69
70
|
- LICENSE
|
@@ -72,6 +73,7 @@ files:
|
|
72
73
|
- brewscribe.gemspec
|
73
74
|
- lib/brewscribe.rb
|
74
75
|
- lib/brewscribe/conversion.rb
|
76
|
+
- lib/brewscribe/document.rb
|
75
77
|
- lib/brewscribe/grain.rb
|
76
78
|
- lib/brewscribe/hops.rb
|
77
79
|
- lib/brewscribe/ingredient_list.rb
|
@@ -80,6 +82,7 @@ files:
|
|
80
82
|
- lib/brewscribe/yeast.rb
|
81
83
|
- spec/brewscribe_spec.rb
|
82
84
|
- spec/conversion_spec.rb
|
85
|
+
- spec/document_spec.rb
|
83
86
|
- spec/grain_spec.rb
|
84
87
|
- spec/hops_spec.rb
|
85
88
|
- spec/ingredient_list_spec.rb
|
@@ -101,7 +104,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
101
104
|
version: '0'
|
102
105
|
segments:
|
103
106
|
- 0
|
104
|
-
hash: -
|
107
|
+
hash: -1660558714471080415
|
105
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
109
|
none: false
|
107
110
|
requirements:
|
@@ -110,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
113
|
version: '0'
|
111
114
|
segments:
|
112
115
|
- 0
|
113
|
-
hash: -
|
116
|
+
hash: -1660558714471080415
|
114
117
|
requirements: []
|
115
118
|
rubyforge_project:
|
116
119
|
rubygems_version: 1.8.17
|
@@ -120,6 +123,7 @@ summary: A Beersmith (.bsmx) file parser
|
|
120
123
|
test_files:
|
121
124
|
- spec/brewscribe_spec.rb
|
122
125
|
- spec/conversion_spec.rb
|
126
|
+
- spec/document_spec.rb
|
123
127
|
- spec/grain_spec.rb
|
124
128
|
- spec/hops_spec.rb
|
125
129
|
- spec/ingredient_list_spec.rb
|