views:

106

answers:

3

I have a rails model that looks something like this:

class Recipe < ActiveRecord::Base
   has_many :ingredients
   attr_accessor :ingredients_string
   attr_accessible :title, :directions, :ingredients, :ingredients_string

   before_save :set_ingredients

   def ingredients_string
      ingredients.join("\n")
   end

   private

   def set_ingredients
      self.ingredients.each { |x| x.destroy }
      self.ingredients_string ||= false
      if self.ingredients_string
         self.ingredients_string.split("\n").each do |x|
            ingredient = Ingredient.create(:ingredient_string => x)
            self.ingredients << ingredient
         end
      end
   end
end

The idea is that when I create the ingredient from the webpage, I pass in the ingredients_string and let the model sort it all out. Of course, if I am editing an ingredient I need to re-create that string. The bug is basically this: how do I inform the view of the ingredient_string (elegantly) and still check to see if the ingredient_string is defined in the set_ingredients method?

A: 

Using these two together are probably causing your issues. Both are trying to define an ingredients_string method that do different things

   attr_accessor :ingredients_string

   def ingredients_string
      ingredients.join("\n")
   end

Get rid of the attr_accessor, the before_save, set_ingredients method and define your own ingredients_string= method, something like this:

def ingredients_string=(ingredients)
    ingredients.each { |x| x.destroy }
    ingredients_string ||= false
    if ingredients_string
        ingredients_string.split("\n").each do |x|
            ingredient = Ingredient.create(:ingredient_string => x)
            self.ingredients << ingredient
        end
    end
end

Note I just borrowed your implementation of set_ingredients. There's probably a more elegant way to break up that string and create/delete Ingredient model associations as needed, but it's late and I can't think of it right now. :)

Otto
A: 

The previous answer is very good but it could do with a few changes.

def ingredients_string=(text) ingredients.each { |x| x.destroy } unless text.blank? text.split("\n").each do |x| ingredient = Ingredient.find_or_create_by_ingredient_string(:ingredient_string => x) self.ingredients
fatgeekuk
Well, this isn't actually a find_or_create_by situation. Ingredients are unique. IngredientTypes, otoh, are not unique, and they are set up with a find_or_create_by method. Thanks though!
Frew
A: 

I basically just modified Otto's answer:

class Recipe < ActiveRecord::Base
   has_many :ingredients
   attr_accessible :title, :directions, :ingredients, :ingredients_string

   def ingredients_string=(ingredient_string)
      ingredient_string ||= false
      if ingredient_string
         self.ingredients.each { |x| x.destroy }
         unless ingredient_string.blank?
            ingredient_string.split("\n").each do |x|
               ingredient = Ingredient.create(:ingredient_string => x)
               self.ingredients << ingredient
            end
         end
      end
   end

   def ingredients_string
      ingredients.join("\n")
   end

end
Frew