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).auto_unit 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? UnitConversion::with_custom_units(self.ingredient.custom_units) do density = UnitConversion.parse(ingredient.density) if density.density? value_unit = UnitConversion.parse(self.quantity, self.units) value_unit = value_unit.to_mass(density).auto_unit self.quantity = value_unit.pretty_value self.units = value_unit.unit.to_s end end end 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 if self.ingredient.blank? || self.quantity.blank? return nil end UnitConversion::with_custom_units(self.ingredient.custom_units) do unit_is_each = self.units.blank? || %w(each ech item items per recipe recipes).include?(self.units.downcase) unit = UnitConversion::parse(self.quantity, unit_is_each ? 'each' : self.units) nutrition_unit = self.ingredient.nutrition_unit converted_unit = unit.convert(nutrition_unit.unit.unit, self.ingredient.density) converted_unit.scale(1.0 / nutrition_unit.value.value).raw_value end rescue UnitConversion::UnparseableUnitError nil 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