Search code examples
ruby-on-railsdevisejrubypundit

Rails 4 Devise, Pundit, Join Table ActionView::Template::Error (undefined method `task_definition_path' for #<#<Class:0x37682f95>:0x6919b2b2>):


Here is my use case:

  1. I have one user model with Devise for AuthN and I am using Pundit for AuthZ.
  2. I restrict access to the main application through a subdomain constraint.
  3. I have some pages that are for end users (will be a different LandF at some point) and I have administrative pages etc. Common story you know the drill.
  4. I am using a has_and_belongs_to_many utilizing a join table with :id's
  5. I have my controllers, view directories and migrations named as plurals, my models as singular. Example: TaskDefinition => Model, TaskDefinitions => Controller and Tables.
  6. The default routes are generated and i have provided the content.
  7. I am using partials in the view directories and this is very new issue since a port from Ruby to JRuby.

Stack Trace:

ActionView::Template::Error (undefined method `task_definition_path' for #<#<Class:0x37682f95>:0x6919b2b2>):
    10:     <div class="col-md-10">
    11:       <div class="panel-body">
    12:         <div class="form">
    13:           <%= bootstrap_form_for @task do |f| %>
    14:               <div class="form-group">
    15:                 <%= render '/admin/task_definitions/errors' %>
    16:               </div>
  app/views/admin/task_definitions/edit.html.erb:13:in`_app_views_admin_task_definitions_edit_html_erb__1276994696_33458'

Migrations:

class CreateTaskDefinitions < ActiveRecord::Migration
  def change
    create_table :task_definitions do |t|
      # foreign key
      t.integer :organization_id

      # attributes
....

      t.timestamps
    end

    # index
    add_index :task_definitions, :name, unique: true
  end
end

class CreateOrganizations < ActiveRecord::Migration
  def change
    create_table :organizations do |t|
      # for the relationship between parent orgs and child nodes
      t.references :parent

      # Used to determine Parent or Child
      t.string :org_type

      # Subdomain used for scoping site
      t.string :subdomain

      # Common fields
....

      t.timestamps
    end
    # index
    add_index :organizations, [:name, :subdomain], unique: true
  end
end

class CreateOrganizationsTaskDefinitions < ActiveRecord::Migration
  def change
    create_table :organizations_task_definitions, id: false do |t|
      t.integer :organization_id
      t.integer :task_definition_id
    end
    add_index :organizations_task_definitions, [:organization_id, :task_definition_id], name: 'index_organizations_task_definitions'
  end
end

models:

class Organization < ActiveRecord::Base
  #associations
  has_many :users, class_name: 'User', inverse_of: :organization
  has_and_belongs_to_many :task_definitions, class_name: 'TaskDefinition', inverse_of: :organizations
  has_one :address, class_name: 'Address'
  has_many :children, class_name: 'Organization', foreign_key: 'parent_id'
  belongs_to :parent, class_name: 'Organization'
  accepts_nested_attributes_for :address
end

class TaskDefinition < ActiveRecord::Base
  #associations
  has_many :steps, class_name: 'TaskStep', inverse_of: :task_definition
  has_and_belongs_to_many :organizations, class_name: 'Organization', inverse_of: :task_definitions
  has_and_belongs_to_many :task_events, class_name: 'TaskEvent', inverse_of: :task_definitions
  accepts_nested_attributes_for :steps
end

Controller:

class Admin::TaskDefinitionsController < ApplicationController
  before_filter :authenticate_user!
  after_action :verify_authorized

.....

  def edit
    @tasks = current_organization.task_definitions
    if(@tasks.size > 0 )
      @task = @tasks.find(params[:id])
      authorize @task

      # add breadcrumb
      add_breadcrumb @task.name, admin_task_definition_path(@task)

      unless current_user.org_super_admin? or current_user.finch_admin?
        unless @user == current_user
          redirect_to :back, :alert => "Access denied."
        end
      end
    end
  end
end

Routes:

Rails.application.routes.draw do
 ......

  constraints(Finch::Constraints::SubdomainRequired) do
    #
    # dashboards
    #
    resource :dash_boards, only: [:index, :show, :edit, :update, :destroy]

    #
    # orgss
    #
    resource :organizations, only: [:index, :show, :edit, :update, :destroy]

    #
    # Only Admins are allowed to access
    #
    namespace :admin do
      #
      # Workflow Data
      #
      resources :task_definitions, only: [:index, :show, :edit, :update, :destroy]
      resources :task_steps, only: [:show, :edit, :update, :destroy]
      resource  :task_actions, only: [:show, :edit, :update, :destroy]
      resource  :task_action_attributes, only: [:show, :edit, :update, :destroy]
      resource  :task_transitions, only: [:show, :edit, :update, :destroy]
    end
  end
end

view:

    <div class="form">
      <%= bootstrap_form_for @task do |f| %>
          <div class="form-group">
            <%= render '/admin/task_definitions/errors' %>
          </div>

rake routes:

           edit_organizations GET    /organizations/edit(.:format)                organizations#edit
                organizations GET    /organizations(.:format)                     organizations#show
                              PATCH  /organizations(.:format)                     organizations#update
                              PUT    /organizations(.:format)                     organizations#update
                              DELETE /organizations(.:format)                     organizations#destroy
       admin_task_definitions GET    /admin/task_definitions(.:format)            admin/task_definitions#index
   edit_admin_task_definition GET    /admin/task_definitions/:id/edit(.:format)   admin/task_definitions#edit
        admin_task_definition GET    /admin/task_definitions/:id(.:format)        admin/task_definitions#show

Solution

  • Undefined method errors ending in _path or _url usually indicate a bad route helper.

    Checking the spot of the error, it seems that a helper method (bootstrap_form_for) is calling the route helper task_definitions_path, which is incorrect. That route is namespaced under admin according to your routes file, so the proper route helper is:

    admin_task_definitions_path  
    

    I don't know what is in the bootstrap_form_for helper method, so I don't have a specific fix for you. Assuming you use the Bootstrap forms gem, skip it and write the form manually.


    In the future, rake routes will show all registered route helpers. A handy list for debugging bad route helpers.