I have a rails application that models a house. house contains rooms
and rooms have nested attributes for light and small_appliance. I have
a calculator controller, which is how end users will access the
application.

My problem is that I can't get the partial for adding rooms to submit
correctly from calculator. The initial page lets the user enter house
information, which is saved using save_house when submit is clicked.
This also redirects the user to the add_rooms page, where they can add
rooms to the house.

add_rooms displays correctly, but when I click submit, I get this
error:

-------------------------------

RuntimeError in Calculator#add_room

Showing app/views/calculator/add_rooms.html.erb where line #2 raised:

Called id for nil, which would mistakenly be 4 -- if you really wanted
the id of nil, use object_id

Extracted source (around line #2):

1: <div id="addRooms">
2:   <p>House id is <%= @house.id %></p>
3:
4:   <h3>Your rooms:</h3>
5:   <% if @house.rooms %>

RAILS_ROOT: C:/Users/ryan/Downloads/react
Application Trace | Framework Trace | Full Trace

C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb:
2:in `_run_erb_app47views47calculator47add_rooms46html46erb'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:
36:in `add_room'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:
33:in `add_room'

-------------------------------

This is odd to me, because when add_rooms first renders, it shows the
house_id. I don't understand why it isn't passed after the form is
submitted.

Here's the code:

-------------------------------

#app/models/room.rb

class Room < ActiveRecord::Base
  # schema { name:string, house_id:integer }
  belongs_to :house
  has_many :lights, :dependent => :destroy
  has_many :small_appliances, :dependent => :destroy
  validates_presence_of :name
  accepts_nested_attributes_for :lights, :reject_if => lambda { |a|
a.values.all?(&:blank?) }, :allow_destroy => true
  accepts_nested_attributes_for :small_appliances, :reject_if =>
lambda { |a| a.values.all?(&:blank?) }, :allow_destroy =>
true
end

-------------------------------

#app/models/house.rb

class House < ActiveRecord::Base
  has_many :rooms

  # validation code not included

  def add_room(room)
    rooms << room
  end

end

-------------------------------

#app/controllers/calculator_controller.rb

class CalculatorController < ApplicationController
  def index
  end

  def save_house
    @house = House.new(params[:house])
    respond_to do |format|
      if @house.save
        format.html { render :action => 'add_rooms', :id => @house }
        format.xml { render :xml => @house, :status
=> :created, :location => @house }
      else
        format.html { render :action => 'index' }
        format.xml  { render :xml => @house.errors, :status
=> :unprocessable_entity }
      end
    end
  end

  def add_rooms
    @house = House.find(params[:id])
    @rooms = Room.find_by_house_id(@house.id)

  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before adding rooms"
    redirect_to :action => 'index'
  end

  def add_room
    @room = Room.new(params[:room])
    @house = @room.house

    respond_to do |format|
      if @room.save
        flash[:notice] = "Room \"#[email protected]}\" was successfully
added."
        format.html { render :action => 'add_rooms' }
        format.xml { render :xml => @room, :status
=> :created, :location => @room }
      else
        format.html { render :action => 'add_rooms' }
        format.xml  { render :xml => @room.errors, :status
=> :unprocessable_entity }
      end
    end
  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before adding a room"
    redirect_to :action => 'index'
  end

  def report
    flash[:notice] = nil
    @house = House.find(params[:id])
    @rooms = Room.find_by_house_id(@house.id)
  rescue ActiveRecord::RecordNotFound
    logger.error("Attempt to access invalid house #{params[:id]}")
    flash[:notice] = "You must create a house before generating a
report"
    redirect_to :action => 'index'
  end

end

-------------------------------

#app/views/calculator/add_rooms.html.erb

<div id="addRooms">
  <p>House id is <%= @house.id %></p>

  <h3>Your rooms:</h3>
  <% if @house.rooms %>
  <ul>
    <% for room in @house.rooms %>
    <li>
      <%= h room.name %> has <%= h room.number_of_bulbs %>
      <%= h room.wattage_of_bulbs %> watt bulbs, in use for
      <%= h room.usage_hours %> hours per day.
    </li>
    <% end %>
  </ul>
  <% else %>
  <p>You have not added any rooms yet</p>
  <% end %>

  <%= render :partial => 'rooms/room_form' %>

  <br />
</div>

<%= button_to "Continue to report", :action => "report", :id => @house
%>

-------------------------------

#app/views/rooms/_room_form.html.erb

<% form_for :room, @house.rooms.build, :url => { :action
=> :add_room } do |form| %>
  <%= form.error_messages %>
  <p>
    <%= form.label :name %><br />
    <%= form.text_field :name %>
  </p>

  <h3>Lights</h3>
  <% form.object.lights.build if form.object.lights.empty? %>
  <% form.fields_for :lights do |light_form| %>
    <%= render :partial => "light", :locals => { :form => light_form }
%>
  <% end %>
  <p class="addLink"><%= add_child_link "[+] Add new light",
form, :lights %></p>

  <h3>Small Appliances</h3>
  <% form.object.small_appliances.build if
form.object.small_appliances.empty? %>
  <% form.fields_for :small_appliances do |sm_appl_form| %>
    <%= render :partial => "small_appliance", :locals => { :form =>
sm_appl_form } %>
  <% end %>
  <p class="addLink"><%= add_child_link "[+] Add new small appliance",
form, :small_appliances %></p>

  <p><%= form.submit "Submit" %></p>
<% end %>

-------------------------------

#application_helper.rb

module ApplicationHelper
  def remove_child_link(name, form)
    form.hidden_field(:_delete) + link_to_function(name, "remove_fields
(this)")
  end

  def add_child_link(name, form, method)
    fields = new_child_fields(form, method)
    link_to_function(name, h("insert_fields(this, \"#{method}\", \"#
{escape_javascript(fields)}\")"))
  end

  def new_child_fields(form_builder, method, options = {})
    options[:object] ||=
form_builder.object.class.reflect_on_association(method).klass.new
    options[:partial] ||= method.to_s.singularize
    options[:form_builder_local] ||= :form
    form_builder.fields_for(method, options[:object], :child_index =>
"new_#{method}") do |form|
      render(:partial => options[:partial], :locals => { options
[:form_builder_local] => form })
    end
  end
end

-------------------------------

Thanks,
Ryan

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Talk" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to