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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 59e1b71fdd45cf09b6e19ad900d4c06ec6147e6d
|
4
|
+
data.tar.gz: 52ed2eb7953a641a03c5bf17a0e739d99211b184
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d14843fe110208602459b8b29449fc4ab716c9f7d8619f883a57785dd65859f41d6545da30caaf024ddc7fb278f8526269454398e2b4030ecb5176dadcb6c319
|
7
|
+
data.tar.gz: 9ce324455ba1812396ebccfe661eb2c45bf44451874d0858b2adeb25d246b3ec7db13076f6aef1a9cbd0e7c990843664c7068bc063b7ed36178084c31cf170bd
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Kim Persson and Lennart Fridén
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Nemah [![Build Status](https://secure.travis-ci.org/Lavinia/Nemah.png)](http://travis-ci.org/Lavinia/Nemah) [![Code Climate](https://codeclimate.com/github/Lavinia/Nemah.png)](https://codeclimate.com/github/Lavinia/Nemah) ![Gem version](https://badge.fury.io/rb/nemah.png)
|
2
|
+
|
3
|
+
Nemah is a library for calculating the proper amount of fodder for your horse. In real life, Nemah is an [Arabian horse](http://en.wikipedia.org/wiki/Arabian_horse) with an immense appetite.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
Given that Nemah is an Arabian princess, she wants as many rubies as possible, but no fewer than 2.0.
|
8
|
+
|
9
|
+
## Installation.
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'nemah'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install nemah
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Initial version
|
26
|
+
|
27
|
+
* Calculate a horse's need of energy, protein, solids, macrominerals, and selenium.
|
28
|
+
* Calculations are made internally with at least three digits precision. Public methods will return values rounded to two decimals by default.
|
29
|
+
|
30
|
+
### Yet to be implemented
|
31
|
+
|
32
|
+
* Microminerals apart from selenium
|
33
|
+
* Balances
|
34
|
+
* Fodder
|
35
|
+
* Rations
|
36
|
+
* Given a horse, its needs, and a list of fodder, calculate suitable rations.
|
37
|
+
|
38
|
+
## Contributing
|
39
|
+
|
40
|
+
1. Fork it
|
41
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
42
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
43
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
44
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/nemah.rb
ADDED
data/lib/nemah/horse.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Nemah
|
2
|
+
class Horse
|
3
|
+
class AttributeRequiredError < ArgumentError
|
4
|
+
def initialize(attribute)
|
5
|
+
super("#{attribute} required")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :weight, :gender, :name, :feedability, :workload
|
10
|
+
|
11
|
+
def initialize(attributes)
|
12
|
+
@weight = attributes.fetch(:weight) { raise AttributeRequiredError, :weight }
|
13
|
+
@gender = attributes.fetch(:gender) { raise AttributeRequiredError, :gender }
|
14
|
+
@name = attributes.fetch(:name, '')
|
15
|
+
@feedability = attributes.fetch(:feedability, :normal)
|
16
|
+
@workload = attributes.fetch(:workload, Workload.new)
|
17
|
+
HorseValidator.new(self).assert_validity_of(:feedability, :gender, :weight)
|
18
|
+
end
|
19
|
+
|
20
|
+
def stallion?
|
21
|
+
gender == :stallion
|
22
|
+
end
|
23
|
+
|
24
|
+
def weight_in_deciton
|
25
|
+
weight / 100.00
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
class HorseValidator
|
31
|
+
def initialize(horse)
|
32
|
+
@horse = horse
|
33
|
+
end
|
34
|
+
|
35
|
+
def assert_validity_of(*attributes)
|
36
|
+
attributes.each { |attribute| assert_valid(attribute) }
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :horse
|
42
|
+
|
43
|
+
def assert_valid(attribute)
|
44
|
+
unless allowed_values_for(attribute).include? horse.send(attribute)
|
45
|
+
raise ArgumentError, "#{horse.send(attribute).inspect} is not an allowed #{attribute}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def allowed_values_for(attribute)
|
50
|
+
case attribute
|
51
|
+
when :gender then allowed_genders
|
52
|
+
when :feedability then allowed_feedabilities
|
53
|
+
when :weight then PositiveWeight.new
|
54
|
+
else
|
55
|
+
Everything.new
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def allowed_feedabilities
|
60
|
+
[:easy, :normal, :hard]
|
61
|
+
end
|
62
|
+
|
63
|
+
def allowed_genders
|
64
|
+
[:gelding, :mare, :stallion]
|
65
|
+
end
|
66
|
+
|
67
|
+
class PositiveWeight
|
68
|
+
def include?(weight)
|
69
|
+
weight >= 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Everything
|
74
|
+
def include?(_)
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/nemah/need.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Nemah
|
2
|
+
class Need
|
3
|
+
attr_reader :horse
|
4
|
+
|
5
|
+
def initialize(horse)
|
6
|
+
@horse = horse
|
7
|
+
end
|
8
|
+
|
9
|
+
def calcium
|
10
|
+
Nemah::SpecificNeed::Calcium.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def energy
|
14
|
+
Nemah::SpecificNeed::Energy.new(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def magnesium
|
18
|
+
Nemah::SpecificNeed::Magnesium.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def phosphor
|
22
|
+
Nemah::SpecificNeed::Phosphor.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def protein
|
26
|
+
Nemah::SpecificNeed::Protein.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def salt
|
30
|
+
Nemah::SpecificNeed::Salt.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def selenium
|
34
|
+
Nemah::SpecificNeed::Selenium.new(self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def solids
|
38
|
+
Nemah::SpecificNeed::Solids.new(self)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Nemah
|
2
|
+
module SpecificNeed
|
3
|
+
class AbstractNeed
|
4
|
+
attr_reader :need
|
5
|
+
|
6
|
+
def initialize(need)
|
7
|
+
@need = need
|
8
|
+
end
|
9
|
+
|
10
|
+
def ideal(decimals: 2)
|
11
|
+
_ideal.round(decimals)
|
12
|
+
end
|
13
|
+
|
14
|
+
def min(decimals: 2)
|
15
|
+
_min.round(decimals)
|
16
|
+
end
|
17
|
+
|
18
|
+
def max(decimals: 2)
|
19
|
+
_max.round(decimals)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_rounded_range(decimals: 2)
|
23
|
+
min(decimals: decimals)..max(decimals: decimals)
|
24
|
+
end
|
25
|
+
|
26
|
+
def unit
|
27
|
+
:g
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def _ideal
|
33
|
+
raise NotImplementedError, '#_ideal() must return an object responding to #round(n)'
|
34
|
+
end
|
35
|
+
|
36
|
+
def _min
|
37
|
+
raise NotImplementedError, '#min() must return an object responding to #round(n)'
|
38
|
+
end
|
39
|
+
|
40
|
+
def _max
|
41
|
+
raise NotImplementedError, '#max() must return an object responding to #round(n)'
|
42
|
+
end
|
43
|
+
|
44
|
+
def horse
|
45
|
+
need.horse
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Nemah
|
2
|
+
module SpecificNeed
|
3
|
+
class Calcium < AbstractNeed
|
4
|
+
include Nemah::SpecificNeed::MineralBehaviour
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def factor_table
|
9
|
+
FactorTable.new(no_work: 4.0, below_30: 6.0, below_50: 7.0, below_75: 8.0, above_75: 8.0)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Nemah
|
2
|
+
module SpecificNeed
|
3
|
+
class Energy < AbstractNeed
|
4
|
+
def for_maintenance(decimals: 2)
|
5
|
+
(ideal - workload_energy).round(decimals)
|
6
|
+
end
|
7
|
+
|
8
|
+
def for_workload(decimals: 2)
|
9
|
+
workload_energy.round(decimals)
|
10
|
+
end
|
11
|
+
|
12
|
+
def unit
|
13
|
+
:MJ
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def _ideal
|
19
|
+
0.50 * (horse.weight ** 0.75 ) * feedability_factor * gender_factor + workload_energy
|
20
|
+
end
|
21
|
+
|
22
|
+
def feedability_factor
|
23
|
+
case horse.feedability
|
24
|
+
when :easy then 1.00
|
25
|
+
when :normal then 1.05
|
26
|
+
when :hard then 1.10
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def gender_factor
|
31
|
+
(horse.stallion?) ? 1.10 : 1.00
|
32
|
+
end
|
33
|
+
|
34
|
+
def workload_energy
|
35
|
+
energy_addition_for_walk + energy_addition_for_trot_and_canter
|
36
|
+
end
|
37
|
+
|
38
|
+
def energy_addition_for_walk
|
39
|
+
(0.20 * horse.weight_in_deciton * workload.walk / 10.0) * days_per_week_factor
|
40
|
+
end
|
41
|
+
|
42
|
+
def energy_addition_for_trot_and_canter
|
43
|
+
(1.30 * horse.weight_in_deciton * workload.trot_and_canter / 10.0) * days_per_week_factor
|
44
|
+
end
|
45
|
+
|
46
|
+
def days_per_week_factor
|
47
|
+
workload.days_per_week / 7.0
|
48
|
+
end
|
49
|
+
|
50
|
+
def _min
|
51
|
+
[_ideal - 3, 0].max
|
52
|
+
end
|
53
|
+
|
54
|
+
def _max
|
55
|
+
_ideal + 3
|
56
|
+
end
|
57
|
+
|
58
|
+
def workload
|
59
|
+
horse.workload
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Nemah
|
2
|
+
module SpecificNeed
|
3
|
+
class Magnesium < AbstractNeed
|
4
|
+
include Nemah::SpecificNeed::MineralBehaviour
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def factor_table
|
9
|
+
FactorTable.new(no_work: 1.5, below_30: 1.9, below_50: 2.3, below_75: 3.0, above_75: 3.0)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Nemah
|
2
|
+
module SpecificNeed
|
3
|
+
module MineralBehaviour
|
4
|
+
def max(decimals: 2)
|
5
|
+
_max
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def _ideal
|
11
|
+
_min
|
12
|
+
end
|
13
|
+
|
14
|
+
def _min
|
15
|
+
horse.weight_in_deciton * energy_and_workload_factor
|
16
|
+
end
|
17
|
+
|
18
|
+
def _max
|
19
|
+
Float::INFINITY
|
20
|
+
end
|
21
|
+
|
22
|
+
def energy_and_workload_factor
|
23
|
+
EnergyWorkloadRatio.new(energy.for_workload, energy.for_maintenance, factor_table).factor
|
24
|
+
end
|
25
|
+
|
26
|
+
def energy
|
27
|
+
need.energy
|
28
|
+
end
|
29
|
+
|
30
|
+
def factor_table
|
31
|
+
raise NotImplementedError, 'factor_table must return an instance of FactorTable'
|
32
|
+
end
|
33
|
+
|
34
|
+
class FactorTable
|
35
|
+
attr_reader :no_work, :below_30, :below_50, :below_75, :above_75
|
36
|
+
|
37
|
+
def initialize(factors)
|
38
|
+
@no_work = factors.fetch(:no_work)
|
39
|
+
@below_30 = factors.fetch(:below_30)
|
40
|
+
@below_50 = factors.fetch(:below_50)
|
41
|
+
@below_75 = factors.fetch(:below_75)
|
42
|
+
@above_75 = factors.fetch(:above_75)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class EnergyWorkloadRatio
|
47
|
+
def initialize(workload_energy, maintenance_energy, factor_table)
|
48
|
+
@workload_energy = workload_energy
|
49
|
+
@maintenance_energy = maintenance_energy
|
50
|
+
@factor_table = factor_table
|
51
|
+
end
|
52
|
+
|
53
|
+
def factor
|
54
|
+
if workload_energy_quota == 0
|
55
|
+
factor_table.no_work
|
56
|
+
elsif workload_energy_quota < 0.30
|
57
|
+
factor_table.below_30
|
58
|
+
elsif workload_energy_quota < 0.50
|
59
|
+
factor_table.below_50
|
60
|
+
elsif workload_energy_quota < 0.75
|
61
|
+
factor_table.below_75
|
62
|
+
else
|
63
|
+
factor_table.above_75
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :workload_energy, :maintenance_energy, :factor_table
|
70
|
+
|
71
|
+
def workload_energy_quota
|
72
|
+
workload_energy.to_f / maintenance_energy.to_f
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|