Is my routing wrong?

My routing:

resources :incidents do     resource :timesheet do   resource :command_officer     end end

My CommandOfficersController: def new     @command_officer = CommandOfficer.new ..

Now if I want to add a Command Officer to an Incident's Timesheet, I go to: /incidents/41/timesheet/command_officer/new

And I get this error:

NoMethodError in Command_officers#new Showing C:/Rails/costrecovery/app/views/command_officers/_form.html.erb where line #1 raised:

undefined method `command_officers_path' for #<#<Class:0x422f1c0>:0x422cb38>

Extracted source (around line #1): 1: <%= form_for(@command_officer) do |f| %> 2: <% if @command_officer.errors.any? %>

Can anyone explain why I'm getting this error. I've been trying to find out for hours.

Finne Jager wrote in post #971120:

My routing:

resources :incidents do     resource :timesheet do       resource :command_officer     end end

My CommandOfficersController: def new     @command_officer = CommandOfficer.new ..

Now if I want to add a Command Officer to an Incident's Timesheet, I go to: /incidents/41/timesheet/command_officer/new

And I get this error:

NoMethodError in Command_officers#new Showing C:/Rails/costrecovery/app/views/command_officers/_form.html.erb where line #1 raised:

undefined method `command_officers_path' for #<#<Class:0x422f1c0>:0x422cb38>

Extracted source (around line #1): 1: <%= form_for(@command_officer) do |f| %> 2: <% if @command_officer.errors.any? %>

Can anyone explain why I'm getting this error. I've been trying to find out for hours.

Since your resources are nested, you need to use the array syntax of form_for so that the proper route is generated.

Best,

Since your resources are nested, you need to use the array syntax of form_for so that the proper route is generated.

<%= form_for :command_officer, @command_officer do |f| %>

That one doesn't generate any errors, thanks. But when I fill in the form and press submit, I get:

Routing Error No route matches "/incidents/41/timesheet/command_officer/new"

Finne Jager wrote in post #971133:

Since your resources are nested, you need to use the array syntax of form_for so that the proper route is generated.

<%= form_for :command_officer, @command_officer do |f| %>

That one doesn't generate any errors, thanks. But when I fill in the form and press submit, I get:

Routing Error No route matches "/incidents/41/timesheet/command_officer/new"

Nevermind this! I forgot to add the options hash: :url => { :action => "create" }

Finne Jager wrote in post #971133:

Since your resources are nested, you need to use the array syntax of form_for so that the proper route is generated.

<%= form_for :command_officer, @command_officer do |f| %>

No! You need the *parent* resource names in there, I think. Rails already knows what kind of resource @command_officer itself is.

That one doesn't generate any errors, thanks. But when I fill in the form and press submit, I get:

Routing Error No route matches "/incidents/41/timesheet/command_officer/new"

Best,

Marnen Laibow-Koser wrote in post #971140:

Finne Jager wrote in post #971133:

Since your resources are nested, you need to use the array syntax of form_for so that the proper route is generated.

<%= form_for :command_officer, @command_officer do |f| %>

No! You need the *parent* resource names in there, I think. Rails already knows what kind of resource @command_officer itself is.

Hmm, it seems to work like that without the parent resource. Probably because I have CommandOfficers#create like this?

def create     @timesheet = Incident.find(params[:incident_id]).timesheet

    respond_to do |format|       if @timesheet.create_command_officer(params[:command_officer]) ..

I'm having another issue. This is my create method for a Command Officer:

def create   @timesheet = Incident.find(params[:incident_id]).timesheet

    respond_to do |format|       if @timesheet.create_command_officer(params[:command_officer])

This is working fine. But when I use the same code in FireFighter#create:       if @timesheet.create_fire_fighter(params[:fire_fighter])

I get this error: NoMethodError in Fire fightersController#create undefined method `create_fire_fighter' for "#<Timesheet:0x3f8b938>":Timesheet

As far as I know everything is set up identical to Command Officer and I don't know why the create_fire_fighter doesn't work, while it does work for Command Officer?

Finne Jager wrote in post #971310:

I'm having another issue. This is my create method for a Command Officer:

def create   @timesheet = Incident.find(params[:incident_id]).timesheet

    respond_to do |format|       if @timesheet.create_command_officer(params[:command_officer])

This is working fine. But when I use the same code in FireFighter#create:       if @timesheet.create_fire_fighter(params[:fire_fighter])

I get this error: NoMethodError in Fire fightersController#create undefined method `create_fire_fighter' for "#<Timesheet:0x3f8b938>":Timesheet

As far as I know everything is set up identical to Command Officer

Obviously it isn't.

and I don't know why the create_fire_fighter doesn't work, while it does work for Command Officer?

You forgot to set up the associations on Timesheet properly. What associations does Timesheet currently have?

Best,

You forgot to set up the associations on Timesheet properly. What associations does Timesheet currently have?

class Timesheet < ActiveRecord::Base   belongs_to :incident

  has_one :command_officer   has_one :fire_chief   has_many :fire_fighters

class FireFighter < ActiveRecord::Base   belongs_to :timesheet

Finne Jager wrote in post #971314:

You forgot to set up the associations on Timesheet properly. What associations does Timesheet currently have?

class Timesheet < ActiveRecord::Base   belongs_to :incident

  has_one :command_officer   has_one :fire_chief   has_many :fire_fighters

Still using the data model I advised you against, I see...

...but in any case, I think you want @timesheet.fire_fighters.create, not @timesheet.create_fire_fighter , which doesn't exist on has_many.

class FireFighter < ActiveRecord::Base   belongs_to :timesheet

I tested it with the console, Tiemsheet.last.fire_fighters returns an empty array

You do know that Timesheet.last will return an arbitrary timesheet, and that you can't predict which one it returns, right?

Best,

Marnen Laibow-Koser wrote in post #971320: ...

You do know that Timesheet.last will return an arbitrary timesheet, and that you can't predict which one it returns, right?

That is not entirely true. It is 'Timesheet.first' that would better be renamed to 'Timesheet.random' :wink: Same goes for Timesheet.find_by_name (that is really "pick an arbitrary entry with that name").

But 'Timesheet.last' actually has a default ordering on 'id DESC'.

I saw that behavior in Rails 2.3.5 and 3.0.3. Here in 3.0.3:

ruby-1.9.2-p136 :013 > Timesheet.first => #<Timesheet id: 1, name: "Jon", date: "2010-12-29", project: "test"> ruby-1.9.2-p136 :014 > Timesheet.last => #<Timesheet id: 4, name: "Jon", date: "2010-12-31", project: "test 4"> ruby-1.9.2-p136 :015 > Timesheet.find_by_name('Jon') => #<Timesheet id: 1, name: "Jon", date: "2010-12-29", project: "test">

yields:

  Timesheet Load (0.2ms) SELECT "timesheets".* FROM "timesheets" LIMIT 1   Timesheet Load (0.2ms) SELECT "timesheets".* FROM "timesheets" ORDER BY timesheets.id DESC LIMIT 1   Timesheet Load (0.2ms) SELECT "timesheets".* FROM "timesheets" WHERE ("timesheets"."name" = 'Jon') LIMIT 1

This caused us quite some fun at work (e.g. flickering tests at best, unpredictable results at worst), so I eventually implemented a ".single" method that barfs loudly if you _expect_ to find only zero or 1 entries for a certain query or in a certain array and then unexpectly, there are more entries there ...

For that reason, at work, I advise against the use of: * .first => replaced by .single * find_by => replaced by find_all_by().single

HTH,

Peter

Still using the data model I advised you against, I see...

Yes, unfortunately my boss has been pushing to get a first version online within two weeks. I've decided to stick with this structure for now and then restructure after launch, especially since only 3-4 clients will be using this initially.

...but in any case, I think you want @timesheet.fire_fighters.create, not @timesheet.create_fire_fighter , which doesn't exist on has_many.

Thanks, I did not know that 'create_modelname' was only available with has_one relationships.

I tested it with the console, Tiemsheet.last.fire_fighters returns an empty array

You do know that Timesheet.last will return an arbitrary timesheet, and that you can't predict which one it returns, right?

Yes, I was just using that to test if the association was working correctly.

Finne Jager wrote in post #971329:

Still using the data model I advised you against, I see...

Yes, unfortunately my boss has been pushing to get a first version online within two weeks. I've decided to use this current structure for now and then restructure after launch.

I advise against this course of action in the strongest possible terms. Normalizing the schema will be easy. Refactoring the application code will be easy if you have proper tests. But normalizing the data already entered into the denormalized schema will be nearly impossible.

This is one of those things that you want to get right the first time. Normalize your schema. It's easier to denormalize later than to normalize later.

...but in any case, I think you want @timesheet.fire_fighters.create, not @timesheet.create_fire_fighter , which doesn't exist on has_many.

Thanks, I did not know that 'create_modelname' was only available with has_one relationships.

Did you check the docs? :slight_smile:

I tested it with the console, Tiemsheet.last.fire_fighters returns an empty array

You do know that Timesheet.last will return an arbitrary timesheet, and that you can't predict which one it returns, right?

Yes, I was just using that to test if the association was working correctly.

OK. You'd probably be better served to get good automated tests working.

Best,

I advise against this course of action in the strongest possible terms. Normalizing the schema will be easy. Refactoring the application code will be easy if you have proper tests. But normalizing the data already entered into the denormalized schema will be nearly impossible.

This is one of those things that you want to get right the first time. Normalize your schema. It's easier to denormalize later than to normalize later.

Ok thanks, I am going to ask my boss to move the launch deadline in order to avoid those problems.