views:

376

answers:

2

Hey rails folks,

I haven't used rails since version 1.2 or so and a few things have changed. I have an issue where I am trying to save an empty model to get validation errors on attributes using :validates_presence_of and instead I am getting the error 'can't convert HashWithIndifferentAccess into String'. I will attempt to simplify my code to get the point across as tersely as possible...

my model:

class Project < ActiveRecord::Base

    validates_presence_of :title, :description
    validates_uniqueness_of :title

    has_one :address

    accepts_nested_attributes_for :address, :allow_destroy => true
end

Child model:

class Address < ActiveRecord::Base
    validates_presence_of :title, :street
    belongs_to :project
end

controller:

class ProjectsController < ApplicationController
    def create
        @project = Project.new(params[:project])
        if @project.save
            flash[:notice] = @project.title + ' successfully created'
            redirect_to projects_path
        else
            render :action => 'new'
        end
    end
end

view:

<%= error_messages_for 'project' %>
<% form_for @project do |f| %>
<table width="100%" cellpadding="3" cellspacing="0">
    <tr>
        <td class="adminlabel">
            <label for="Title">Title</label>
        </td>
        <td class="adminbody">
            <%= f.text_field :title %>
        </td>
    </tr>   
    .....
    <% f.fields_for :address do |address| %>
    ....

This code adds and updates just fine as long as I fill in all the required fields, if I leave any blank I get the error mentioned above, not the most graceful way of alerting users there is an issue ;)

Request params:
{"commit"=>"Save",
"project"=>{"title"=>"",
"notes"=>"",
"description"=>"",
"address_attributes"=>{"city"=>"",
"zip"=>"",
"title"=>"",
"country"=>"",
"suite"=>"",
"street"=>"",
"state"=>""}},
"authenticity_token"=>"iNPQZrf/oBv22vaI0toTGhknwx0aAU3BSvnIh6qgYQ8="}

I have been searching for days and am at my wits end, if anyone can shed a little light on this for me I would greatly appreciate it.

Thanks in advance!

Brendan

PS - the stack trace as requested:

.../app/controllers/projects_controller.rb:27:in `+'  
.../app/controllers/projects_controller.rb:27:in `create'  
../actionpack-2.3.4/lib/action_controller/base.rb:1331:in `send'  
../actionpack-2.3.4/lib/action_controller/base.rb:1331:in `perform_action_without_filters'  
../actionpack-2.3.4/lib/action_controller/filters.rb:617:in `call_filters'  
../actionpack-2.3.4/lib/action_controller/filters.rb:610:in `perform_action_without_benchmark'  
../actionpack-2.3.4/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'  
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:17:in `ms'  
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:10:in `realtime'  
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:17:in `ms'  
../actionpack-2.3.4/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'  
../actionpack-2.3.4/lib/action_controller/rescue.rb:160:in `perform_action_without_flash'  
../actionpack-2.3.4/lib/action_controller/flash.rb:146:in `perform_action'  
../actionpack-2.3.4/lib/action_controller/base.rb:532:in `send'  
../actionpack-2.3.4/lib/action_controller/base.rb:532:in `process_without_filters'  
../actionpack-2.3.4/lib/action_controller/filters.rb:606:in `process'  
../actionpack-2.3.4/lib/action_controller/base.rb:391:in `process'  
../actionpack-2.3.4/lib/action_controller/base.rb:386:in `call'  
../actionpack-2.3.4/lib/action_controller/routing/route_set.rb:437:in `call'  
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:87:in `dispatch'  
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:121:in `_call'  
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:130:in `build_middleware_stack'   
../activerecord-2.3.4/lib/active_record/query_cache.rb:29:in `call'  
../activerecord-2.3.4/lib/active_record/query_cache.rb:29:in `call'  
../activerecord-2.3.4/lib/active_record/connection_adapters/abstract/query_cache.rb:34:in `cache'  
..activerecord-2.3.4/lib/active_record/query_cache.rb:9:in `cache'   
../activerecord-2.3.4/lib/active_record/query_cache.rb:28:in `call'  
../activerecord-2.3.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:361:in `call'  
../rack-1.0.1/lib/rack/head.rb:9:in `call'  
../rack-1.0.1/lib/rack/methodoverride.rb:24:in `call'  
../actionpack-2.3.4/lib/action_controller/params_parser.rb:15:in `call'  
../actionpack-2.3.4/lib/action_controller/session/cookie_store.rb:93:in `call'  
../actionpack-2.3.4/lib/action_controller/failsafe.rb:26:in `call'  
../rack-1.0.1/lib/rack/lock.rb:11:in `call'  
../rack-1.0.1/lib/rack/lock.rb:11:in `synchronize'  
../rack-1.0.1/lib/rack/lock.rb:11:in `call'  
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:114:in `call'  
../actionpack-2.3.4/lib/action_controller/reloader.rb:34:in `run'  
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:108:in `call'  
../rails-2.3.4/lib/rails/rack/static.rb:31:in `call'  
../rack-1.0.1/lib/rack/urlmap.rb:46:in `call'  
../rack-1.0.1/lib/rack/urlmap.rb:40:in `each'  
../rack-1.0.1/lib/rack/urlmap.rb:40:in `call'  
../rails-2.3.4/lib/rails/rack/log_tailer.rb:17:in `call'  
../rack-1.0.1/lib/rack/content_length.rb:13:in `call'  
../rack-1.0.1/lib/rack/chunked.rb:15:in `call'  
../rack-1.0.1/lib/rack/handler/mongrel.rb:64:in `process'
..... etc.

A bit lengthy, sorry ;)

+2  A: 

Try adding

@project.address.build

to your controller. You need to instantiate the address object.

bensie
The problem isn't with instantiating the address, as I mentioned above the code works swimmingly in a best-case scenario, it only fails when I try to pass empty strings for required fields.
Brendan Kendrick
Looks like your "+" is causing the issue when the project title is null. Try removing that from your flash message and just have a basic "Created" string for now.
bensie
The '+' in the flash message is never being hit because the model is never successfully created, @project.save would return false if it ever got that far. Also I believe the project title is passed as an empty string, not nil.
Brendan Kendrick
What is line 27 in that controller? Did you try commenting it out anyway?
bensie
Haha, that's embarrassing. The guilty code is in my 'else' logic that I stripped out for the sake of simplicity. Thanks the reality check!
Brendan Kendrick
+1  A: 

The trace doesn't lie. You're having issues with a line 27. The code you posted doesn't have 37 lines, but based on the trace I'm willing to bet it's this line. Because none others have have a '+'. If there was an implicit + its caller would've been listed in the trace.

 flash[:notice] = @project.title + ' successfully created'

It's odd that it's reaching that point based on your validations. validates_presence_of should add errors on attributes that are either nil, false or "". Does the validation fail as expected when run in the console?

Here's a console friendly version of your code that could be used to track down your issue. You should be able to just paste it into the console to help track down your issue.

params = {
  "project"=> {
    "title"=>"", "notes"=>"","description"=>"", 
    "address_attributes"=>{
      "city"=>"", "zip"=>"","title"=>"","country"=>"",
      "suite"=>"","street"=>"","state"=>""
    }
   }
flash = {}

@project = Project.new(params[:project])
if @project.save
  flash[:notice] = @project.title + ' successfully created'
  puts "Saved. Flash: #{flash[:notice]}"
else
  puts "validations failed:" 
  puts @project.errors.full_messages.map{|m| "\t#{m}"}.join("\n")
end
EmFi
Yeah, I discovered it was actually a huge oversight of mine on line 27, trying to '+' something that simply didn't exist. Thanks so much for your response, I'm just not very used to debugging rails stuff just yet.
Brendan Kendrick