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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 59e1b71fdd45cf09b6e19ad900d4c06ec6147e6d
4
+ data.tar.gz: 52ed2eb7953a641a03c5bf17a0e739d99211b184
5
+ SHA512:
6
+ metadata.gz: d14843fe110208602459b8b29449fc4ab716c9f7d8619f883a57785dd65859f41d6545da30caaf024ddc7fb278f8526269454398e2b4030ecb5176dadcb6c319
7
+ data.tar.gz: 9ce324455ba1812396ebccfe661eb2c45bf44451874d0858b2adeb25d246b3ec7db13076f6aef1a9cbd0e7c990843664c7068bc063b7ed36178084c31cf170bd
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ /sandi_meter/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.1.0
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nemah.gemspec
4
+ gemspec
@@ -0,0 +1,6 @@
1
+ guard :rspec do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{spec/support/shared_examples_for_(.+)\.rb}) { 'spec' }
5
+ watch('spec/spec_helper.rb') { 'spec' }
6
+ end
@@ -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.
@@ -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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,2 @@
1
+ require 'nemah/specific_need/mineral_behaviour'
2
+ Dir[File.join(File.dirname(__FILE__), 'nemah', '**', '*.rb')].sort.each { |file| require file }
@@ -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
@@ -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