summaryrefslogtreecommitdiff
path: root/app/models/concerns/nutrient_vector.rb
blob: ba4f28a978160e3e30d10990b6e96c1b5127848e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
module NutrientVector
  extend ActiveSupport::Concern

  NUTRIENT_KEYS = %i[
    nno3 p k ca mg s na cl si fe zn b mn cu mo nnh4
  ].freeze

  included do
    # Define simple readers (nno3, p, k, ...) that read from #nutrient_values
    NUTRIENT_KEYS.each do |k|
      define_method(k) { nutrient_values[k] }
    end
  end

  # Hash-like access
  def [](key)
    key = key.to_sym
    return nil unless NUTRIENT_KEYS.include?(key)
    public_send(key)
  end

  def keys = NUTRIENT_KEYS

  # Returns a copy to avoid accidental mutation
  def to_h
    NUTRIENT_KEYS.index_with { |k| public_send(k) }
  end

  # Iterate over pairs
  def each_pair
    return enum_for(:each_pair) unless block_given?
    NUTRIENT_KEYS.each { |k| yield k, public_send(k) }
  end

  # Simple difference (self - other), useful for “how far from target?”
  def delta_against(other)
    NUTRIENT_KEYS.index_with { |k| (public_send(k).to_f) - (other.public_send(k).to_f) }
  end

  # Percent difference relative to other (e.g., measurement vs target)
  # Returns 0 when both are 0, and nil when target is 0 but measurement isn’t.
  def percent_diff_against(other)
    NUTRIENT_KEYS.index_with do |k|
      a = public_send(k).to_f
      b = other.public_send(k).to_f
      if b.zero? && a.zero?
        0.0
      elsif b.zero?
        nil
      else
        ((a - b) / b) * 100.0
      end
    end
  end
end
Copyright 2019--2026 Marius PETER