2
votes

I am having troubles with a polymorphic association in Rails. I have an application where it should be possible to comment on different models, such as Posts, Images, Projects

Right now I just have Posts to comment on. On the start page there is an index view of the latest Posts and each Post has a small Comment form underneath to comment on via Ajax, very much like Facebook.

My models look like this:

class Post < ActiveRecord::Base
  belongs_to :post_category
  belongs_to :user

  has_many :comments, :as => :commentable  

  validates_presence_of :user_id
  validates_presence_of :post_category_id
  validates_presence_of :title
  validates_presence_of :body

end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :commentable, :polymorphic => true
end

Now in my Comments controller I added the following method (I think I took it from railscasts or something), which I assume tries to find out the @commentable dynamically when creating an comment. But this always returns the error undefined methodcomments' for nil:NilClass`

   # find commentable (parent) item
   def find_commentable
      params.each do |name, value|
        if name =~ /(.+)_id$/
           return $1.classify.constantize.find(value) unless name == 'user_id'
        end
      end
      nil
   end

  def create
    @commentable = find_commentable 
    @comment = @commentable.comments.build(params[:comment])
    if @comment.save
      redirect_to @comment, :notice => 'Comment was successfully created.'
      redirect_to :id => nil 
    else
      render :action => "new"
    end
  end

The two things I tried in my partial were:

  1. leaving the commentable info out of the form

    = form_for [@commentable, Comment.new], :remote => true do |f| #new_comment.add_comment = f.hidden_field :user_id, :value => current_user.id = f.text_field :content, :size => 55, :value => 'leave a comment...', :class => 'comment_form' = f.submit "send"

and 2. passing the commentable_id and commentable_type

= form_for [@commentable, Comment.new], :remote => true do |f|
    #new_comment.add_comment
        = f.hidden_field :user_id, :value => current_user.id
        = f.hidden_field :commentable_id, :value => post_id
        = f.hidden_field :commentable_type, :value => 'Post'
        = f.text_field :content, :size => 55,  :value => 'leave a comment...', :onfocus => 'this.select()', :class => 'comment_form'
        = f.submit "send" 

both without luck. Any help would be highly appreciated. the whole comments controller code is in this gist: https://gist.github.com/1334286

2

2 Answers

2
votes

It seems like the commentable is not assigned correctly in the comments controller. This could have multiple reasons. Here is a setup that should work for you:

In the Posts controller, e.g. action "show":

@post = Post.find(params[:id])

In the posts/show view comments form:

= form_for [@post, @post.comments.new], :remote => true do |f|

You should be able to use your comments controller as it - but you should change the render to e.g. a redirect_to :back in the create action since the comments controller will most probably not have a "new" view on its own (it is dependent from the commentable)

Also, make sure that you have nested routes for all resources that can act as a commentable, like so:

resources :posts do
  resources :comments do
end

resources :comments do
  resources :comments # subomments
end

UPDATED to reflect information in the comments

0
votes

Don't use @commentable in the post show view, since it's only defined in the comments controller.

Do this instead:

_comment.html.erb: (the comment partial in the post show view)

  <%= form_for ([comments, @vote]), remote: true do |f| %>

posts/posts_controller.rb:

  <%= form_for ([@post, @vote]), remote: true do |f| %>