nrb-beerxml 0.0.2

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +33 -0
  4. data/lib/beerxml.rb +24 -0
  5. data/lib/beerxml/builder.rb +30 -0
  6. data/lib/beerxml/equipment.rb +38 -0
  7. data/lib/beerxml/fermentable.rb +33 -0
  8. data/lib/beerxml/hop.rb +38 -0
  9. data/lib/beerxml/inflector.rb +27 -0
  10. data/lib/beerxml/mash.rb +35 -0
  11. data/lib/beerxml/mash_step.rb +26 -0
  12. data/lib/beerxml/misc.rb +24 -0
  13. data/lib/beerxml/parser.rb +114 -0
  14. data/lib/beerxml/recipe.rb +109 -0
  15. data/lib/beerxml/record.rb +49 -0
  16. data/lib/beerxml/record_set.rb +39 -0
  17. data/lib/beerxml/record_validators.rb +8 -0
  18. data/lib/beerxml/record_validators/boolean_validator.rb +16 -0
  19. data/lib/beerxml/record_validators/percentage_validator.rb +38 -0
  20. data/lib/beerxml/style.rb +49 -0
  21. data/lib/beerxml/version.rb +6 -0
  22. data/lib/beerxml/water.rb +26 -0
  23. data/lib/beerxml/yeast.rb +38 -0
  24. data/spec/cases/beerxml/builder_spec.rb +107 -0
  25. data/spec/cases/beerxml/equipment_spec.rb +38 -0
  26. data/spec/cases/beerxml/fermentable_spec.rb +17 -0
  27. data/spec/cases/beerxml/hop_spec.rb +21 -0
  28. data/spec/cases/beerxml/mash_spec.rb +25 -0
  29. data/spec/cases/beerxml/mash_step_spec.rb +39 -0
  30. data/spec/cases/beerxml/misc_spec.rb +14 -0
  31. data/spec/cases/beerxml/parser_spec.rb +30 -0
  32. data/spec/cases/beerxml/recipe_spec.rb +98 -0
  33. data/spec/cases/beerxml/record_set_spec.rb +36 -0
  34. data/spec/cases/beerxml/record_spec.rb +15 -0
  35. data/spec/cases/beerxml/record_validators/boolean_validator_spec.rb +50 -0
  36. data/spec/cases/beerxml/record_validators/percentage_validator_spec.rb +98 -0
  37. data/spec/cases/beerxml/style_spec.rb +36 -0
  38. data/spec/cases/beerxml/version_spec.rb +13 -0
  39. data/spec/cases/beerxml/water_spec.rb +25 -0
  40. data/spec/cases/beerxml/yeast_spec.rb +20 -0
  41. data/spec/cases/beerxml_spec.rb +3 -0
  42. data/spec/fixtures/equipment.xml +28 -0
  43. data/spec/fixtures/recipes.xml +1412 -0
  44. data/spec/shared/active_model_lint.rb +21 -0
  45. data/spec/shared/record_typing.rb +19 -0
  46. data/spec/spec_helper.rb +8 -0
  47. metadata +210 -0
@@ -0,0 +1,21 @@
1
+ require 'shared/active_model_lint'
2
+
3
+ describe NRB::BeerXML::Hop do
4
+
5
+ it_behaves_like :ActiveModel
6
+
7
+ it { should validate_presence_of :alpha }
8
+ it { should validate_presence_of :amount }
9
+ it { should validate_presence_of :time }
10
+ it { should validate_presence_of :use }
11
+
12
+ it { should validate_inclusion_of(:form).in_array( ["Leaf", "Pellet", "Plug"] ) }
13
+
14
+ it { should validate_numericality_of(:amount).is_greater_than_or_equal_to(0) }
15
+ it { should validate_inclusion_of(:type).in_array(%w(Aroma Bittering Both) ) }
16
+ it { should validate_inclusion_of(:use).in_array( [ "Aroma", "Boil", "Dry Hop", "First Wort", "Mash" ] ) }
17
+
18
+ it { should validate_numericality_of(:time).is_greater_than_or_equal_to(0) }
19
+
20
+
21
+ end
@@ -0,0 +1,25 @@
1
+ require 'shared/active_model_lint'
2
+ describe NRB::BeerXML::Mash do
3
+
4
+ it_behaves_like :ActiveModel
5
+
6
+ it { should validate_presence_of :grain_temp }
7
+ it { should validate_presence_of :mash_steps }
8
+
9
+ it { should validate_numericality_of :grain_temp }
10
+ it { should validate_numericality_of(:ph).is_greater_than_or_equal_to(0).is_less_than_or_equal_to(14) }
11
+ it { should validate_numericality_of :sparge_temp }
12
+ it { should validate_numericality_of(:tun_specific_heat).is_greater_than_or_equal_to(0) }
13
+ it { should validate_numericality_of :tun_temp }
14
+ it { should validate_numericality_of(:tun_weight).is_greater_than_or_equal_to(0) }
15
+
16
+
17
+ it 'should start with empty mash steps' do
18
+ expect( subject.mash_steps ).to be_a(NRB::BeerXML::RecordSet)
19
+ end
20
+
21
+ it 'mash steps should be a RecordSet of mash_steps' do
22
+ expect( subject.mash_steps.record_type ).to eq :mash_step
23
+ end
24
+
25
+ end
@@ -0,0 +1,39 @@
1
+ require 'shared/active_model_lint'
2
+
3
+ describe NRB::BeerXML::MashStep do
4
+
5
+ it_behaves_like :ActiveModel
6
+
7
+ let(:subject) { described_class.new type: type }
8
+ let(:type) { "Temperature" }
9
+
10
+ it { should validate_presence_of(:step_temp) }
11
+ it { should validate_presence_of(:step_time) }
12
+ it { should validate_presence_of(:type) }
13
+
14
+ it { should validate_inclusion_of(:type).in_array(%w(Decoction Infusion Temperature)) }
15
+
16
+ it { should validate_numericality_of(:end_temp) }
17
+ it { should validate_numericality_of(:infuse_amount) }
18
+ it { should validate_numericality_of(:ramp_time).is_greater_than_or_equal_to(0) }
19
+ it { should validate_numericality_of(:step_temp) }
20
+ it { should validate_numericality_of(:step_time).is_greater_than_or_equal_to(0) }
21
+
22
+ context "Decoction steps" do
23
+ let(:type) { "Decoction" }
24
+ it { should_not validate_presence_of :infuse_amount }
25
+ end
26
+
27
+
28
+ context "Infusion steps" do
29
+ let(:type) { "Infusion" }
30
+ it { should validate_presence_of :infuse_amount }
31
+ end
32
+
33
+
34
+ context "Temperature steps" do
35
+ let(:type) { "Temperature" }
36
+ it { should_not validate_presence_of :infuse_amount }
37
+ end
38
+
39
+ end
@@ -0,0 +1,14 @@
1
+ describe NRB::BeerXML::Misc do
2
+
3
+ it { should validate_presence_of :amount }
4
+ it { should validate_presence_of :time }
5
+ it { should validate_presence_of :type }
6
+ it { should validate_presence_of :use }
7
+
8
+ it { should validate_numericality_of(:amount).is_greater_than_or_equal_to(0) }
9
+ it { should validate_numericality_of(:time).is_greater_than_or_equal_to(0) }
10
+
11
+ it { should validate_inclusion_of(:type).in_array(["Fining", "Flavor", "Herb", "Other", "Spice", "Water Agent"]) }
12
+ it { should validate_inclusion_of(:use).in_array(%w(Boil Bottling Mash Primary Secondary)) }
13
+
14
+ end
@@ -0,0 +1,30 @@
1
+ describe NRB::BeerXML::Parser do
2
+
3
+ context 'Parsing' do
4
+
5
+ let(:base_dir) { File.expand_path File.join('..','..','..'), __FILE__ }
6
+ let(:fixture_dir) { File.join base_dir,'fixtures' }
7
+ let(:fixture_path) { File.join fixture_dir, 'recipes.xml' }
8
+
9
+ it 'does not raise error when parsing a path name' do
10
+ expect { subject.parse fixture_path }.to_not raise_error
11
+ end
12
+
13
+
14
+ it 'does not raise error when parsing a stream' do
15
+ expect { subject.parse File.open(fixture_path) }.to_not raise_error
16
+ end
17
+
18
+
19
+ it 'raises an error when parsing something else' do
20
+ expect { subject.parse Array.new }.to raise_error
21
+ end
22
+
23
+
24
+ it 'returns a NRB::BeerXML::Parser' do
25
+ expect( subject.parse fixture_path ).to be_a(NRB::BeerXML::RecordSet)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,98 @@
1
+ require 'shared/active_model_lint'
2
+
3
+ describe NRB::BeerXML::Recipe do
4
+
5
+ it_behaves_like :ActiveModel
6
+
7
+ it { should_not validate_presence_of :age }
8
+ it { should_not validate_presence_of :age_temp }
9
+ it { should_not validate_presence_of :asst_brewer }
10
+ it { should validate_presence_of :batch_size }
11
+ it { should validate_presence_of :boil_size }
12
+ it { should validate_presence_of :boil_time }
13
+ it { should validate_presence_of :brewer }
14
+ it { should_not validate_presence_of :carbonation }
15
+ it { should_not validate_presence_of :carbonation_temp }
16
+ it { should_not validate_presence_of :date }
17
+ it { should validate_presence_of :equipment }
18
+ it { should validate_presence_of :fermentables }
19
+ it { should_not validate_presence_of :fermentation_stages }
20
+ it { should_not validate_presence_of :fg }
21
+ it { should_not validate_presence_of :forced_carbonation }
22
+ it { should validate_presence_of :hops }
23
+ it { should_not validate_presence_of :keg_priming_factor }
24
+ it { should validate_presence_of :mash }
25
+ it { should validate_presence_of :miscs }
26
+ it { should_not validate_presence_of :notes }
27
+ it { should_not validate_presence_of :og }
28
+ it { should_not validate_presence_of :primary_age }
29
+ it { should_not validate_presence_of :primary_temp }
30
+ it { should_not validate_presence_of :priming_sugar_equiv }
31
+ it { should_not validate_presence_of :priming_sugar_name }
32
+ it { should_not validate_presence_of :secondary_age }
33
+ it { should_not validate_presence_of :secondary_temp }
34
+ it { should validate_presence_of :style }
35
+ it { should_not validate_presence_of :taste_notes }
36
+ it { should_not validate_presence_of :taste_rating }
37
+ it { should_not validate_presence_of :tertiary_age }
38
+ it { should_not validate_presence_of :tertiary_temp }
39
+ it { should validate_presence_of :type }
40
+ it { should validate_presence_of :waters }
41
+ it { should validate_presence_of :yeasts }
42
+
43
+ it { should validate_inclusion_of(:type).in_array([ "All Grain", "Extract", "Partial Mash" ]) }
44
+
45
+ it { should validate_numericality_of(:age).is_greater_than_or_equal_to(0) }
46
+ it { should validate_numericality_of(:age_temp) }
47
+ it { should validate_numericality_of(:batch_size).is_greater_than_or_equal_to(0) }
48
+ it { should validate_numericality_of(:boil_size).is_greater_than_or_equal_to(0) }
49
+ it { should validate_numericality_of(:boil_time).is_greater_than_or_equal_to(0) }
50
+ it { should validate_numericality_of(:carbonation).is_greater_than_or_equal_to(0) }
51
+ it { should validate_numericality_of(:carbonation_temp) }
52
+ it { should validate_numericality_of(:fermentation_stages).only_integer }
53
+ it { should validate_numericality_of(:fg) }
54
+ it { should validate_numericality_of(:og) }
55
+ it { should validate_numericality_of(:primary_age).is_greater_than_or_equal_to(0) }
56
+ it { should validate_numericality_of(:primary_temp) }
57
+ it { should validate_numericality_of(:secondary_age).is_greater_than_or_equal_to(0) }
58
+ it { should validate_numericality_of(:secondary_temp) }
59
+ it { should validate_numericality_of(:tertiary_age).is_greater_than_or_equal_to(0) }
60
+ it { should validate_numericality_of(:tertiary_temp) }
61
+
62
+
63
+ %i(fermentable hop misc water yeast).each do |record_type|
64
+ it "#{record_type}s should be a RecordSet" do
65
+ expect(subject.send("#{record_type}s")).to be_a(NRB::BeerXML::RecordSet)
66
+ end
67
+
68
+ it "its #{record_type}s RecordSet should be of type #{record_type}" do
69
+ expect(subject.send("#{record_type}s").record_type).to eq record_type
70
+ end
71
+ end
72
+
73
+
74
+ shared_examples_for :restricted_assignment do
75
+ it "allows assignment of the correct type" do
76
+ expect{ subject.send("#{record_type}=", good) }.to_not raise_exception
77
+ end
78
+
79
+ it 'disallows non-Styles' do
80
+ expect{ subject.send("#{record_type}=", bad) }.to raise_exception
81
+ end
82
+ end
83
+
84
+ context "assigning equipment" do
85
+ let(:bad) { NRB::BeerXML::Hop.new }
86
+ let(:good) { NRB::BeerXML::Equipment.new }
87
+ let(:record_type) { :equipment }
88
+ it_behaves_like :restricted_assignment
89
+ end
90
+
91
+ context "assigning style" do
92
+ let(:bad) { NRB::BeerXML::Hop.new }
93
+ let(:good) { NRB::BeerXML::Style.new }
94
+ let(:record_type) { :style }
95
+ it_behaves_like :restricted_assignment
96
+ end
97
+
98
+ end
@@ -0,0 +1,36 @@
1
+ describe NRB::BeerXML::RecordSet do
2
+
3
+ context "initialization" do
4
+
5
+ described_class.valid_record_types.each do |record_type|
6
+ it "does not blow up given #{record_type}" do
7
+ expect { described_class.new record_type: record_type }.to_not raise_exception
8
+ end
9
+ end
10
+
11
+ it 'blows up when given a blark' do
12
+ expect { described_class.new record_type: :blark }.to raise_exception
13
+ end
14
+
15
+ end
16
+
17
+
18
+ context 'adding records' do
19
+
20
+ let(:invalid_record) { NRB::BeerXML::Mash.new }
21
+ let(:record_set) { described_class.new record_type: record_type }
22
+ let(:record_type) { :hop }
23
+ let(:valid_record) { NRB::BeerXML::Hop.new }
24
+
25
+ it 'lets you add valid records' do
26
+ expect { record_set << valid_record }.to_not raise_exception
27
+ end
28
+
29
+ it "doesen't let you add invalid records" do
30
+ expect { record_set << inalid_record }.to raise_exception
31
+ end
32
+
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'shared/active_model_lint'
2
+ describe NRB::BeerXML::Record do
3
+
4
+ it { should validate_presence_of :name }
5
+ it { should validate_presence_of :version }
6
+
7
+ it { should validate_numericality_of(:version).only_integer }
8
+
9
+ it 'tells you its name' do
10
+ expect(subject.record_type).to be_a(Symbol)
11
+ end
12
+
13
+ it_behaves_like :ActiveModel
14
+
15
+ end
@@ -0,0 +1,50 @@
1
+ require 'beerxml/record_validators/boolean_validator'
2
+ module NRB
3
+ module Fake
4
+ class FakeBooleanObject
5
+ include ActiveModel::Model
6
+ attr_accessor :boolean
7
+ validates :boolean, boolean: true
8
+ end
9
+ end
10
+ end
11
+
12
+ describe NRB::BeerXML::RecordValidators::BooleanValidator do
13
+
14
+ shared_examples_for :a_borked_record do
15
+
16
+ it 'is invalid' do
17
+ expect(record).to_not be_valid
18
+ end
19
+
20
+ it 'tells you why the record is invalid' do
21
+ record.valid?
22
+ expect(record.errors[:boolean]).to include('must be false or true')
23
+ end
24
+
25
+ end
26
+
27
+
28
+ let(:record) { NRB::Fake::FakeBooleanObject.new boolean: boolean }
29
+
30
+
31
+ context 'an invalid record' do
32
+ let(:boolean) { :false }
33
+ it_behaves_like :a_borked_record
34
+ end
35
+
36
+
37
+ context 'false is valid' do
38
+ let(:boolean) { false }
39
+ it { expect(record).to be_valid }
40
+ end
41
+
42
+
43
+ context 'true is valid' do
44
+ let(:boolean) { true }
45
+ it { expect(record).to be_valid }
46
+ end
47
+
48
+
49
+
50
+ end
@@ -0,0 +1,98 @@
1
+ require 'beerxml/record_validators/percentage_validator'
2
+ module NRB
3
+ module Fake
4
+ class FakePercentage
5
+ include ActiveModel::Model
6
+ attr_accessor :percentage
7
+ end
8
+ class NoMaxFakePercentage < FakePercentage
9
+ validates :percentage, percentage: { give_110: true }
10
+ end
11
+ class NoMinFakePercentage < FakePercentage
12
+ validates :percentage, percentage: { allow_negative: true }
13
+ end
14
+ class VanillaFakePercentage < FakePercentage
15
+ validates :percentage, percentage: true
16
+ end
17
+ end
18
+ end
19
+
20
+ describe NRB::BeerXML::RecordValidators::PercentageValidator do
21
+
22
+ let(:nan) { :a }
23
+
24
+ shared_examples_for :a_borked_record do
25
+
26
+ it 'is invalid' do
27
+ expect(record).to_not be_valid
28
+ end
29
+
30
+ it 'tells you why the record is invalid' do
31
+ record.valid?
32
+ expect(record.errors[:percentage]).to include('must be a BeerXML percentage (a number between 0 & 100)')
33
+ end
34
+
35
+ end
36
+
37
+
38
+ shared_examples_for :rejects_nan do
39
+ let(:percentage) { nan }
40
+ it_behaves_like :a_borked_record
41
+ end
42
+
43
+
44
+ shared_examples_for :goldilocks do
45
+ let(:percentage) { okay }
46
+ it 'is valid' do
47
+ expect(record).to be_valid
48
+ end
49
+ end
50
+
51
+
52
+ shared_examples_for :rejects_large do
53
+ let(:percentage) { too_big }
54
+ it_behaves_like :a_borked_record
55
+ end
56
+
57
+
58
+ shared_examples_for :rejects_small do
59
+ let(:percentage) { too_small }
60
+ it_behaves_like :a_borked_record
61
+ end
62
+
63
+
64
+ context 'a vanilla percentage validator' do
65
+ let(:okay) { 88 }
66
+ let(:record) { NRB::Fake::VanillaFakePercentage.new percentage: percentage }
67
+ let(:too_big) { 101 }
68
+ let(:too_small) { -1 }
69
+
70
+ it_behaves_like :goldilocks
71
+ it_behaves_like :rejects_large
72
+ it_behaves_like :rejects_nan
73
+ it_behaves_like :rejects_small
74
+ end
75
+
76
+
77
+ context 'with no maximum' do
78
+ let(:okay) { 1000 }
79
+ let(:record) { NRB::Fake::NoMaxFakePercentage.new percentage: percentage }
80
+ let(:too_small) { -1 }
81
+
82
+ it_behaves_like :goldilocks
83
+ it_behaves_like :rejects_small
84
+ it_behaves_like :rejects_nan
85
+ end
86
+
87
+
88
+ context 'with no minimum' do
89
+ let(:okay) { -10 }
90
+ let(:record) { NRB::Fake::NoMinFakePercentage.new percentage: percentage }
91
+ let(:too_big) { 101 }
92
+
93
+ it_behaves_like :goldilocks
94
+ it_behaves_like :rejects_large
95
+ it_behaves_like :rejects_nan
96
+ end
97
+
98
+ end
@@ -0,0 +1,36 @@
1
+ require 'shared/active_model_lint'
2
+
3
+ describe NRB::BeerXML::Style do
4
+
5
+ it_behaves_like :ActiveModel
6
+
7
+ it { should validate_presence_of :category }
8
+ it { should validate_presence_of :category_number }
9
+ it { should validate_presence_of :color_max }
10
+ it { should validate_presence_of :color_min }
11
+ it { should validate_presence_of :fg_max }
12
+ it { should validate_presence_of :fg_min }
13
+ it { should validate_presence_of :ibu_max }
14
+ it { should validate_presence_of :ibu_min }
15
+ it { should validate_presence_of :og_max }
16
+ it { should validate_presence_of :og_min }
17
+ it { should validate_presence_of :style_guide }
18
+ it { should validate_presence_of :style_letter }
19
+ it { should validate_presence_of :type }
20
+
21
+ it { should validate_inclusion_of(:type).in_array(%w(Ale Cider Lager Mead Mixed Wheat) ) }
22
+
23
+ it { should validate_numericality_of(:abv_max).is_greater_than_or_equal_to(0) }
24
+ it { should validate_numericality_of(:abv_min).is_greater_than_or_equal_to(0) }
25
+ it { should validate_numericality_of(:carb_max).is_greater_than_or_equal_to(0) }
26
+ it { should validate_numericality_of(:carb_min).is_greater_than_or_equal_to(0) }
27
+ it { should validate_numericality_of(:color_max).is_greater_than_or_equal_to(0) }
28
+ it { should validate_numericality_of(:color_min).is_greater_than_or_equal_to(0) }
29
+ it { should validate_numericality_of(:fg_max).is_greater_than_or_equal_to(0) }
30
+ it { should validate_numericality_of(:fg_min).is_greater_than_or_equal_to(0) }
31
+ it { should validate_numericality_of(:ibu_max).is_greater_than_or_equal_to(0) }
32
+ it { should validate_numericality_of(:ibu_min).is_greater_than_or_equal_to(0) }
33
+ it { should validate_numericality_of(:og_max).is_greater_than_or_equal_to(0) }
34
+ it { should validate_numericality_of(:og_min).is_greater_than_or_equal_to(0) }
35
+
36
+ end