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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +33 -0
- data/lib/beerxml.rb +24 -0
- data/lib/beerxml/builder.rb +30 -0
- data/lib/beerxml/equipment.rb +38 -0
- data/lib/beerxml/fermentable.rb +33 -0
- data/lib/beerxml/hop.rb +38 -0
- data/lib/beerxml/inflector.rb +27 -0
- data/lib/beerxml/mash.rb +35 -0
- data/lib/beerxml/mash_step.rb +26 -0
- data/lib/beerxml/misc.rb +24 -0
- data/lib/beerxml/parser.rb +114 -0
- data/lib/beerxml/recipe.rb +109 -0
- data/lib/beerxml/record.rb +49 -0
- data/lib/beerxml/record_set.rb +39 -0
- data/lib/beerxml/record_validators.rb +8 -0
- data/lib/beerxml/record_validators/boolean_validator.rb +16 -0
- data/lib/beerxml/record_validators/percentage_validator.rb +38 -0
- data/lib/beerxml/style.rb +49 -0
- data/lib/beerxml/version.rb +6 -0
- data/lib/beerxml/water.rb +26 -0
- data/lib/beerxml/yeast.rb +38 -0
- data/spec/cases/beerxml/builder_spec.rb +107 -0
- data/spec/cases/beerxml/equipment_spec.rb +38 -0
- data/spec/cases/beerxml/fermentable_spec.rb +17 -0
- data/spec/cases/beerxml/hop_spec.rb +21 -0
- data/spec/cases/beerxml/mash_spec.rb +25 -0
- data/spec/cases/beerxml/mash_step_spec.rb +39 -0
- data/spec/cases/beerxml/misc_spec.rb +14 -0
- data/spec/cases/beerxml/parser_spec.rb +30 -0
- data/spec/cases/beerxml/recipe_spec.rb +98 -0
- data/spec/cases/beerxml/record_set_spec.rb +36 -0
- data/spec/cases/beerxml/record_spec.rb +15 -0
- data/spec/cases/beerxml/record_validators/boolean_validator_spec.rb +50 -0
- data/spec/cases/beerxml/record_validators/percentage_validator_spec.rb +98 -0
- data/spec/cases/beerxml/style_spec.rb +36 -0
- data/spec/cases/beerxml/version_spec.rb +13 -0
- data/spec/cases/beerxml/water_spec.rb +25 -0
- data/spec/cases/beerxml/yeast_spec.rb +20 -0
- data/spec/cases/beerxml_spec.rb +3 -0
- data/spec/fixtures/equipment.xml +28 -0
- data/spec/fixtures/recipes.xml +1412 -0
- data/spec/shared/active_model_lint.rb +21 -0
- data/spec/shared/record_typing.rb +19 -0
- data/spec/spec_helper.rb +8 -0
- metadata +210 -0
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# NRB::Beerxml
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/NewRepublicBrewing/beerxml)
|
4
|
+
|
5
|
+
A BeerXML parser
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'nrb-beerxml'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
% bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
% gem install nrb-beerxml
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it ( https://github.com/NewRepublicBrewing/nrb-beerxml/fork )
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Add some beers'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-beer`)
|
33
|
+
5. Create a Pull Request
|
data/lib/beerxml.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module NRB
|
2
|
+
module BeerXML
|
3
|
+
|
4
|
+
autoload :Builder, 'beerxml/builder'
|
5
|
+
autoload :Equipment, 'beerxml/equipment'
|
6
|
+
autoload :Fermentable, 'beerxml/fermentable'
|
7
|
+
autoload :Hop, 'beerxml/hop'
|
8
|
+
autoload :Inflector, 'beerxml/inflector'
|
9
|
+
autoload :Mash, 'beerxml/mash'
|
10
|
+
autoload :MashStep, 'beerxml/mash_step'
|
11
|
+
autoload :Misc, 'beerxml/misc'
|
12
|
+
autoload :Parser, 'beerxml/parser'
|
13
|
+
autoload :Recipe, 'beerxml/recipe'
|
14
|
+
autoload :Record, 'beerxml/record'
|
15
|
+
autoload :RecordSet, 'beerxml/record_set'
|
16
|
+
autoload :RecordValidators, 'beerxml/record_validators'
|
17
|
+
autoload :Style, 'beerxml/style'
|
18
|
+
autoload :Water, 'beerxml/water'
|
19
|
+
autoload :Yeast, 'beerxml/yeast'
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
defined?(BeerXML) || BeerXML = NRB::BeerXML
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module NRB; module BeerXML
|
2
|
+
class Builder
|
3
|
+
|
4
|
+
include Inflector
|
5
|
+
|
6
|
+
RECORDS = %w( Equipment Fermentable Hop Mash MashStep Misc Recipe Style Water Yeast )
|
7
|
+
|
8
|
+
def self.records; RECORDS; end
|
9
|
+
def self.record_sets; records.map() { |type| "#{type}s" }; end
|
10
|
+
|
11
|
+
|
12
|
+
def build(type)
|
13
|
+
if record_set? type
|
14
|
+
RecordSet.new record_type: underscore(type.sub(/s$/,'')).to_sym
|
15
|
+
else
|
16
|
+
::Module.nesting[1].const_get(type).new
|
17
|
+
end
|
18
|
+
rescue NameError
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
|
25
|
+
def record_set?(type)
|
26
|
+
self.class.record_sets.include? type
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end; end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'beerxml/record_validators/boolean_validator'
|
2
|
+
require 'beerxml/record_validators/percentage_validator'
|
3
|
+
module NRB
|
4
|
+
module BeerXML
|
5
|
+
class Equipment < Record
|
6
|
+
|
7
|
+
attr_accessor :batch_size # volume required
|
8
|
+
attr_accessor :boil_size # volume required
|
9
|
+
attr_accessor :boil_time # time
|
10
|
+
attr_accessor :calc_boil_volume # boolean
|
11
|
+
attr_accessor :evap_rate # percent
|
12
|
+
attr_accessor :hop_utilization # percent
|
13
|
+
attr_accessor :lauter_tun_deadspace # volume
|
14
|
+
attr_accessor :notes # string
|
15
|
+
attr_accessor :top_up_kettle # volume
|
16
|
+
attr_accessor :top_up_water # volume
|
17
|
+
attr_accessor :trub_chiller_loss # volume
|
18
|
+
attr_accessor :tun_specific_heat # float
|
19
|
+
attr_accessor :tun_volume # volume
|
20
|
+
attr_accessor :tun_weight # weight
|
21
|
+
|
22
|
+
validates :batch_size, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
23
|
+
validates :boil_size, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
24
|
+
validates :boil_time, numericality: { greater_than_or_equal_to: 0 }
|
25
|
+
validates :calc_boil_volume, boolean: true
|
26
|
+
validates :evap_rate, percentage: true
|
27
|
+
validates :hop_utilization, percentage: { give_110: true }
|
28
|
+
validates :lauter_tun_deadspace, numericality: { greater_than_or_equal_to: 0 }
|
29
|
+
validates :top_up_kettle, numericality: { greater_than_or_equal_to: 0 }
|
30
|
+
validates :top_up_water, numericality: { greater_than_or_equal_to: 0 }
|
31
|
+
validates :trub_chiller_loss, numericality: { greater_than_or_equal_to: 0 }
|
32
|
+
validates :tun_specific_heat, numericality: { greater_than_or_equal_to: 0 }
|
33
|
+
validates :tun_volume, numericality: { greater_than_or_equal_to: 0 }
|
34
|
+
validates :tun_weight, numericality: { greater_than_or_equal_to: 0 }
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'beerxml/record_validators/boolean_validator' # hack so "validates percentage:" magic works
|
2
|
+
require 'beerxml/record_validators/percentage_validator' # hack so "validates percentage:" magic works
|
3
|
+
|
4
|
+
module NRB
|
5
|
+
module BeerXML
|
6
|
+
class Fermentable < Record
|
7
|
+
|
8
|
+
attr_accessor :add_after_boil # boolean
|
9
|
+
attr_accessor :amount # weight required
|
10
|
+
attr_accessor :coarse_fine_diff # percent
|
11
|
+
attr_accessor :color # float required
|
12
|
+
attr_accessor :diastatic_power # float
|
13
|
+
attr_accessor :ibu_gal_per_lb # float
|
14
|
+
attr_accessor :max_in_batch # percent
|
15
|
+
attr_accessor :moisture # percent
|
16
|
+
attr_accessor :notes # text
|
17
|
+
attr_accessor :origin # text
|
18
|
+
attr_accessor :recommend_mash # boolean
|
19
|
+
attr_accessor :supplier # text
|
20
|
+
attr_accessor :type # list required
|
21
|
+
attr_accessor :yield # percent required
|
22
|
+
|
23
|
+
validates :add_after_boil, boolean: true
|
24
|
+
validates :amount, presence: true
|
25
|
+
validates :color, presence: true
|
26
|
+
validates :type, inclusion: { in: [ "Adjunct", "Dry Extract", "Extract", "Grain", "Sugar" ] }, presence: true
|
27
|
+
validates :yield, presence: true
|
28
|
+
|
29
|
+
validates :diastatic_power, numericality: { greater_than_or_equal_to: 0 }
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/beerxml/hop.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'beerxml/record_validators/percentage_validator' # hack so "validates percentage:" magic works
|
2
|
+
module NRB
|
3
|
+
module BeerXML
|
4
|
+
class Hop < Record
|
5
|
+
|
6
|
+
attr_accessor :alpha # percentage required
|
7
|
+
attr_accessor :amount # weight required
|
8
|
+
attr_accessor :beta # percentage
|
9
|
+
attr_accessor :caryophyllene # percentage
|
10
|
+
attr_accessor :cohumulone # percentage
|
11
|
+
attr_accessor :form # list
|
12
|
+
attr_accessor :hsi # percentage
|
13
|
+
attr_accessor :myrcene # percentage
|
14
|
+
attr_accessor :notes # text
|
15
|
+
attr_accessor :origin # text
|
16
|
+
attr_accessor :substitutes # text
|
17
|
+
attr_accessor :time # time required
|
18
|
+
attr_accessor :type # list
|
19
|
+
attr_accessor :use # list required
|
20
|
+
|
21
|
+
validates :alpha, presence: true, percentage: true
|
22
|
+
validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
23
|
+
validates :beta, percentage: true
|
24
|
+
validates :caryophyllene, percentage: true
|
25
|
+
validates :cohumulone, percentage: true
|
26
|
+
validates :form, inclusion: { in: %w(Leaf Pellet Plug) }
|
27
|
+
validates :hsi, percentage: true
|
28
|
+
validates :myrcene, percentage: true
|
29
|
+
validates :time, presence: true,
|
30
|
+
numericality: { greater_than_or_equal_to: 0 }
|
31
|
+
validates :type, inclusion: { in: %w(Aroma Bittering Both) }
|
32
|
+
|
33
|
+
validates :use, presence: true,
|
34
|
+
inclusion: { in: [ "Aroma", "Boil", "Dry Hop", "First Wort", "Mash" ] }
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module NRB; module BeerXML
|
2
|
+
module Inflector
|
3
|
+
|
4
|
+
def camelize(term, uppercase_first_letter = true)
|
5
|
+
string = term.to_s
|
6
|
+
if uppercase_first_letter
|
7
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
8
|
+
else
|
9
|
+
string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { $&.downcase }
|
10
|
+
end
|
11
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
12
|
+
string.gsub!('/', '::')
|
13
|
+
string
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def underscore(camel_cased_word)
|
18
|
+
word = camel_cased_word.to_s.gsub('::', '/')
|
19
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
20
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
21
|
+
word.tr!("-", "_")
|
22
|
+
word.downcase!
|
23
|
+
word
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end; end
|
data/lib/beerxml/mash.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'beerxml/record_validators/boolean_validator'
|
2
|
+
module NRB
|
3
|
+
module BeerXML
|
4
|
+
class Mash < Record
|
5
|
+
|
6
|
+
attr_accessor :equip_adjust # boolean
|
7
|
+
attr_accessor :grain_temp # temperature required
|
8
|
+
attr_accessor :mash_steps # record set required
|
9
|
+
attr_accessor :notes # string
|
10
|
+
attr_accessor :ph # float
|
11
|
+
attr_accessor :sparge_temp # temperature
|
12
|
+
attr_accessor :tun_specific_heat # float
|
13
|
+
attr_accessor :tun_temp # temperature
|
14
|
+
attr_accessor :tun_weight # weight
|
15
|
+
|
16
|
+
validates :grain_temp, numericality: true, presence: true
|
17
|
+
validates :mash_steps, presence: true
|
18
|
+
validates :ph, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 14 }
|
19
|
+
validates :sparge_temp, numericality: true
|
20
|
+
validates :tun_specific_heat, numericality: { greater_than_or_equal_to: 0 }
|
21
|
+
validates :tun_temp, numericality: true
|
22
|
+
validates :tun_weight, numericality: { greater_than_or_equal_to: 0 }
|
23
|
+
|
24
|
+
|
25
|
+
after_initialize :initialize_mash_steps
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize_mash_steps
|
30
|
+
self.mash_steps ||= RecordSet.new(record_type: :mash_step)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module NRB
|
2
|
+
module BeerXML
|
3
|
+
class MashStep < Record
|
4
|
+
|
5
|
+
attr_accessor :end_temp # temperature
|
6
|
+
attr_accessor :infuse_amount # volume conditionally required
|
7
|
+
attr_accessor :ramp_time # time
|
8
|
+
attr_accessor :step_temp # temperature required
|
9
|
+
attr_accessor :step_time # time required
|
10
|
+
attr_accessor :type # list required
|
11
|
+
|
12
|
+
validates :end_temp, numericality: true
|
13
|
+
validates :infuse_amount, presence: { if: Proc.new { |step| step.infuse_amount_required? } }, numericality: true
|
14
|
+
validates :ramp_time, numericality: { greater_than_or_equal_to: 0 }
|
15
|
+
validates :step_temp, numericality: true, presence: true
|
16
|
+
validates :step_time, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
17
|
+
validates :type, inclusion: { in: %w(Decoction Infusion Temperature) },
|
18
|
+
presence: true
|
19
|
+
|
20
|
+
def infuse_amount_required?
|
21
|
+
type == "Infusion"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/beerxml/misc.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'beerxml/record_validators/boolean_validator' # hack so "validates boolean:" magic works
|
2
|
+
module NRB
|
3
|
+
module BeerXML
|
4
|
+
class Misc < Record
|
5
|
+
|
6
|
+
attr_accessor :amount # float required
|
7
|
+
attr_accessor :amount_is_weight # boolean
|
8
|
+
attr_accessor :notes # string
|
9
|
+
attr_accessor :time # time required
|
10
|
+
attr_accessor :type # list required
|
11
|
+
attr_accessor :use # list required
|
12
|
+
attr_accessor :use_for # string
|
13
|
+
|
14
|
+
validates :amount, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
15
|
+
validates :amount_is_weight, boolean: true
|
16
|
+
validates :time, numericality: { greater_than_or_equal_to: 0 },
|
17
|
+
presence: true
|
18
|
+
validates :type, inclusion: { in: ["Fining", "Flavor", "Herb", "Other", "Spice", "Water Agent"] }, presence: true
|
19
|
+
validates :use, inclusion: { in: %w(Boil Bottling Mash Primary Secondary) }, presence: true
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module NRB; module BeerXML
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
include Inflector
|
7
|
+
|
8
|
+
attr_reader :builder, :reader
|
9
|
+
|
10
|
+
def initialize(builder: Builder.new, reader: Nokogiri::XML)
|
11
|
+
@builder = builder
|
12
|
+
@reader = reader
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def parse(entry)
|
17
|
+
doc = case entry
|
18
|
+
when IO
|
19
|
+
parse_xml entry
|
20
|
+
when String
|
21
|
+
parse_path(file: entry)
|
22
|
+
else
|
23
|
+
raise ArgumentError "Don't know how to parse a #{entry.class}"
|
24
|
+
end
|
25
|
+
parse_node doc.root
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def assign_attribute(object, node)
|
31
|
+
return unless object
|
32
|
+
return unless attribute_node?(node)
|
33
|
+
meth = guess_attribute_assignment_method node
|
34
|
+
if object.respond_to? meth
|
35
|
+
object.send meth, attribute_value(node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def assign_child_to_parent(parent, child)
|
41
|
+
meth = guess_child_assignment_method parent, child
|
42
|
+
parent.respond_to?(meth) && parent.send(meth, child)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def attribute_node?(node)
|
47
|
+
node.children.size == 1 && node.children.first.text?
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def attribute_value(node)
|
52
|
+
node.children.first.text
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def guess_attribute_assignment_method(node)
|
57
|
+
node.name.downcase + '='
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def guess_child_assignment_method(parent, child)
|
62
|
+
# Smelly, needs refactor
|
63
|
+
return :<< if parent.is_a? RecordSet
|
64
|
+
name = if child.is_a? RecordSet
|
65
|
+
"#{child.record_type.to_s}s"
|
66
|
+
else
|
67
|
+
child.class.name.split(/::/).last
|
68
|
+
end
|
69
|
+
underscore(name) + '='
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def guess_class_name(string)
|
74
|
+
camelize string.downcase
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def parse_node(node,parent=nil)
|
79
|
+
return unless node
|
80
|
+
|
81
|
+
if attribute_node?(node)
|
82
|
+
assign_attribute parent, node
|
83
|
+
|
84
|
+
else
|
85
|
+
|
86
|
+
obj = builder.build guess_class_name(node.name)
|
87
|
+
|
88
|
+
node.children.each do |child|
|
89
|
+
parse_node child, obj
|
90
|
+
end
|
91
|
+
|
92
|
+
assign_child_to_parent parent, obj
|
93
|
+
|
94
|
+
obj
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def parse_path(file: nil)
|
100
|
+
f = File.open(file)
|
101
|
+
doc = parse_xml(f)
|
102
|
+
f.close
|
103
|
+
doc
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def parse_xml(stream)
|
108
|
+
reader.parse(stream) do |config|
|
109
|
+
config.noblanks
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end; end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'beerxml/record_validators/boolean_validator' # hack so "validates boolean:" magic works
|
2
|
+
require 'beerxml/record_validators/percentage_validator' # hack so "validates percentag:" magic works
|
3
|
+
module NRB
|
4
|
+
module BeerXML
|
5
|
+
class Recipe < Record
|
6
|
+
|
7
|
+
after_initialize :set_up_record_sets
|
8
|
+
|
9
|
+
attr_accessor :age # time
|
10
|
+
attr_accessor :age_temp # temperature
|
11
|
+
attr_accessor :asst_brewer # string
|
12
|
+
attr_accessor :batch_size # volume required
|
13
|
+
attr_accessor :boil_size # volume required
|
14
|
+
attr_accessor :boil_time # time required
|
15
|
+
attr_accessor :brewer # string required
|
16
|
+
attr_accessor :carbonation # float
|
17
|
+
attr_accessor :carbonation_temp # temperature
|
18
|
+
attr_accessor :date # string
|
19
|
+
attr_accessor :efficiency # percentage conditionally required
|
20
|
+
attr_reader :equipment # equipment record
|
21
|
+
attr_accessor :fermentables # fermentables record set required
|
22
|
+
attr_accessor :fermentation_stages # integer
|
23
|
+
attr_accessor :fg # specific gravity
|
24
|
+
attr_accessor :forced_carbonation # boolean
|
25
|
+
attr_accessor :hops # hops record set required
|
26
|
+
attr_accessor :keg_priming_factor # float
|
27
|
+
attr_accessor :mash # mash profile record required
|
28
|
+
attr_accessor :miscs # miscs record set required
|
29
|
+
attr_accessor :notes # string
|
30
|
+
attr_accessor :og # specific gravity
|
31
|
+
attr_accessor :primary_age # time
|
32
|
+
attr_accessor :primary_temp # temperature
|
33
|
+
attr_accessor :priming_sugar_equiv # float
|
34
|
+
attr_accessor :priming_sugar_name # string
|
35
|
+
attr_accessor :secondary_age # time
|
36
|
+
attr_accessor :secondary_temp # temperature
|
37
|
+
attr_reader :style # style record required
|
38
|
+
attr_accessor :taste_notes # string
|
39
|
+
attr_accessor :taste_rating # float
|
40
|
+
attr_accessor :tertiary_age # time
|
41
|
+
attr_accessor :tertiary_temp # temperature
|
42
|
+
attr_accessor :type # list required
|
43
|
+
attr_accessor :waters # waters record set required
|
44
|
+
attr_accessor :yeasts # yeasts record set required
|
45
|
+
|
46
|
+
validates :age, numericality: { greater_than_or_equal_to: 0 }
|
47
|
+
validates :age_temp, numericality: true
|
48
|
+
validates :batch_size, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
49
|
+
validates :boil_size, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
50
|
+
validates :boil_time, numericality: { greater_than_or_equal_to: 0 }, presence: true
|
51
|
+
validates :brewer, presence: true
|
52
|
+
validates :carbonation, numericality: { greater_than_or_equal_to: 0 }
|
53
|
+
validates :carbonation_temp, numericality: true
|
54
|
+
validates :equipment, presence: true
|
55
|
+
validates :efficiency, percentage: true, presence: { if: Proc.new { |recipe| recipe.efficiency_required? } }
|
56
|
+
validates :fermentables, presence: true
|
57
|
+
validates :fermentation_stages, numericality: { only_integer: true }
|
58
|
+
validates :fg, numericality: true
|
59
|
+
validates :forced_carbonation, boolean: true
|
60
|
+
validates :hops, presence: true
|
61
|
+
validates :keg_priming_factor, numericality: true
|
62
|
+
validates :mash, presence: true
|
63
|
+
validates :miscs, presence: true
|
64
|
+
validates :og, numericality: true
|
65
|
+
validates :primary_age, numericality: { greater_than_or_equal_to: 0 }
|
66
|
+
validates :primary_temp, numericality: true
|
67
|
+
validates :priming_sugar_equiv, numericality: true
|
68
|
+
validates :secondary_age, numericality: { greater_than_or_equal_to: 0 }
|
69
|
+
validates :secondary_temp, numericality: true
|
70
|
+
validates :style, presence: true
|
71
|
+
validates :taste_rating, numericality: true
|
72
|
+
validates :tertiary_age, numericality: { greater_than_or_equal_to: 0 }
|
73
|
+
validates :tertiary_temp, numericality: true
|
74
|
+
validates :type, inclusion: { in: [ "All Grain", "Extract", "Partial Mash" ] }, presence: true
|
75
|
+
validates :waters, presence: true
|
76
|
+
validates :yeasts, presence: true
|
77
|
+
|
78
|
+
|
79
|
+
def equipment=(equipment)
|
80
|
+
restricted_assign :equipment, equipment
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def efficiency_required?
|
85
|
+
["ALl Grain", "Partial Mash"].include?(type)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def set_up_record_sets
|
90
|
+
%i(fermentable hop misc water yeast).each do |record_type|
|
91
|
+
send "#{record_type}s=", RecordSet.new(record_type: record_type)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def style=(style)
|
97
|
+
restricted_assign :style, style
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def restricted_assign(type, value)
|
103
|
+
raise "Can't assign a non-#{type} to #{type} (got a #{value.record_type}" unless value.nil? || value.send("#{type}?")
|
104
|
+
instance_variable_set "@#{type}", value
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|