views:

147

answers:

2

I have a piece of code here that i really could use some help with refactoring. I need the different methods for adding relational data in a form in rails. The code is taken from http://railscasts.com/episodes/75-complex-forms-part-3, my problem is that i need to have the methods fro both the Material model and the Answer model. So i need the exact same code twice with "materials" replaced by "answers".

It seems this should be solved with some dynamic programming? But I have no experience at all with that.

How is this solved?

after_update :save_materials
after_update :save_answers  

def new_material_attributes=(material_attributes)
  material_attributes.each do |attributes|
    materials.build(attributes)
  end
end

def existing_material_attributes=(material_attributes)
  materials.reject(&:new_record?).each do |material|
    attributes = material_attributes[material.id.to_s]
    if attributes
      material.attributes = attributes
    else
      materials.delete(material)
    end
  end
end

def save_materials
  materials.each do |material|
    material.save(false)
  end
end
+5  A: 

You might also want to take a look at this site:

http://refactormycode.com/

srboisvert
+1  A: 

If I understood you correctly, you want to have the same methods for answers as for materials, but duplicating the least code. The way to do this is by abstracting some private methods that will work for either answers or materials and call them with the appropriate model from the methods specific to those models. I've given a sample below. Note that I didn't do anything with the save_ methods because I felt they were short enough that abstracting them really didn't save much.

after_update :save_materials
after_update :save_answers  

// Public methods

def new_material_attributes=(material_attributes)
  self.new_with_attributes(materials, material_attributes)
end

def new_answer_attributes=(answer_attributes)
  self.new_with_attributes(answers, answer_attributes)
end

def existing_material_attributes=(material_attributes)
  self.existing_with_attributes(materials, material_attributes)
end

def existing_answer_attributes=(answer_attributes)
  self.existing_with_attributes(answers, answer_attributes)
end

def save_materials
  materials.each do |material|
    material.save(false)
  end
end

def save_answers
  answers.each do |answer|
     answer.save(false)
  end
end

// Private methods    

private
def new_with_atttributes(thing,attributes)
    attributes.each do |attribute|
       thing.build(attribute)
    end
end

def existing_with_attributes=(things, attributes)
  things.reject(&:new_record?).each do |thing|
    attrs = attributes[thing.id.to_s]
    if attrs
      thing.attributes = attrs
    else
      things.delete(thing)
    end
  end
end
tvanfosson