nemah 0.1.1

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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +6 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +44 -0
  9. data/Rakefile +6 -0
  10. data/lib/nemah.rb +2 -0
  11. data/lib/nemah/horse.rb +80 -0
  12. data/lib/nemah/need.rb +41 -0
  13. data/lib/nemah/specific_need/abstract_need.rb +49 -0
  14. data/lib/nemah/specific_need/calcium.rb +13 -0
  15. data/lib/nemah/specific_need/energy.rb +63 -0
  16. data/lib/nemah/specific_need/magnesium.rb +13 -0
  17. data/lib/nemah/specific_need/mineral_behaviour.rb +77 -0
  18. data/lib/nemah/specific_need/phosphor.rb +13 -0
  19. data/lib/nemah/specific_need/protein.rb +23 -0
  20. data/lib/nemah/specific_need/salt.rb +13 -0
  21. data/lib/nemah/specific_need/selenium.rb +23 -0
  22. data/lib/nemah/specific_need/solids.rb +27 -0
  23. data/lib/nemah/version.rb +3 -0
  24. data/lib/nemah/workload.rb +30 -0
  25. data/nemah.gemspec +27 -0
  26. data/spec/nemah/horse_spec.rb +93 -0
  27. data/spec/nemah/need_spec.rb +103 -0
  28. data/spec/nemah/specific_need/calcium_spec.rb +15 -0
  29. data/spec/nemah/specific_need/energy_spec.rb +76 -0
  30. data/spec/nemah/specific_need/magnesium_spec.rb +15 -0
  31. data/spec/nemah/specific_need/phosphor_spec.rb +15 -0
  32. data/spec/nemah/specific_need/protein_spec.rb +55 -0
  33. data/spec/nemah/specific_need/salt_spec.rb +15 -0
  34. data/spec/nemah/specific_need/selenium_spec.rb +57 -0
  35. data/spec/nemah/specific_need/solids_spec.rb +57 -0
  36. data/spec/nemah/workload_spec.rb +35 -0
  37. data/spec/spec_helper.rb +4 -0
  38. data/spec/support/shared_examples_for_a_mineral.rb +73 -0
  39. data/spec/support/shared_examples_for_a_specific_need.rb +12 -0
  40. metadata +182 -0
@@ -0,0 +1,13 @@
1
+ module Nemah
2
+ module SpecificNeed
3
+ class Phosphor < AbstractNeed
4
+ include Nemah::SpecificNeed::MineralBehaviour
5
+
6
+ private
7
+
8
+ def factor_table
9
+ FactorTable.new(no_work: 2.8, below_30: 3.6, below_50: 4.2, below_75: 5.8, above_75: 5.8)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Nemah
2
+ module SpecificNeed
3
+ class Protein < AbstractNeed
4
+ private
5
+
6
+ def _ideal
7
+ ideal_energy * 6
8
+ end
9
+
10
+ def _min
11
+ 0.90 * _ideal
12
+ end
13
+
14
+ def _max
15
+ 1.10 * _ideal
16
+ end
17
+
18
+ def ideal_energy
19
+ need.energy.ideal(decimals: 4)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Nemah
2
+ module SpecificNeed
3
+ class Salt < AbstractNeed
4
+ include Nemah::SpecificNeed::MineralBehaviour
5
+
6
+ private
7
+
8
+ def factor_table
9
+ FactorTable.new(no_work: 5.1, below_30: 7.0, below_50: 9.0, below_75: 9.0, above_75: 13.0)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Nemah
2
+ module SpecificNeed
3
+ class Selenium < AbstractNeed
4
+ def unit
5
+ :mg
6
+ end
7
+
8
+ private
9
+
10
+ def _ideal
11
+ min
12
+ end
13
+
14
+ def _min
15
+ horse.weight_in_deciton * 0.20
16
+ end
17
+
18
+ def _max
19
+ horse.weight_in_deciton * 5.00
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Nemah
2
+ module SpecificNeed
3
+ class Solids < AbstractNeed
4
+ def max(decimals: 2)
5
+ _max
6
+ end
7
+
8
+ def unit
9
+ :kg
10
+ end
11
+
12
+ private
13
+
14
+ def _ideal
15
+ min
16
+ end
17
+
18
+ def _min
19
+ horse.weight_in_deciton * 1.50
20
+ end
21
+
22
+ def _max
23
+ Float::INFINITY
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Nemah
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,30 @@
1
+ module Nemah
2
+ class Workload
3
+ ATTRIBUTES = [:walk, :trot_and_canter, :days_per_week].freeze
4
+
5
+ attr_reader *ATTRIBUTES
6
+
7
+ def initialize(args = {})
8
+ attributes.each do |attribute|
9
+ instance_variable_set "@#{attribute}", args.fetch(attribute, 0)
10
+ end
11
+ assert_valid_days_per_week
12
+ end
13
+
14
+ def ==(other)
15
+ attributes.all? do |attribute|
16
+ send(attribute) == other.send(attribute)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def attributes
23
+ ATTRIBUTES.dup
24
+ end
25
+
26
+ def assert_valid_days_per_week
27
+ raise ArgumentError.new('days_per_week must be between 0 and 7') unless (0..7).include? days_per_week
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nemah/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'nemah'
8
+ spec.version = Nemah::VERSION
9
+ spec.authors = ['Kim Persson', 'Lennart Fridén']
10
+ spec.email = ['forgetmenotox@notingham.se']
11
+ spec.description = 'Nemah is a library for calculating the proper amount of fodder for your horse.'
12
+ spec.summary = 'Horse fodder calculations library.'
13
+ spec.homepage = 'https://github.com/Lavinia/Nemah'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'byebug', '~> 2.5'
23
+ spec.add_development_dependency 'guard', '~> 2.2'
24
+ spec.add_development_dependency 'guard-rspec', '~> 4.2'
25
+ spec.add_development_dependency 'rake', '~> 10.1'
26
+ spec.add_development_dependency 'rspec', '~> 2.14'
27
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nemah do
4
+ describe Nemah::Horse do
5
+ it 'must have a weight' do
6
+ expect { a_horse_without(:weight) }.to raise_error(ArgumentError, 'weight required')
7
+ end
8
+
9
+ it 'must have a gender' do
10
+ expect { a_horse_without(:gender) }.to raise_error(ArgumentError, 'gender required')
11
+ end
12
+
13
+ it 'cannot have a non-existing gender' do
14
+ expect {
15
+ a_horse_with(gender: :non_existing)
16
+ }.to raise_error(ArgumentError, ':non_existing is not an allowed gender')
17
+ end
18
+
19
+ it 'can be named' do
20
+ expect(a_horse_named('Nemah').name).to eq('Nemah')
21
+ end
22
+
23
+ it 'is considered to be of normal difficulty to feed' do
24
+ expect(a_horse.feedability).to eq(:normal)
25
+ end
26
+
27
+ it 'can be hard to feed' do
28
+ expect(a_horse_with(feedability: :hard).feedability).to eq(:hard)
29
+ end
30
+
31
+ it 'can be easy to feed' do
32
+ expect(a_horse_with(feedability: :easy).feedability).to eq(:easy)
33
+ end
34
+
35
+ it 'cannot be impossible to feed' do
36
+ expect {
37
+ a_horse_with(feedability: :impossible)
38
+ }.to raise_error(ArgumentError, ':impossible is not an allowed feedability')
39
+ end
40
+
41
+ it 'cannot have a negative weight' do
42
+ expect {
43
+ a_horse_with(weight: -50)
44
+ }.to raise_error(ArgumentError, '-50 is not an allowed weight')
45
+ end
46
+
47
+ it 'defaults to no workload' do
48
+ no_workload = Nemah::Workload.new(walk: 0, trot_and_canter: 0, days_per_week: 0)
49
+ expect(a_horse_without(:workload).workload).to eq(no_workload)
50
+ end
51
+
52
+ describe '#stallion?' do
53
+ it 'returns true if the horse is a stallion' do
54
+ expect(a_horse_with gender: :stallion).to be_stallion
55
+ end
56
+ end
57
+
58
+ describe '#weight_in_deciton' do
59
+ it 'returns the weight divided by a hundred' do
60
+ expect(a_horse_with(weight: 650).weight_in_deciton).to eq(6.5)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def workload_for_a_horse_told(method)
67
+ workload = double(walk: 30, trot_and_canter: 20, days_per_week: 3)
68
+ horse = a_horse_with(workload: workload)
69
+ horse.send(method)
70
+ workload
71
+ end
72
+
73
+ def a_horse
74
+ Nemah::Horse.new(default_attributes)
75
+ end
76
+
77
+ def a_horse_with(attributes)
78
+ Nemah::Horse.new(default_attributes.merge(attributes))
79
+ end
80
+
81
+ def a_horse_without(attribute)
82
+ Nemah::Horse.new(default_attributes.delete_if { |key, _| key == attribute })
83
+ end
84
+
85
+ def a_horse_named(name)
86
+ a_horse_with(name: name)
87
+ end
88
+
89
+ def default_attributes
90
+ { weight: 450, gender: :mare }
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nemah::Need do
4
+ describe 'calculating solids' do
5
+ it 'for an arabian princess' do
6
+ expect(need_for_nemah.solids.to_rounded_range).to eq(6.75..Float::INFINITY)
7
+ end
8
+
9
+ it 'for a huge horse' do
10
+ expect(need_for_samson.solids.to_rounded_range).to eq(13.50..Float::INFINITY)
11
+ end
12
+ end
13
+
14
+ describe 'calculating energy need with a ±3 variance' do
15
+ it 'for a non-working, hard to feed mare' do
16
+ expect(need_for_nemah.energy.to_rounded_range).to eq(50.74..56.74)
17
+ end
18
+
19
+ it 'for a non-working, easy to feed gelding' do
20
+ expect(need_for_samson.energy.to_rounded_range).to eq(79.16..85.16)
21
+ end
22
+
23
+ it 'for a non-working, normal to feed stallion' do
24
+ expect(need_for_janus.energy.to_rounded_range).to eq(67.01..73.01)
25
+ end
26
+
27
+ it 'for a moderately-working, hard to feed mare' do
28
+ expect(need_for_nemah(workload: moderate_workload).energy.to_rounded_range).to eq(71.31..77.31)
29
+ end
30
+
31
+ it 'for a hard-working, easy to feed gelding' do
32
+ expect(need_for_samson(workload: hard_workload).energy.to_rounded_range).to eq(164.66..170.66)
33
+ end
34
+ end
35
+
36
+ describe 'calculating protein need with a ±10% variance' do
37
+ it 'for a non-working easy to feed gelding' do
38
+ expect(need_for_samson.protein.to_rounded_range).to eq(443.66..542.25)
39
+ end
40
+
41
+ it 'for a moderately-working, hard to feed mare' do
42
+ expect(need_for_nemah(workload: moderate_workload).protein.to_rounded_range).to eq(401.26..490.43)
43
+ end
44
+ end
45
+
46
+
47
+ describe 'calculating calcium' do
48
+ it 'for an arabian princess' do
49
+ expect(need_for_nemah.calcium.to_rounded_range).to eq(18.00..Float::INFINITY)
50
+ end
51
+ end
52
+
53
+ describe 'calculating magnesium' do
54
+ it 'for an arabian princess' do
55
+ expect(need_for_nemah.magnesium.to_rounded_range).to eq(6.75..Float::INFINITY)
56
+ end
57
+ end
58
+
59
+ describe 'calculating phosphor' do
60
+ it 'for an arabian princess' do
61
+ expect(need_for_nemah.phosphor.to_rounded_range).to eq(12.60..Float::INFINITY)
62
+ end
63
+ end
64
+
65
+ describe 'calculating selenium' do
66
+ it 'for an arabian princess' do
67
+ expect(need_for_nemah.selenium.to_rounded_range).to eq(0.90..22.50)
68
+ end
69
+ end
70
+
71
+ describe 'calculating salt' do
72
+ it 'for an arabian princess' do
73
+ expect(need_for_nemah.salt.to_rounded_range).to eq(22.95..Float::INFINITY)
74
+ end
75
+ end
76
+
77
+
78
+ private
79
+
80
+ def moderate_workload
81
+ Nemah::Workload.new(walk: 60, trot_and_canter: 40, days_per_week: 5)
82
+ end
83
+
84
+ def hard_workload
85
+ Nemah::Workload.new(walk: 85, trot_and_canter: 60, days_per_week: 7)
86
+ end
87
+
88
+ def need_for_horse_with(args)
89
+ Nemah::Need.new(Nemah::Horse.new(args))
90
+ end
91
+
92
+ def need_for_samson(args = {})
93
+ need_for_horse_with({weight: 900, gender: :gelding, feedability: :easy}.merge args)
94
+ end
95
+
96
+ def need_for_nemah(args = {})
97
+ need_for_horse_with({weight: 450, gender: :mare, feedability: :hard}.merge args)
98
+ end
99
+
100
+ def need_for_janus
101
+ need_for_horse_with(weight: 600, gender: :stallion, feedability: :normal)
102
+ end
103
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nemah::SpecificNeed::Calcium do
4
+ it_behaves_like 'a specific need' do
5
+ let(:unit) { :g }
6
+ end
7
+
8
+ it_behaves_like 'a mineral' do
9
+ let(:no_workload_min_need) { 21.20 }
10
+ let(:light_workload_min_need) { 31.80 }
11
+ let(:medium_workload_min_need) { 37.10 }
12
+ let(:hard_workload_min_need) { 42.40 }
13
+ let(:very_hard_workload_min_need) { 42.40 }
14
+ end
15
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe Nemah::SpecificNeed::Energy do
4
+ it_behaves_like 'a specific need' do
5
+ let(:unit) { :MJ }
6
+ end
7
+
8
+ describe '#for_workload' do
9
+ it 'returns the workload portion of the energy need' do
10
+ expect(energy.for_workload).to eq(10.97)
11
+ end
12
+ end
13
+
14
+ describe '#for_maintenance' do
15
+ it 'returns the maintenance energy need' do
16
+ expect(energy.for_maintenance).to eq(70.01)
17
+ end
18
+ end
19
+
20
+ describe '#to_rounded_range' do
21
+ it 'returns a range between minimum and maximum allowed amounts, rounded to two decimals' do
22
+ expect(energy.to_rounded_range).to eq(77.98..83.98)
23
+ end
24
+
25
+ it 'optionally takes the number of decimals' do
26
+ expect(energy.to_rounded_range(decimals: 3)).to eq(77.982..83.982)
27
+ end
28
+ end
29
+
30
+ describe '#ideal' do
31
+ it 'returns the ideal energy need' do
32
+ expect(energy.ideal).to eq(80.98)
33
+ end
34
+
35
+ it 'optionally takes the number of decimals' do
36
+ expect(energy.ideal(decimals: 5)).to eq(80.98229)
37
+ end
38
+ end
39
+
40
+ describe '#min' do
41
+ it 'returns the minimum energy need' do
42
+ expect(energy.min).to eq(77.98)
43
+ end
44
+
45
+ it 'optionally takes the number of decimals' do
46
+ expect(energy.min(decimals: 4)).to eq(77.9823)
47
+ end
48
+
49
+ it 'cannot be less than 0' do
50
+ feather_weight_horse_energy = Nemah::SpecificNeed::Energy.new(need(weight: 0.1))
51
+ expect(feather_weight_horse_energy.min).to eq(0)
52
+ end
53
+ end
54
+
55
+ describe '#max' do
56
+ it 'returns the maximum energy need' do
57
+ expect(energy.max).to eq(83.98)
58
+ end
59
+
60
+ it 'optionally takes the number of decimals' do
61
+ expect(energy.max(decimals: 0)).to eq(84)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def energy
68
+ Nemah::SpecificNeed::Energy.new(need)
69
+ end
70
+
71
+ def need(weight: 600)
72
+ workload = double('workload', walk: 30, trot_and_canter: 20, days_per_week: 4)
73
+ horse = double('horse', stallion?: true, weight: weight, weight_in_deciton: weight / 100.0, feedability: :normal, workload: workload)
74
+ double('need', horse: horse)
75
+ end
76
+ end