Alon Shmiel
Alon Shmiel

Reputation: 7121

undefined method `tasks_path' for #<#<Class:0xb663ccf0>:0xb663b3c8>

I have two controllers: tasks and workers.

I visited localhost:3000/workers. When I pressed "Add a New Task" (this is the link I put: <%= link_to 'Add a New Task', new_worker_path %>), I got errors:

NoMethodError in Workers#new

Showing /home/alon/projects/todo/app/views/workers/new.html.erb where line #3 raised:

undefined method `tasks_path' for #<#<Class:0xb663ccf0>:0xb663b3c8>
Extracted source (around line #3):

1: <h1>New task</h1>
2: 
3: <%= form_for @workers do |f| %>
4:  <p>
5:      <%= f.label :name %><br />
6:      <%= f.text_field :name %>
Rails.root: /home/alon/projects/todo

Application Trace | Framework Trace | Full Trace
app/views/workers/new.html.erb:3:in     `_app_views_workers_new_html_erb___132490529__619346358'
app/controllers/workers_controller.rb:28:in `new'
Request

Parameters:

None
Show session dump

Show env dump

Response

Headers:

None

Why did I get these errors?

I created the files _form.html.erb, index.html.erb, new.html.erb, show.html.erb, and edit.html.erb in views/workers.

I defined in routes.rb:

resources :workers do
    resources :tasks
end

This is my workers_controller:

class WorkersController < ApplicationController

  def index
    @workers = Task.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @workers }
    end
  end

  # GET /workers/1
  # GET /workers/1.json
  def show
    #.find(params[:id])
     @workers = Task.find(params[:id])
    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @workers }
    end
  end

  # GET /workers/new
  # GET /workers/new.json
  def new
    @workers = Task.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @workers }
    end
  end

  # GET /workers/1/edit
  def edit
    @worker = Task.find(params[:id])
  end

  # POST /workers
  # POST /workers.json
  def create
    @worker = Task.new(params[:task])

    respond_to do |format|
      if @worker.save
        format.html { redirect_to @worker, notice: 'Your task was created.' }
        format.json { render json: @worker, status: :created, location: @worker }
      else
        render "new"
      end
    end
  end

  # PUT /workers/1
  # PUT /workers/1.json
  def update
    @worker = Task.find(params[:id])

    respond_to do |format|
      if @worker.update_attributes(params[:task])
        format.html { redirect_to @worker, notice: 'Task was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @worker.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /workers/1
  # DELETE /workers/1.json
  def destroy
    @worker = Task.find(params[:id])
    @worker.destroy

    respond_to do |format|
      format.html { redirect_to tasks_url }
      format.json { head :no_content }
    end
  end

end

In addition, I wrote the next code in models/tasks.rb:

class Task < ActiveRecord::Base
  belongs_to :Worker
  attr_accessible :done, :name, :task
end

This is my workers/new.html.erb file:

<h1>New task</h1>

<%= form_for @workers do |f| %>
<p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
</p>
<p>
    <%= f.label :task %><br />
    <%= f.text_area :task %>
</p>
<p>
    <%= f.label :done %><br />
    <%= f.check_box :done %>    
</p>
<p>
    <%= f.submit "add a new task" %>
</p>
<% end %>

and this is workers/_form.html.erb:

<%= form_for(@task) do |f| %>

  <% if @task.errors.any? %>
      <div id="error_explanation">
          <h2><%= pluralize(@task.errors.count, "error") %> prohibited this task from being saved:</h2>
          <ul>
              <% @task.errors.full_messages.each do |msg| %>
                 <li><%= msg %></li>
              <% end %>
          </ul>
      </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :task %><br />
    <%= f.text_area :task %>
  </div>
  <div class="field">
    <%= f.label :done %><br />
    <%= f.check_box :done %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Upvotes: 0

Views: 498

Answers (2)

Michael Durrant
Michael Durrant

Reputation: 96484

If you type rake routes you will see:

$ rake routes
    worker_tasks GET    /workers/:worker_id/tasks(.:format)          tasks#index
                 POST   /workers/:worker_id/tasks(.:format)          tasks#create
 new_worker_task GET    /workers/:worker_id/tasks/new(.:format)      tasks#new
...

So new_worker_task will give you a new task for the worker in question when you pass the worker in.

<%= link_to 'Add a New Task', new_worker_task_path(worker) %> # (below)

I recreated your app locally and got it to work with:

<table>
  <tr>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @workers.each do |worker| %>
  <tr>
    <td><%= link_to 'Show', worker %></td>
    <td><%= link_to worker.name, worker %></td>
    <td><%= link_to 'Edit', edit_worker_path(worker) %></td>
    <td><%= link_to 'New Task', new_worker_task_path(worker) %>
    <td><%= link_to 'Destroy', worker, method: :delete, data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

Note that I added a 'name' attribute for worker to make them easier to show (added it to the form and the index pages and of course a db migration to add it to the db).

As pjammer points out you can also do this through nested_attributes in forms, e.g. form_for([@worker, @task])

Upvotes: 2

pjammer
pjammer

Reputation: 9577

you seem to be using nested routes but are passing in Task.new instead of something like Worker.new and if you want to add tasks to the worker, use accepts_nested_attributes_for or create a Form Object to handle the creation of tasks.

Upvotes: 1

Related Questions