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 <http://github.com/ryanb/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