Hi everyone,

My previous question was posted
here<http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/dddaeb075789d4b5>.
I'm starting a new topic as the problems have shifted.

I have a rails application that models a house. There is a house model that
has_many rooms. A room has a house_id and a name. I've also used the
complex-form-examples to give room nested attributes of lights and
small_appliances. complex-form-examples uses RJS and partials to accomplish
this.

There is a controller called calculator that is what users will use to
access the application. When the submit button on calculator is pressed, it
saves house information and redirects to an add_rooms page (located in
app/views/calculator/add_rooms.html.erb) where the user can add rooms to the
house. The add_rooms page uses a partial from
app/views/rooms/_room_form.html.erb. I can get this page to display by
removing the add_child_link links. If I don't remove them, I get this error:

    Showing app/views/rooms/_room_form.html.erb where line #13 raised:

    undefined method `reflect_on_association' for NilClass:Class

With add_child_link gone the page renders. However, when I click submit I
get a new error message:

    ActiveRecord::AssociationTypeMismatch in CalculatorController#add_room

    SmallAppliance(#49096610) expected, got Array(#1560620)

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

    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_proxy.rb:263:in
`raise_on_type_mismatch'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in
`replace'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in
`each'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_collection.rb:320:in
`replace'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations.rb:1322:in
`small_appliances='
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2744:in
`send'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2744:in
`attributes='
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2740:in
`each'
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2740:in
`attributes='
    
C:/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2438:in
`initialize'
    C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:31:in
`new'
    C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:31:in
`add_room'

If I remove the small_application part, the same thing happens for light. I
think it has something to do with accepts_nested_attributes_for in the room
model. I need to get this page working so that rooms can be added to a house
without using the scaffold built pages. Users won't have access to those. I
also need the house id to be saved as house_id in the rooms table. Here is
the code:

*app/models/room.rb*

    class Room < ActiveRecord::Base
      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

      # save_house is called when submit is
      # pressed in app/views/calculator/index.html.erb
      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
        @house = House.find(params[:id])
        @room = Room.new(params[:room])

        respond_to do |format|
          if @room.save
            @house.add_room(@room)
            @house.save
            flash[:notice] = "Room \"#...@room.name}\" 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 />
    <%= button_to "Continue to report", :action => "report", :id => @house %>
    </div>

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

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

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

      <h3>Small Appliances</h3>
      <% form.fields_for :small_appliances do |sm_appl_form| %>
        <%= render :partial => 'rooms/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

*public/javascripts/application.js*

    function insert_fields(link, method, content) {
      var new_id = new Date().getTime();
      var regexp = new RegExp("new_" + method, "g")
      $(link).up().insert({
        before: content.replace(regexp, new_id)
      });
    }

    function remove_fields(link) {
      var hidden_field = $(link).previous("input[type=hidden]");
      if (hidden_field) {
        hidden_field.value = '1';
      }
      $(link).up(".fields").hide();
    }

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 rubyonrails-talk@googlegroups.com
To unsubscribe from this group, send email to 
rubyonrails-talk+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to