nemah 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +6 -0
- data/lib/nemah.rb +2 -0
- data/lib/nemah/horse.rb +80 -0
- data/lib/nemah/need.rb +41 -0
- data/lib/nemah/specific_need/abstract_need.rb +49 -0
- data/lib/nemah/specific_need/calcium.rb +13 -0
- data/lib/nemah/specific_need/energy.rb +63 -0
- data/lib/nemah/specific_need/magnesium.rb +13 -0
- data/lib/nemah/specific_need/mineral_behaviour.rb +77 -0
- data/lib/nemah/specific_need/phosphor.rb +13 -0
- data/lib/nemah/specific_need/protein.rb +23 -0
- data/lib/nemah/specific_need/salt.rb +13 -0
- data/lib/nemah/specific_need/selenium.rb +23 -0
- data/lib/nemah/specific_need/solids.rb +27 -0
- data/lib/nemah/version.rb +3 -0
- data/lib/nemah/workload.rb +30 -0
- data/nemah.gemspec +27 -0
- data/spec/nemah/horse_spec.rb +93 -0
- data/spec/nemah/need_spec.rb +103 -0
- data/spec/nemah/specific_need/calcium_spec.rb +15 -0
- data/spec/nemah/specific_need/energy_spec.rb +76 -0
- data/spec/nemah/specific_need/magnesium_spec.rb +15 -0
- data/spec/nemah/specific_need/phosphor_spec.rb +15 -0
- data/spec/nemah/specific_need/protein_spec.rb +55 -0
- data/spec/nemah/specific_need/salt_spec.rb +15 -0
- data/spec/nemah/specific_need/selenium_spec.rb +57 -0
- data/spec/nemah/specific_need/solids_spec.rb +57 -0
- data/spec/nemah/workload_spec.rb +35 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/shared_examples_for_a_mineral.rb +73 -0
- data/spec/support/shared_examples_for_a_specific_need.rb +12 -0
- 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,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
|
data/nemah.gemspec
ADDED
@@ -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
|