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,49 @@
1
+ require 'active_model'
2
+ module NRB
3
+ module BeerXML
4
+ class Record
5
+ include Inflector
6
+ include ActiveModel::Model
7
+ extend ActiveModel::Callbacks
8
+ extend ActiveModel::Naming
9
+
10
+ define_model_callbacks :initialize, only: :after
11
+
12
+ attr_accessor :name
13
+ attr_accessor :version
14
+
15
+ validates :name, presence: true
16
+ validates :version, presence: true, numericality: { only_integer: true }
17
+
18
+
19
+ def initialize(*args)
20
+ super
21
+ run_callbacks :initialize
22
+ end
23
+
24
+
25
+ def record_type
26
+ underscore(self.class.name.split(/::/).last).to_sym
27
+ end
28
+
29
+
30
+ def equipment?; am_a? :equipment; end
31
+ def fermentable?; am_a? :fermentable; end
32
+ def hop?; am_a? :hop; end
33
+ def mash_profile?; am_a? :mash_profile; end
34
+ def mash_step?; am_a? :mash_step; end
35
+ def misc?; am_a? :misc; end
36
+ def recipe?; am_a? :recipe; end
37
+ def style?; am_a? :style; end
38
+ def water?; am_a? :water; end
39
+ def yeast?; am_a? :yeast; end
40
+
41
+ def persisted?; false; end
42
+
43
+ private
44
+
45
+ def am_a?(question); record_type == question; end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,39 @@
1
+ module NRB; module BeerXML
2
+ class RecordSet
3
+
4
+ include Enumerable
5
+
6
+ class UnknownRecordTypeError < RuntimeError; end
7
+
8
+ attr_reader :record_type, :records
9
+
10
+ def self.valid_record_types
11
+ %i( equipment fermentable hop mash mash_step misc recipe style water yeast )
12
+ end
13
+
14
+
15
+ def <<(record)
16
+ raise UnknownRecordTypeError.new("Can't add a #{record.record_type} to this set (only #{record_type}s)") unless valid_record_type?(record.record_type)
17
+ @records << record
18
+ end
19
+
20
+
21
+ def each(&block)
22
+ @records.each &block
23
+ end
24
+
25
+
26
+ def initialize(record_type: nil)
27
+ raise UnknownRecordTypeError.new("Don't know what to do with a #{record_type} record") unless valid_record_type?(record_type)
28
+ @record_type = record_type
29
+ @records = []
30
+ end
31
+
32
+ private
33
+
34
+ def valid_record_type?(type)
35
+ self.class.valid_record_types.include?(type)
36
+ end
37
+
38
+ end
39
+ end; end
@@ -0,0 +1,8 @@
1
+ module NRB
2
+ module BeerXML
3
+ module RecordValidators
4
+ autoload :BooleanValidator, 'beerxml/record_validators/boolean_validator'
5
+ autoload :PercentageValidator, 'beerxml/record_validators/percentage_validator'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_model'
2
+ module NRB; module BeerXML;
3
+ module RecordValidators
4
+ class BooleanValidator < ActiveModel::Validations::InclusionValidator
5
+ def validate_each(record, attribute, value)
6
+ unless include?(record, value)
7
+ record.errors[attribute] << "must be false or true"
8
+ end
9
+ end
10
+ private
11
+ def delimiter; [false, true]; end
12
+ end
13
+ end
14
+ end; end
15
+ defined?(BooleanValidator) || BooleanValidator = NRB::BeerXML::RecordValidators::BooleanValidator
16
+
@@ -0,0 +1,38 @@
1
+ require 'active_model'
2
+ module NRB; module BeerXML;
3
+ module RecordValidators
4
+ class PercentageValidator < ActiveModel::EachValidator
5
+
6
+ def validate_each(record, attribute, value)
7
+ return unless value
8
+ unless value.is_a?(Numeric) && greater_than_min(value) && less_than_max(value)
9
+ record.errors[attribute] << 'must be a BeerXML percentage (a number between 0 & 100)'
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def greater_than_min(value)
16
+ options[:allow_negative] ? true : value >= min
17
+ end
18
+
19
+
20
+ def less_than_max(value)
21
+ options[:give_110] ? true : value <= max
22
+ end
23
+
24
+
25
+ def max
26
+ options[:max] || 100
27
+ end
28
+
29
+
30
+ def min
31
+ options[:min] || 0
32
+ end
33
+
34
+ end
35
+ end
36
+ end; end
37
+ defined?(PercentageValidator) || PercentageValidator = NRB::BeerXML::RecordValidators::PercentageValidator
38
+
@@ -0,0 +1,49 @@
1
+ require 'beerxml/record_validators/percentage_validator'
2
+ module NRB
3
+ module BeerXML
4
+ class Style < Record
5
+
6
+ attr_accessor :abv_max # float
7
+ attr_accessor :abv_min # float
8
+ attr_accessor :carb_max # float
9
+ attr_accessor :carb_min # float
10
+ attr_accessor :category # string required
11
+ attr_accessor :category_number # string required
12
+ attr_accessor :color_max # float required
13
+ attr_accessor :color_min # float required
14
+ attr_accessor :examples # text
15
+ attr_accessor :fg_max # float required
16
+ attr_accessor :fg_min # float required
17
+ attr_accessor :ibu_max # float required
18
+ attr_accessor :ibu_min # float required
19
+ attr_accessor :ingredients # text
20
+ attr_accessor :notes # text
21
+ attr_accessor :og_max # float required
22
+ attr_accessor :og_min # float required
23
+ attr_accessor :profile # text
24
+ attr_accessor :style_guide # string required
25
+ attr_accessor :style_letter # string required
26
+ attr_accessor :type # list required
27
+
28
+ validates :abv_max, numericality: { greater_than_or_equal_to: 0 }
29
+ validates :abv_min, numericality: { greater_than_or_equal_to: 0 }
30
+ validates :carb_max, numericality: { greater_than_or_equal_to: 0 }
31
+ validates :carb_min, numericality: { greater_than_or_equal_to: 0 }
32
+ validates :category, presence: true
33
+ validates :category_number, presence: true
34
+ validates :color_max, numericality: { greater_than_or_equal_to: 0 }, presence: true
35
+ validates :color_min, numericality: { greater_than_or_equal_to: 0 }, presence: true
36
+ validates :fg_max, numericality: { greater_than_or_equal_to: 0 }, presence: true
37
+ validates :fg_min, numericality: { greater_than_or_equal_to: 0 }, presence: true
38
+ validates :ibu_max, numericality: { greater_than_or_equal_to: 0 }, presence: true
39
+ validates :ibu_min, numericality: { greater_than_or_equal_to: 0 }, presence: true
40
+ validates :og_max, numericality: { greater_than_or_equal_to: 0 }, presence: true
41
+ validates :og_min, numericality: { greater_than_or_equal_to: 0 }, presence: true
42
+ validates :style_guide, presence: true
43
+ validates :style_letter, presence: true
44
+ validates :type, inclusion: { in: %w(Ale Cider Lager Mead Mixed Wheat ) },
45
+ presence: true
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,6 @@
1
+ module NRB
2
+ module BeerXML
3
+ VERSION = "0.0.2"
4
+ def self.version; VERSION; end
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ module NRB
2
+ module BeerXML
3
+ class Water < Record
4
+
5
+ attr_accessor :amount # volume required
6
+ attr_accessor :bicarbonate # float required
7
+ attr_accessor :calcium # float required
8
+ attr_accessor :chloride # float required
9
+ attr_accessor :magnesium # float required
10
+ attr_accessor :notes # string
11
+ attr_accessor :ph # float
12
+ attr_accessor :sodium # float required
13
+ attr_accessor :sulfate # float required
14
+
15
+ validates :amount, numericality: true, presence: true
16
+ validates :bicarbonate, numericality: true, presence: true
17
+ validates :calcium, numericality: true, presence: true
18
+ validates :chloride, numericality: true, presence: true
19
+ validates :magnesium, numericality: true, presence: true
20
+ validates :ph, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 14 }
21
+ validates :sodium, numericality: true, presence: true
22
+ validates :sulfate, numericality: true, presence: true
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ require 'beerxml/record_validators/boolean_validator' # hack so "validates boolean:" magic works
2
+ require 'beerxml/record_validators/percentage_validator' # hack so "validates percentage:" magic works
3
+ module NRB
4
+ module BeerXML
5
+ class Yeast < Record
6
+
7
+ attr_accessor :add_to_secondary # boolean
8
+ attr_accessor :amount # float required
9
+ attr_accessor :amount_is_weight # boolean
10
+ attr_accessor :attenuation # percentage
11
+ attr_accessor :best_for # string
12
+ attr_accessor :flocculation # list
13
+ attr_accessor :form # list required
14
+ attr_accessor :laboratory # string
15
+ attr_accessor :max_reuse # integer
16
+ attr_accessor :max_temperature # temperature
17
+ attr_accessor :min_temperature # temperature
18
+ attr_accessor :notes # string
19
+ attr_accessor :product_id # string
20
+ attr_accessor :times_cultured # integer
21
+ attr_accessor :type # list required
22
+
23
+ validates :add_to_secondary, boolean: true
24
+ validates :amount, presence: { greater_than_or_equal_to: 0 }
25
+ validates :amount_is_weight, boolean: true
26
+ validates :attenuation, percentage: true
27
+ validates :flocculation, inclusion: { in: ["High", "Low", "Medium", "Very High" ] }
28
+ validates :form, presence: true, inclusion: { in: %w( Culture Dry Liquid Slant ) }
29
+ validates :max_reuse, numericality: { greater_than_or_equal_to: 0, only_integer: true }
30
+ validates :max_temperature, numericality: true
31
+ validates :min_temperature, numericality: true
32
+ validates :times_cultured, numericality: { greater_than_or_equal_to: 0, only_integer: true }
33
+ validates :type, presence: true, inclusion: { in: %w(Ale Champagne Lager Wheat Wine) }
34
+
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,107 @@
1
+ describe NRB::BeerXML::Builder do
2
+
3
+ it "returns nil when it can't guess" do
4
+ expect( subject.build("Blark") ).to be_nil
5
+ end
6
+
7
+
8
+ it 'returns a Equipment when asked' do
9
+ expect( subject.build("Equipment") ).to be_a(NRB::BeerXML::Equipment)
10
+ end
11
+
12
+
13
+ it 'returns a RecordSet when asked for Equipments' do
14
+ expect( subject.build("Equipments") ).to be_a(NRB::BeerXML::RecordSet)
15
+ end
16
+
17
+
18
+ it 'returns a Fermentable when asked' do
19
+ expect( subject.build("Fermentable") ).to be_a(NRB::BeerXML::Fermentable)
20
+ end
21
+
22
+
23
+ it 'returns a RecordSet when asked for Fermentables' do
24
+ expect( subject.build("Fermentables") ).to be_a(NRB::BeerXML::RecordSet)
25
+ end
26
+
27
+
28
+ it 'returns a Hop when asked' do
29
+ expect( subject.build("Hop") ).to be_a(NRB::BeerXML::Hop)
30
+ end
31
+
32
+
33
+ it 'returns a RecordSet when asked for Hops' do
34
+ expect( subject.build("Hops") ).to be_a(NRB::BeerXML::RecordSet)
35
+ end
36
+
37
+
38
+ it 'returns a Mash when asked' do
39
+ expect( subject.build("Mash") ).to be_a(NRB::BeerXML::Mash)
40
+ end
41
+
42
+
43
+ it 'returns a RecordSet when asked for Mashs' do
44
+ expect( subject.build("Mashs") ).to be_a(NRB::BeerXML::RecordSet)
45
+ end
46
+
47
+
48
+ it 'returns a MashStep when asked' do
49
+ expect( subject.build("MashStep") ).to be_a(NRB::BeerXML::MashStep)
50
+ end
51
+
52
+
53
+ it 'returns a RecordSet when asked for MashSteps' do
54
+ expect( subject.build("MashSteps") ).to be_a(NRB::BeerXML::RecordSet)
55
+ end
56
+
57
+
58
+ it 'returns a Misc when asked' do
59
+ expect( subject.build("Misc") ).to be_a(NRB::BeerXML::Misc)
60
+ end
61
+
62
+
63
+ it 'returns a RecordSet when asked for Miscs' do
64
+ expect( subject.build("Miscs") ).to be_a(NRB::BeerXML::RecordSet)
65
+ end
66
+
67
+
68
+ it 'returns a Recipe when asked' do
69
+ expect( subject.build("Recipe") ).to be_a(NRB::BeerXML::Recipe)
70
+ end
71
+
72
+
73
+ it 'returns a RecordSet when asked for Recipes' do
74
+ expect( subject.build("Recipes") ).to be_a(NRB::BeerXML::RecordSet)
75
+ end
76
+
77
+
78
+ it 'returns a Style when asked' do
79
+ expect( subject.build("Style") ).to be_a(NRB::BeerXML::Style)
80
+ end
81
+
82
+
83
+ it 'returns a RecordSet when asked for Styles' do
84
+ expect( subject.build("Styles") ).to be_a(NRB::BeerXML::RecordSet)
85
+ end
86
+
87
+
88
+ it 'returns a Water when asked' do
89
+ expect( subject.build("Water") ).to be_a(NRB::BeerXML::Water)
90
+ end
91
+
92
+
93
+ it 'returns a RecordSet when asked for a Waters' do
94
+ expect( subject.build("Waters") ).to be_a(NRB::BeerXML::RecordSet)
95
+ end
96
+
97
+
98
+
99
+ it 'returns a Yeast when asked' do
100
+ expect( subject.build("Yeast") ).to be_a(NRB::BeerXML::Yeast)
101
+ end
102
+
103
+ it 'returns a RecordSet when asked for a Yeasts' do
104
+ expect( subject.build("Yeasts") ).to be_a(NRB::BeerXML::RecordSet)
105
+ end
106
+
107
+ end
@@ -0,0 +1,38 @@
1
+ require 'shared/active_model_lint'
2
+ describe NRB::BeerXML::Equipment do
3
+
4
+ it_behaves_like :ActiveModel
5
+
6
+ it { should validate_presence_of :batch_size }
7
+ it { should validate_presence_of :boil_size }
8
+
9
+ it { should validate_numericality_of(:batch_size).is_greater_than_or_equal_to(0) }
10
+ it { should validate_numericality_of(:boil_size).is_greater_than_or_equal_to(0) }
11
+ it { should validate_numericality_of(:boil_time).is_greater_than_or_equal_to(0) }
12
+ it { should validate_numericality_of(:lauter_tun_deadspace).is_greater_than_or_equal_to(0) }
13
+ it { should validate_numericality_of(:top_up_kettle).is_greater_than_or_equal_to(0) }
14
+ it { should validate_numericality_of(:top_up_water).is_greater_than_or_equal_to(0) }
15
+ it { should validate_numericality_of(:trub_chiller_loss).is_greater_than_or_equal_to(0) }
16
+ it { should validate_numericality_of(:tun_specific_heat).is_greater_than_or_equal_to(0) }
17
+ it { should validate_numericality_of(:tun_volume).is_greater_than_or_equal_to(0) }
18
+ it { should validate_numericality_of(:tun_weight).is_greater_than_or_equal_to(0) }
19
+
20
+
21
+ shared_examples_for :record_typing do
22
+ let(:all_types) { %i( equipment fermentable hop mash_profile mash_step misc recipe style water yeast ) }
23
+ it 'correctly answers to its own type' do
24
+ expect subject.send("#{type}?")
25
+ end
26
+ it 'correctly answers to other types' do
27
+ (all_types - [type]).each do |question|
28
+ expect ! subject.send("#{question}?")
29
+ end
30
+ end
31
+ end
32
+
33
+
34
+ it_behaves_like :record_typing do
35
+ let(:type) { :equipment }
36
+ end
37
+
38
+ end
@@ -0,0 +1,17 @@
1
+ require 'shared/active_model_lint'
2
+
3
+ describe NRB::BeerXML::Fermentable do
4
+
5
+ it_behaves_like :ActiveModel
6
+
7
+ it { should validate_presence_of :amount }
8
+ it { should validate_presence_of :color }
9
+ it { should validate_presence_of :type }
10
+ it { should validate_presence_of :yield }
11
+
12
+ it { should validate_inclusion_of(:type).in_array( [ "Adjunct", "Dry Extract", "Extract", "Grain", "Sugar" ] ) }
13
+
14
+ it { should validate_numericality_of(:diastatic_power).is_greater_than_or_equal_to(0) }
15
+
16
+
17
+ end