class RecipeIngredient < ApplicationRecord belongs_to :food, optional: true belongs_to :recipe_as_ingredient, class_name: 'Recipe', optional: true belongs_to :recipe, inverse_of: :recipe_ingredients, touch: true validates :sort_order, presence: true def name if self.ingredient.present? self.ingredient.name else super end end def display_name str = [quantity, units, name].delete_if { |i| i.blank? }.join(' ') str << ", #{preparation}" if preparation.present? str end def ingredient_id case when recipe_as_ingredient_id "R#{recipe_as_ingredient_id}" when food_id "F#{food_id}" else nil end end def ingredient_id=(val) return val if self.ingredient_id == val @ingredient = nil case val when -> (v) { v.blank? } self.recipe_as_ingredient_id = nil self.food_id = nil when /^R(\d+)$/ self.food_id = nil self.recipe_as_ingredient_id = $1.to_i when /^F(\d+)$/ self.recipe_as_ingredient_id = nil self.food_id = $1.to_i else raise "Invalid ingredient_id: #{val}" end end def ingredient @ingredient ||= case when self.recipe_as_ingredient_id self.recipe_as_ingredient when self.food_id self.food else nil end end def scale(factor, auto_unit = false) if factor.present? && self.quantity.present? && factor != '1' value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.scale(factor) if auto_unit value_unit = value_unit.auto_unit end self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end end def to_metric return unless self.quantity.present? value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.to_metric self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end def to_standard return unless self.quantity.present? value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.to_standard self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end def to_volume return unless self.quantity.present? if ingredient && ingredient.density? density = UnitConversion.parse(ingredient.density) if density.density? value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.to_volume(density) self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end end end def to_mass return unless self.quantity.present? if ingredient && ingredient.density? density = UnitConversion.parse(ingredient.density) if density.density? value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.to_mass(density) self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end end end def get_custom_unit_equivalent if self.ingredient unit = self.units.present? ? self.units.downcase : '' pair = self.ingredient.custom_units.detect do |u, e| if unit.empty? ['each', 'ech', 'item', 'per', 'recipe'].include?(u.downcase) else [u.downcase, u.downcase.singularize, u.downcase.pluralize].any? { |uv| [unit, unit.singularize, unit.pluralize].include?(uv) } end end pair ? pair[1] : nil else nil end end def can_convert_to_grams? vu = as_value_unit vu.present? && (vu.mass? || (vu.volume? && self.ingredient && self.ingredient.density?)) end def to_grams value_unit = as_value_unit gram_unit = value_unit.convert('g', self.ingredient ? self.ingredient.density : nil) gram_unit.raw_value end # Based on current quantity and units, return the value with with to multiply each nutrient to get the total amount # supplied by this ingredient def calculate_nutrition_ratio end def as_value_unit custom_unit = self.get_custom_unit_equivalent case when self.quantity.blank? nil when custom_unit.present? vu = UnitConversion.parse(custom_unit) vu.scale(self.quantity) when self.units.present? UnitConversion.parse(self.quantity, self.units) else nil end end def log_copy copy = RecipeIngredient.new copy.food = self.food copy.recipe_as_ingredient = self.recipe_as_ingredient copy.name = self.name copy.sort_order = self.sort_order copy.quantity = self.quantity copy.units = self.units copy.preparation = self.preparation copy end end