Dimitri de Ruiter
Dimitri de Ruiter

Reputation: 745

Rails 5 - NoMethod Error - undefined method

In the app I am building to learn Rails, I have a polymorphic relationship between the model "TAG" and the models "ANNOTATION" and "DOCUMENT". Similar to Article and Comment models.

Create and destroy of tags is working. Now, I want to update the tags and run into a no method error I don't understand. Tried many alternatives (e.g. using <%= link_to tag.content, [object, tag]... with the form <%= simple_form_for object.tag ... ).

The form is called using:

<% object.tags.each do |tag| %>
        <% unless tag.content.blank? %>
        <tr>
            <td><%= link_to tag.content, @tag, method: :patch %></td>

This is the tags controller:

class TagsController < ApplicationController

def index

end

def create
  tagable = detect_tagable
  tagable.tags.create(tag_params)
  redirect_to tagable_path(tagable)
end

def update
  tagable = detect_tagable
  @tag = tagable.tags.find(params[:id]) 
  @tag.save
  render '_tag_update'
end

def destroy
  tagable = detect_tagable
  @tag = tagable.tags.find(params[:id])
  @tag.destroy
  redirect_to tagable_path(tagable)
end

private

  def tagable_path(tagable)
    case tagable
    when Document
      document_path(tagable)
    when Annotation
      annotate_path(tagable)
    else
      fail 'Unknown tagable'
    end
  end

  def detect_tagable
    if params[:annotation_id]
      Annotation.find(params[:annotation_id])
    elsif params[:document_id]
      Document.find(params[:document_id])
    else
      fail 'Tagable not found'
    end
  end

  def tag_params
    params.require(:tag).permit(:content, :location, :tagtype_id,annotation_attributes: { annotation_ids:[] }, document_attributes: { document_ids:[] })
  end

end

It renders the correct form _tag_update.html.erb with the right parameters (annotation id and tag id) yet throws the error on:

<%= simple_form_for @tag, html: { class: 'form-vertical', multipart: true },

Full error

NoMethodError in Tags#update Showing /Users/Dimitri/Documents/AppDev/shine/app/views/tags/_tag_update.html.erb where line #1 raised: undefined method `tag_path' for #<#:0x007fc2aede9d88> Did you mean? tagtype_path Extracted source (around line #1): 1 2 3 4 5 6
<%= simple_form_for @tag, html: { class: 'form-vertical', multipart: true }, wrapper: :horizontal_form, wrapper_mappings: { check_boxes: :horizontal_radio_and_checkboxes, radio_buttons: :horizontal_radio_and_checkboxes, file: :horizontal_file_input, Rails.root: /Users/Dimitri/Documents/AppDev/shine

Application Trace | Framework Trace | Full Trace:

app/views/tags/_tag_update.html.erb:1:in _app_views_tags__tag_update_html_erb___1949489846228898836_70237067101380' app/controllers/tags_controller.rb:17:inupdate' Request

Parameters:

{"_method"=>"patch", "authenticity_token"=>"LhqKjyjbYdznMvx+GjsIL0phwT8pRTtanooKU6Xt4hHaPRFMmZJmZVm7GGZa8iaWxN1MIfm7xHwwhSSrSBoO/g==", "annotation_id"=>"6", "id"=>"24"}

Upvotes: 0

Views: 851

Answers (1)

max
max

Reputation: 101871

When you pass a record to link_to, form_for or redirect_to rails passes the record to the polymorphic route helpers (note that this has nothing to do with polymorphic associations).

To generate a route to a nested resource you need to pass both the parent and child record:

simple_form_for( [@tag.taggable, @tag],  # ...

link_to( @tag.content, [@tag.taggable, @tag] )

redirect_to( [@tag.taggable, @tag] )

From your controller you don't need to do:

def tagable_path(tagable)
  case tagable
  when Document
    document_path(tagable)
  when Annotation
    annotate_path(tagable)
  else
    fail 'Unknown tagable'
  end
end

Just redirect_to taggable and rails will figure out the route for you by using the clever conventions.

Nesting is not always good.

Member routes do not need to be nested. Since each record be accessed by a unique id you can unnest the member routes:

# avoids duplication
concern :taggable do
  resources :tags, only: [:new, :index, :create]
end

# generates GET|PATCH|DELETE /tags/:id and /tags/:id/edit
resources :tags, only: [:show, :edit, :destroy, :update]

resources :documents, concerns: :taggable
resources :annotations, concerns: :taggable

The resources :annotations, shallow: true option gives somewhat similar results.

This means that you can do redirect_to(@tag) or link_to('Delete tag', @tag, method: :delete )

Upvotes: 1

Related Questions