<!-- projects/edit.rhtml -->
<% form_for :project, :url => project_path(@project), :html => { :method => 'put' } do |f| %>
<%= render :partial => 'fields', :locals => { :f => f } %>
<p><%= submit_tag "Update Project" %></p>
<% end %>

<!-- projects/new.rhtml -->
<% form_for :project, :url => projects_path do |f| %>
<%= render :partial => 'fields', :locals => { :f => f } %>
<p><%= submit_tag "Create Project" %></p>
<% end %>

<!-- projects/_fields.rhtml -->
<p>
Name: <%= f.text_field :name %>
</p>
<div id="tasks">
<%= render :partial => 'task', :collection => @project.tasks %>
</div>
<%= add_task_link "Add a task" %>

<!-- projects/_task.rhtml -->
<div class="task">
<% fields_for "project[task_attributes][]", task do |f| %>
<p>
Task: <%= f.text_field :name, :index => nil %>
<% if task.new_record? %>
<%= link_to_function "remove", "$(this).up('.task').remove()" %>
<% else %>
<%= link_to_function "remove", "mark_for_destroy(this)" %>
<%= f.hidden_field :id, :index => nil %>
<%= f.hidden_field :should_destroy, :index => nil, :class => 'should_destroy' %>
<% end %>
</p>
<% end %>
</div>


# models/project.rb
class Project < ActiveRecord::Base
has_many :tasks
after_update :save_tasks
validates_associated :tasks

def task_attributes=(task_attributes)
task_attributes.each do |attributes|
if attributes[:id].blank?
tasks.build(attributes)
else
task = tasks.detect { |t| t.id == attributes[:id].to_i }
task.attributes = attributes
end
end
end

def save_tasks
tasks.each do |t|
if t.should_destroy?
t.destroy
else
t.save(false)
end
end
end
end

# models/task.rb
class Task < ActiveRecord::Base
belongs_to :project
attr_accessor :should_destroy

def should_destroy?
should_destroy.to_i == 1
end
end

# projects_controller.rb
def edit
@project = Project.find(params[:id])
end

def update
@project = Project.find(params[:id])
if @project.update_attributes(params[:project])
flash[:notice] = "Successfully updated project."
redirect_to projects_path
else
render :action => 'edit'
end
end


/* application.js */
function mark_for_destroy(element) {
$(element).next('.should_destroy').value = 1
$(element).up('.task').hide();
}