First post on stackoverflow, apologies beforehand for anything noob-like.
I have been receiving an error on one of my functional tests and after many hours, I just can’t seem to figure out how to pass it.
In short, an invoice_schedule which has_many invoice_milestones. Then, invoice_milestones has an attribute, estimate_percentage (an integer). My model models/invoice_schedule.rb has a validation which requires the sum of all estimate_percentages must equal 100.
The functional test for the invoice_schedule create action fails every time. With my current code below it has been erroring out instead. I can’t see how it would error out with the params I’m passing.
I’ll also note that estimate has_one :invoice_schedule
Any help or suggestions are appreciated 🙂
models/invoice_schedule.rb:
class InvoiceSchedule < ActiveRecord::Base
belongs_to :estimate
has_many :invoice_milestones, :dependent => :destroy
accepts_nested_attributes_for :invoice_milestones, :allow_destroy => true
validate :total_must_equal_one_hundred_percent
def total_must_equal_one_hundred_percent
if total_percent != 100
errors.add(:invoice_milestones, "Total must equal 100%")
end
end
def total_percent
invoice_milestones.to_a.sum { |item| item.estimate_percentage || 0 }
end
end
models/invoice_milestone.rb:
class InvoiceMilestone < ActiveRecord::Base
belongs_to :invoice_schedule
belongs_to :invoice
validates :estimate_percentage, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 100}
end
controllers/invoice_schedules_controller.rb
def create
@invoice_schedule = InvoiceSchedule.new(params[:invoice_schedule])
@estimate = Estimate.find_by_id(params[:estimate_id])
respond_to do |format|
if @invoice_schedule.save
format.html { redirect_to invoice_schedule_path(@invoice_schedule), :flash => {:notice => 'Invoice schedule was successfully created.', :status => 'success'} }
format.json { render json: @invoice_schedule, status: :created, location: @invoice_schedule }
else
format.html { render action: "new" }
format.json { render json: @invoice_schedule.errors, status: :unprocessable_entity }
end
end
end
test/functional/invoice_schedules_controller_test.rb
setup do
@account = accounts(:lorem)
@estimate = estimates(:lorem_one)
@user = users(:lorem_vendor)
@request.host = "#{@account.subdomain}.myapp.local"
session[:user_id] = @user.id
@invoice_schedule = invoice_schedules(:lorem_one)
@invoice_milestone = invoice_milestones(:lorem_one)
@data_estimate = estimates(:lorem_two)
@data_invoice_schedule = @invoice_schedule.attributes.merge({estimate_id: @data_estimate.id})
end
test "should create invoice_schedule" do
assert_difference('InvoiceSchedule.count') do
post :create, :invoice_schedule => { estimate_id: @data_estimate, :invoice_milestone => {estimate_percentage: 100}}
end
assert_redirected_to estimate_invoice_schedule_path(@estimate, assigns(:invoice_schedule))
end
config/routes.rb
require 'subdomain'
Accountimize::Application.routes.draw do
resources :invoices do
member do
get 'generateInvoiceFromMilestone'
end
end
resources :users
resources :sessions
get "sign_up" => "accounts#new", :as => "sign_up"
resources :accounts
resources :clients do
get :client_address, on: :member
end
resources :estimates do
resources :invoice_schedules, :shallow => true
end
resources :line_items
constraints(Subdomain) do
match '/' => 'accounts#show'
get "log_in" => "sessions#new", :as => "log_in"
get "log_out" => "sessions#destroy", :as => "log_out"
get "register" => "users#new", :as => "register"
end
root :to => 'site#index', :as => 'site'
end
The trace of the error I get:
InvoiceSchedulesControllerTest
test_should_create_invoice_schedule ERROR
No route matches {:invoice_schedule=>{:estimate_id=>"706507935", :invoice_milestones_attributes=>{"0"=>{:description=>"test", :estimate_percentage=>"100"}}}, :controller=>"invoice_schedules", :action=>"create"}
STDERR:
Exception `ActionController::RoutingError' at /Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:465:in `raise_routing_error'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:461:in `rescue in generate'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:453:in `generate'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:494:in `generate'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:490:in `generate_extras'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_dispatch/routing/route_set.rb:486:in `extra_keys'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:145:in `assign_parameters'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:438:in `process'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:49:in `process'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/actionpack-3.1.1/lib/action_controller/test_case.rb:370:in `post'
test/functional/invoice_schedules_controller_test.rb:35:in `block (2 levels) in <class:InvoiceSchedulesControllerTest>'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/assertions.rb:55:in `assert_difference'
test/functional/invoice_schedules_controller_test.rb:30:in `block in <class:InvoiceSchedulesControllerTest>'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/setup_and_teardown.rb:35:in `block in run'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:444:in `_run_setup_callbacks'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:81:in `run_callbacks'
/Users/Matt/.rbenv/versions/1.9.3-p194/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/testing/setup_and_teardown.rb:34:in `run'
I’ve tried quite a few other ways to accomplish adding an invoice_milestone to a newly created invoice_schedule in the functional test, however nothing seems to have worked for me. Why would I receive the above routing error if accepts_nested_attributes_for is added?
There are two parts to this. First you need to get the routing side right. Because you’ve nested invoice schedules inside estimates you need to supply an
estimate_idat the top level (not insideparams[:invoice_schedules]), i.e.The expectation is then that you controller does something along the lines of
The second part is to do with nested attributes – you need to provide a params hash that matches what Rails expects. The first part of that is that the key in the hash should be
:invoice_milestones_attributes. Since this is ahas_many, the corresponding value needsto be an array of hashes, or a hash of hashes (the keys are ignored – this is largely present because of interaction between arrays and hashes in a rails’ parameter passing), for example