views:

29

answers:

2

I am following Ryan Bate's tutorial: http://railscasts.com/episodes/163-self-referential-association

But my setup is slightly different.

I am making comments that are self-referential so that comments can be commented on.

The form displays in the view, but when I submit, I get this :

Routing Error

No route matches "/conversations"

And my url says this : http://localhost:3000/conversations?convo_id=1

models

#conversation.rb
belongs_to :comment
belongs_to :convo, :class_name => "Comment"

#comment.rb
belongs_to  :post
has_many :conversations
has_many :convos, :through => :conversations

My form :

- for comment in @comments
  .grid_7.post.alpha.omega
    = comment.text
    %br/
    - form_for comment, :url => conversations_path(:convo_id => comment), :method => :post do |f|
      = f.label 'Comment'
      %br/
      = f.text_area :text
      %br/
      = f.submit 'Submit'

My conversations_controller:

def create
  @conversation = comment.conversations.build(:convo_id => params[:convo_id])

The app fails here in its creation as it never makes it to the redirect portion of the create method.

A: 

The app is failing sooner than you think - it's not finding a route to that action, so it's not reaching it at all. In your routes.rb file, you need to add:

# rails 3
resources :conversations

# rails 2
map.resources :conversations

This should fix it.

Jaime Bellmyer
Its in my routes.rb . But possibly this is occurring because this form is in Post#show . So maybe I need a nested route of some sorts ?
Trip
I just added a different answer, to hopefully address the larger issue of how you're modeling the data. This should simplify things, and get them working for you.
Jaime Bellmyer
+1  A: 

There are several pieces to work on here, but the good news is I think the answer you're looking for is simpler than what you already have. If I understand you correctly, you want comments to have many child comments of their own. This is how YouTube works, letting members reply to existing comments. For this, you don't need the has_many :through solution you've implemented. You don't need the conversations object at all. A comment may have many replies (child comments), but a reply isn't going to have more than one parent.

The answer for this is using polymorphism, which is easier to implement than it is to pronounce :) You want your comments to either belong to a post, or to another comment. Polymorphism lets an object belong to one of possibly many things. In fact, comments are the most common use for this.

I cover polymorphism with the example of addresses in this blog post:

http://kconrails.com/2010/10/19/common-addresses-using-polymorphism-and-nested-attributes-in-rails/

But I can show you how it applies to your case more specifically. First, drop the conversation model/controller/routes entirely. Then, change your comments table:

change_table :comments do |t|
  t.integer :commentable_id
  t.string  :commentable_type

  t.remove :post_id
end

We don't need post_id anymore, because we're going to change how we associate with other tables. Now let's change the models:

# app/models/post.rb
has_many :comments, :as => :commentable

# app/models/comment.rb
belongs_to :commentable, :polymorphic => true
has_many :comments, :as => :commentable

Notice we dropped the comment belonging to a post directly. Instead it connects to the polymorphic "commentable" association. Now you have an unlimited depth to comments having comments.

Now in your Post#show action, you'll want to create a blank comment like so:

get show
  @post = Post.find(params[:id])
  @comment = @post.comments.build
end

@comment will now have commentable_id and commentable_type set for you, automatically. Now in your show page, using erb:

<% form_for @comment do |f| %>
  <%= f.hidden_field :commentable_type %>
  <%= f.hidden_field :commentable_id %>

  /* other fields go here */
<% end %>

Now when Comments#create is called, it works like you'd expect, and attaches to the right parent. The example above was showing a comment being added directly to a post, but the process is essentially the same for a comment. In the controller you'd call @comment.comments.build, and the form itself would stay the same.

I hope this helps!

Jaime Bellmyer
Amazing. Your tips led me Ryan Bates' polymorphic screencast. And I was able to setup <i>almost</i> everything except for being able to comment on comments. I'm using his find_commentables method, and where it works for the parent object of the page, in this case Post, I can't figure out how to write the form_for for comments
Trip
Yeah, that part gets a little tricky, because the user interface needs to be clean. You can't really show a "new comment" form after every comment, so you need to either send the user to a new page (which can use the form code above), or use AJAX to display a form inline.
Jaime Bellmyer
I'm curious how Reddit achieves the same goal. I could probably accomplish it by ajax. But there's got to be a way to set a unique id for each form. ;)
Trip