This commit is contained in:
Dan Elbert 2018-08-27 16:44:45 -05:00
parent a2f2a05679
commit 021c066cf4
8 changed files with 212 additions and 53 deletions

View File

@ -0,0 +1,25 @@
class IngredientProxy
attr_reader :ingredient
def initialize(ingredient)
@ingredient = ingredient
end
def name
@ingredient.name
end
def density
@ingredient.density
end
def density?
@ingredient.density.present?
end
def get_custom_unit_equivalent(custom_unit_name)
@ingredient.custom_unit_weight(custom_unit_name)
end
end

View File

@ -69,6 +69,14 @@ class Recipe < ApplicationRecord
self
end
def yields_list
if self.yields.present?
self.yields.split(',').map { |y| y.strip }.select { |y| y.present? }
else
[]
end
end
def tag_names
self.tags.map { |t| t.name }
end
@ -90,13 +98,6 @@ class Recipe < ApplicationRecord
@nutrition_data
end
def parsed_yield
if @parsed_yield.nil? && self.yields.present?
@parsed_yield = YieldParser.parse(self.yields)
end
@parsed_yield
end
def update_rating!
self.rating = Log.for_recipe(self).for_user(self.user_id).where('rating IS NOT NULL').average(:rating)
save(validate: false)

View File

@ -1,13 +1,14 @@
class RecipeIngredient < ApplicationRecord
belongs_to :ingredient, 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_id.present?
self.ingredient.name
if self.ingredient_detail.present?
self.ingredient_detail.name
else
super
end
@ -19,6 +20,45 @@ class RecipeIngredient < ApplicationRecord
str
end
def ingredient_detail_id
case
when recipe_as_ingredient_id
"R#{recipe_as_ingredient_id}"
when ingredient_id
"I#{ingredient_id}"
else
nil
end
end
def ingredient_detail_id=(val)
@recipe_detail = nil
case val
when -> (v) { v.blank? }
self.recipe_as_ingredient_id = nil
self.ingredient_id = nil
when /^R(\d+)$/
self.ingredient_id = nil
self.recipe_as_ingredient_id = $1.to_i
when /^I(\d+)$/
self.recipe_as_ingredient_id = nil
self.ingredient_id = $1.to_i
else
raise "Invalid ingredient_detail_id: #{val}"
end
end
def ingredient_detail
@recipe_detail ||= case
when self.recipe_as_ingredient_id
RecipeProxy.new(self.recipe_as_ingredient)
when self.ingredient_id
IngredientProxy.new(self.ingredient)
else
nil
end
end
def scale(factor, auto_unit = false)
if factor.present? && self.quantity.present? && factor != '1'
@ -54,7 +94,7 @@ class RecipeIngredient < ApplicationRecord
def to_volume
return unless self.quantity.present?
if ingredient && ingredient.density
if ingredient_detail && ingredient_detail.density?
density = UnitConversion.parse(ingredient.density)
if density.density?
value_unit = UnitConversion.parse(self.quantity, self.units)
@ -67,9 +107,9 @@ class RecipeIngredient < ApplicationRecord
end
def to_mass
if ingredient
if ingredient_detail
value_unit = as_value_unit
density = self.ingredient.density? ? UnitConversion.parse(ingredient.density) : nil
density = self.ingredient_detail.density? ? UnitConversion.parse(ingredient_detail.density) : nil
case
when value_unit.nil?
@ -85,17 +125,17 @@ class RecipeIngredient < ApplicationRecord
end
def custom_unit?
self.ingredient && self.ingredient.custom_unit_weight(self.units).present?
self.ingredient_detail && self.ingredient_detail.get_custom_unit_equivalent(self.units).present?
end
def can_convert_to_grams?
vu = as_value_unit
vu.present? && (vu.mass? || (vu.volume? && self.ingredient && self.ingredient.density.present?))
vu.present? && (vu.mass? || (vu.volume? && self.ingredient_detail && self.ingredient_detail.density.present?))
end
def to_grams
value_unit = as_value_unit
gram_unit = value_unit.convert('g', self.ingredient ? self.ingredient.density : nil)
gram_unit = value_unit.convert('g', self.ingredient_detail ? self.ingredient_detail.density : nil)
gram_unit.raw_value
end
@ -104,7 +144,7 @@ class RecipeIngredient < ApplicationRecord
when self.quantity.blank?
nil
when self.custom_unit?
self.ingredient.custom_unit_weight(self.units).scale(self.quantity)
self.ingredient_detail.get_custom_unit_equivalent(self.units).scale(self.quantity)
when self.units.present?
UnitConversion.parse(self.quantity, self.units)
else
@ -115,6 +155,7 @@ class RecipeIngredient < ApplicationRecord
def log_copy
copy = RecipeIngredient.new
copy.ingredient = self.ingredient
copy.recipe_as_ingredient = self.recipe_as_ingredient
copy.name = self.name
copy.sort_order = self.sort_order
copy.quantity = self.quantity

View File

@ -0,0 +1,71 @@
class RecipeProxy
attr_reader :recipe
def initialize(recipe)
@recipe = recipe
parse_yields
end
def name
@recipe.name
end
def density
@density
end
def density?
!self.density.nil?
end
def get_custom_unit_equivalent(custom_unit_name)
unit = @custom_yields.detect { |vu| vu.unit.unit == custom_unit_name }
known_unit = @unit_yields.first
if unit && known_unit
# ex:
# custom_unit_name: "rolls"
# yields: "3 rolls, 500 g"
# desired return: 166.6 g
ValueUnit.for(known_unit.value.value / unit.value.value, known_unit.unit)
end
end
private
def parse_yields
@custom_yields = []
@unit_yields = []
@density = nil
@recipe.yields_list.each do |y|
begin
vu = UnitConversion::parse(yield_string)
rescue UnparseableUnitError
vu = nil
end
if vu
if vu.unit.nil?
@custom_yields << ValueUnit.for(vu.value, 'servings')
elsif vu.mass? || vu.volume?
@unit_yields << vu
else
@custom_yields << vu
end
end
end
vol = @unit_yields.detect { |u| u.volume? }
mas = @unit_yields.detect { |u| u.mass? }
# ex
# vol: 2 cups
# mas: 7 oz
if vol && mas
@density = "#{mas.value.value / vol.value.value} #{mas.unit.original_unit}/#{vol.unit.original_unit}"
end
end
end

View File

@ -7,13 +7,15 @@ json.rendered_steps MarkdownProcessor.render(recipe.step_text)
json.tags recipe.tag_names
json.ingredients recipe.recipe_ingredients do |ri|
json.extract! ri, :id, :ingredient_id, :display_name, :name, :quantity, :units, :preparation, :sort_order
json.extract! ri, :id, :ingredient_detail_id, :display_name, :name, :quantity, :units, :preparation, :sort_order
json.ingredient do
if ri.ingredient.nil?
json.ingredient_detail do
if ri.ingredient.nil? && ri.ingredient_as_recipe.nil?
json.null!
elsif ri.ingredient
json.extract! ri.ingredient, :name, :density, :notes
else
json.extract! ri.ingredient, :id, :name, :density, :notes
json.extract! ri.recipe_as_ingredient, :name
end
end

View File

@ -0,0 +1,5 @@
class AddRecipeAsIngredientId < ActiveRecord::Migration[5.2]
def change
add_column :recipe_ingredients, :recipe_as_ingredient_id, :integer
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2017_04_14_233856) do
ActiveRecord::Schema.define(version: 2018_08_23_174505) do
create_table "ingredient_units", force: :cascade do |t|
t.integer "ingredient_id", null: false
@ -83,6 +83,7 @@ ActiveRecord::Schema.define(version: 2017_04_14_233856) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "preparation"
t.integer "recipe_as_ingredient_id"
t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id"
end

View File

@ -24,6 +24,7 @@ RSpec.describe RecipeIngredient, type: :model do
end
describe '#can_convert_to_grams' do
describe 'with no ingredient detail' do
it 'returns false if no quantity or unit' do
ri = RecipeIngredient.new
expect(ri.can_convert_to_grams?).to be_falsey
@ -49,20 +50,32 @@ RSpec.describe RecipeIngredient, type: :model do
expect(ri.can_convert_to_grams?).to be_truthy
end
it 'returns false if unit is volume and there is no ingredient' do
it 'returns false if unit is volume' do
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups')
expect(ri.can_convert_to_grams?).to be_falsey
end
end
describe 'with ingredient' do
it 'returns false if unit is volume and ingredient has no density' do
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups', ingredient: create(:ingredient))
expect(ri.can_convert_to_grams?).to be_falsey
end
it 'returns false if unit is volume and ingredient has density' do
it 'returns true if unit is volume and ingredient has density' do
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups', ingredient: create(:ingredient_with_density))
expect(ri.can_convert_to_grams?).to be_truthy
end
end
describe 'with recipe_as_ingredient' do
it 'return true if unit is volume and recipe has density' do
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups', recipe_as_ingredient: create(:recipe))
expect(ri.can_convert_to_grams?).to be_truthy
end
end
end
end