nemah 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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