View raw

1 module NutrientVector 2 extend ActiveSupport::Concern 3 4 NUTRIENT_KEYS = %i[ 5 nno3 p k ca mg s na cl si fe zn b mn cu mo nnh4 6 ].freeze 7 8 included do 9 # Define simple readers (nno3, p, k, ...) that read from #nutrient_values 10 NUTRIENT_KEYS.each do |k| 11 define_method(k) { nutrient_values[k] } 12 end 13 end 14 15 # Hash-like access 16 def [](key) 17 key = key.to_sym 18 return nil unless NUTRIENT_KEYS.include?(key) 19 public_send(key) 20 end 21 22 def keys = NUTRIENT_KEYS 23 24 # Returns a copy to avoid accidental mutation 25 def to_h 26 NUTRIENT_KEYS.index_with { |k| public_send(k) } 27 end 28 29 # Iterate over pairs 30 def each_pair 31 return enum_for(:each_pair) unless block_given? 32 NUTRIENT_KEYS.each { |k| yield k, public_send(k) } 33 end 34 35 # Simple difference (self - other), useful for “how far from target?” 36 def delta_against(other) 37 NUTRIENT_KEYS.index_with { |k| (public_send(k).to_f) - (other.public_send(k).to_f) } 38 end 39 40 # Percent difference relative to other (e.g., measurement vs target) 41 # Returns 0 when both are 0, and nil when target is 0 but measurement isn’t. 42 def percent_diff_against(other) 43 NUTRIENT_KEYS.index_with do |k| 44 a = public_send(k).to_f 45 b = other.public_send(k).to_f 46 if b.zero? && a.zero? 47 0.0 48 elsif b.zero? 49 nil 50 else 51 ((a - b) / b) * 100.0 52 end 53 end 54 end 55 end 56