Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
class Administration::ReportsController < Administration::HomeController # Rather than doing Document.find(params[:document_id]) in all the actions that need # to, just use a before filter. before_filter :find_document def new @report = @document.reports.build @report.reasons.build end def edit @report = @document.reports.find(params[:id]) end def create @report = @document.reports.build(params[:report]) if @report.save flash[:notice] = "Saved" redirect_to administration_document_report_url(@document, @report) else render :action => "new" end end def update @report = @document.reports.find(params[:id]) if @report.update_attributes(params[:report]) flash[:notice] = "Saved" redirect_to administration_document_report_url(@document, @report) else render :action => "new" end end private def find_document @document = Document.find(params[:document_id]) end end
Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
class Report < ActiveRecord::Base belongs_to :document, :counter_cache => true has_many :report_reasons has_many :reasons, :through => :report_reasons #, :accessible => true validates_associated :reasons attr_accessor :new_reason_attributes attr_accessor :existing_reason_attributes after_save :build_reasons protected def build_reasons reasons.clear unless new_reason_attributes.blank? new_reason_attributes.each do |new_reason| reasons << Reason.find_or_create_by_content(new_reason["content"]) end end unless existing_reason_attributes.blank? existing_reason_attributes.each do |existing_reason| reasons << Reason.find_or_create_by_content(existing_reason[1]["content"]) end end end end
View reports/new.html.erb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<% form_for [:administration, @document, @report] do |f| -%> <dl class="form"> <dt class="required"><%= t :when %> <%= f.error_message_on :when %> <span>(...)</span></dt> <dd><%= f.date_select :when, :include_blank => false, :start_year => Time.now.year - 20, :end_year => Time.now.year %></dd> <div id="reasons"> <%= render :partial => 'reason', :collection => @report.reasons %> </div> <p><%= add_reason_link "Add a reason" %></p> </dl> <%= f.submit %> <% end -%>
View reports/_reason.html.erb
1 2 3 4 5 6 7 8
<div class="reason"> <% new_or_existing = reason.new_record? ? 'new' : 'existing' %> <% prefix = "report[#{new_or_existing}_reason_attributes][]" %> <% fields_for prefix, reason do |reason_form| -%> <dt class="required">Reason <%= reason_form.error_message_on :content %></dt> <dd><%= reason_form.text_field :content %> <%= link_to_function "remove", "$(this).up('.reason').remove()" %></dd> <% end -%> </div>
Refactorings
No refactoring yet !
This is an attempt to make "complex forms" with has_many :through.
However I'm sure that there must be something better, this one feels dirty; also it doesn't trap exceptions for invalid records in the view.
There are better ways?