rai
This commit is contained in:
parent
a2f2a05679
commit
021c066cf4
25
app/models/ingredient_proxy.rb
Normal file
25
app/models/ingredient_proxy.rb
Normal 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
|
@ -69,6 +69,14 @@ class Recipe < ApplicationRecord
|
|||||||
self
|
self
|
||||||
end
|
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
|
def tag_names
|
||||||
self.tags.map { |t| t.name }
|
self.tags.map { |t| t.name }
|
||||||
end
|
end
|
||||||
@ -90,13 +98,6 @@ class Recipe < ApplicationRecord
|
|||||||
@nutrition_data
|
@nutrition_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def parsed_yield
|
|
||||||
if @parsed_yield.nil? && self.yields.present?
|
|
||||||
@parsed_yield = YieldParser.parse(self.yields)
|
|
||||||
end
|
|
||||||
@parsed_yield
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_rating!
|
def update_rating!
|
||||||
self.rating = Log.for_recipe(self).for_user(self.user_id).where('rating IS NOT NULL').average(:rating)
|
self.rating = Log.for_recipe(self).for_user(self.user_id).where('rating IS NOT NULL').average(:rating)
|
||||||
save(validate: false)
|
save(validate: false)
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
class RecipeIngredient < ApplicationRecord
|
class RecipeIngredient < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :ingredient, optional: true
|
belongs_to :ingredient, optional: true
|
||||||
|
belongs_to :recipe_as_ingredient, class_name: 'Recipe', optional: true
|
||||||
belongs_to :recipe, inverse_of: :recipe_ingredients, touch: true
|
belongs_to :recipe, inverse_of: :recipe_ingredients, touch: true
|
||||||
|
|
||||||
validates :sort_order, presence: true
|
validates :sort_order, presence: true
|
||||||
|
|
||||||
def name
|
def name
|
||||||
if self.ingredient_id.present?
|
if self.ingredient_detail.present?
|
||||||
self.ingredient.name
|
self.ingredient_detail.name
|
||||||
else
|
else
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
@ -19,6 +20,45 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
str
|
str
|
||||||
end
|
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)
|
def scale(factor, auto_unit = false)
|
||||||
if factor.present? && self.quantity.present? && factor != '1'
|
if factor.present? && self.quantity.present? && factor != '1'
|
||||||
|
|
||||||
@ -54,7 +94,7 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
|
|
||||||
def to_volume
|
def to_volume
|
||||||
return unless self.quantity.present?
|
return unless self.quantity.present?
|
||||||
if ingredient && ingredient.density
|
if ingredient_detail && ingredient_detail.density?
|
||||||
density = UnitConversion.parse(ingredient.density)
|
density = UnitConversion.parse(ingredient.density)
|
||||||
if density.density?
|
if density.density?
|
||||||
value_unit = UnitConversion.parse(self.quantity, self.units)
|
value_unit = UnitConversion.parse(self.quantity, self.units)
|
||||||
@ -67,9 +107,9 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def to_mass
|
def to_mass
|
||||||
if ingredient
|
if ingredient_detail
|
||||||
value_unit = as_value_unit
|
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
|
case
|
||||||
when value_unit.nil?
|
when value_unit.nil?
|
||||||
@ -85,17 +125,17 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def custom_unit?
|
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
|
end
|
||||||
|
|
||||||
def can_convert_to_grams?
|
def can_convert_to_grams?
|
||||||
vu = as_value_unit
|
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
|
end
|
||||||
|
|
||||||
def to_grams
|
def to_grams
|
||||||
value_unit = as_value_unit
|
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
|
gram_unit.raw_value
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -104,7 +144,7 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
when self.quantity.blank?
|
when self.quantity.blank?
|
||||||
nil
|
nil
|
||||||
when self.custom_unit?
|
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?
|
when self.units.present?
|
||||||
UnitConversion.parse(self.quantity, self.units)
|
UnitConversion.parse(self.quantity, self.units)
|
||||||
else
|
else
|
||||||
@ -115,6 +155,7 @@ class RecipeIngredient < ApplicationRecord
|
|||||||
def log_copy
|
def log_copy
|
||||||
copy = RecipeIngredient.new
|
copy = RecipeIngredient.new
|
||||||
copy.ingredient = self.ingredient
|
copy.ingredient = self.ingredient
|
||||||
|
copy.recipe_as_ingredient = self.recipe_as_ingredient
|
||||||
copy.name = self.name
|
copy.name = self.name
|
||||||
copy.sort_order = self.sort_order
|
copy.sort_order = self.sort_order
|
||||||
copy.quantity = self.quantity
|
copy.quantity = self.quantity
|
||||||
|
71
app/models/recipe_proxy.rb
Normal file
71
app/models/recipe_proxy.rb
Normal 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
|
@ -7,13 +7,15 @@ json.rendered_steps MarkdownProcessor.render(recipe.step_text)
|
|||||||
json.tags recipe.tag_names
|
json.tags recipe.tag_names
|
||||||
|
|
||||||
json.ingredients recipe.recipe_ingredients do |ri|
|
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
|
json.ingredient_detail do
|
||||||
if ri.ingredient.nil?
|
if ri.ingredient.nil? && ri.ingredient_as_recipe.nil?
|
||||||
json.null!
|
json.null!
|
||||||
|
elsif ri.ingredient
|
||||||
|
json.extract! ri.ingredient, :name, :density, :notes
|
||||||
else
|
else
|
||||||
json.extract! ri.ingredient, :id, :name, :density, :notes
|
json.extract! ri.recipe_as_ingredient, :name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
5
db/migrate/20180823174505_add_recipe_as_ingredient_id.rb
Normal file
5
db/migrate/20180823174505_add_recipe_as_ingredient_id.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddRecipeAsIngredientId < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :recipe_ingredients, :recipe_as_ingredient_id, :integer
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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|
|
create_table "ingredient_units", force: :cascade do |t|
|
||||||
t.integer "ingredient_id", null: false
|
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 "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.text "preparation"
|
t.text "preparation"
|
||||||
|
t.integer "recipe_as_ingredient_id"
|
||||||
t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id"
|
t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,45 +24,58 @@ RSpec.describe RecipeIngredient, type: :model do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#can_convert_to_grams' do
|
describe '#can_convert_to_grams' do
|
||||||
it 'returns false if no quantity or unit' do
|
describe 'with no ingredient detail' do
|
||||||
ri = RecipeIngredient.new
|
it 'returns false if no quantity or unit' do
|
||||||
expect(ri.can_convert_to_grams?).to be_falsey
|
ri = RecipeIngredient.new
|
||||||
|
expect(ri.can_convert_to_grams?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if no quantity' do
|
||||||
|
ri = RecipeIngredient.new(units: 'lbs')
|
||||||
|
expect(ri.can_convert_to_grams?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if no units' do
|
||||||
|
ri = RecipeIngredient.new(quantity: '5 1/2')
|
||||||
|
expect(ri.can_convert_to_grams?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if weird units' do
|
||||||
|
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'dogs')
|
||||||
|
expect(ri.can_convert_to_grams?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true if unit is mass' do
|
||||||
|
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'lbs')
|
||||||
|
expect(ri.can_convert_to_grams?).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
it 'returns false if no quantity' do
|
describe 'with ingredient' do
|
||||||
ri = RecipeIngredient.new(units: 'lbs')
|
it 'returns false if unit is volume and ingredient has no density' do
|
||||||
expect(ri.can_convert_to_grams?).to be_falsey
|
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups', ingredient: create(:ingredient))
|
||||||
|
expect(ri.can_convert_to_grams?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
it 'returns false if no units' do
|
describe 'with recipe_as_ingredient' do
|
||||||
ri = RecipeIngredient.new(quantity: '5 1/2')
|
it 'return true if unit is volume and recipe has density' do
|
||||||
expect(ri.can_convert_to_grams?).to be_falsey
|
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
|
||||||
|
|
||||||
it 'returns false if weird units' do
|
|
||||||
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'dogs')
|
|
||||||
expect(ri.can_convert_to_grams?).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns true if unit is mass' do
|
|
||||||
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'lbs')
|
|
||||||
expect(ri.can_convert_to_grams?).to be_truthy
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false if unit is volume and there is no ingredient' do
|
|
||||||
ri = RecipeIngredient.new(quantity: '5 1/2', units: 'cups')
|
|
||||||
expect(ri.can_convert_to_grams?).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user