Trying to look up comment through an ID, but failing

I am trying to allow users to reply through comments by allowing users
to click a reply link next to the parent comment. It will send in a
parameter to the 'new' comment view, like so:

  > <%= link_to "Reply", new_comment_path(:in_reply_to => comment.id) %>

The form will use the :in_reply_to parameter in order to save that id as
the parent id, like so:

comments_controller#new:
    @comment_parent = Comment.find(params[:in_reply_to])

comments_form view:
  <%= form_for([@comment]) do |f| %>
    <%#= render 'shared/error_messages', :object => f.object %>
    <div class="field">
      <%= f.label :title %><br />
      <%= f.text_field :title %>
    </div>
    <div class="field">
      <%= f.label :content %><br />
      <%= f.text_area :content %>
    </div>
      <%= f.hidden_field :parent_id, :value => @comment_parent.id %>
    <div class="actions">
      <%= f.submit "Post Comment" %>
    </div>
  <% end %>

So, in the create controller, I want to create a new comment based on
the parameters, and then find the parent comment by looking up the
parent_id in Comment.find, but it cannot find any comment with that ID.

comments_controller#create
      @comment = Comment.new(params[:comment])
      @comment.user_id = current_user.id
      @comment.save!
      @comment_parent = Comment.find(params[:parent_id]) # cannot find a
comment with this ID?
      @comment_parent.children << @comment
      if @comment.save
        flash[:success] = "Comment saved."
        @comment_parent.save
        redirect_to @comment
      else
        flash[:error] = "Error in creating comment."
  # @comments = @commentable.comments.paginate(:page =>
params[:page])
        render 'new'

I get this error: Couldn't find Comment without an ID, from this line:
      @comment_parent = Comment.find(params[:parent_id])

I also try @comment_parent = Comment.find(@comment.parent_id), but I get
the same error.

Thank you.

Kelp Kelp wrote:

comments_controller#new:
    @comment_parent = Comment.find(params[:in_reply_to])

Why not just:
   @comment = Comment.new
   @comment.parent_id = params[:in_reply_to] if params[:in_reply_to]

    </div>
      <%= f.hidden_field :parent_id, :value => @comment_parent.id %>

and skip the :value assignment in the view, the value is already there

and you shouldn't need any of this...

      @comment_parent = Comment.find(params[:parent_id])
      @comment_parent.children << @comment

Unless a day spent in WCF has corrupted my brain too much...

place a hidden field in the form, add link_to_function and change the value of the hidden field with javascript.

In the controller if the value of the hidden fill is empty the comment has no parent, else it does.

example with jquery

$("#the_form [type=hidden]").val("<%=comment.id%>")

this will put the id value of the selected comment in the form parent hidden field

Nice, is there a way that I can save the particular page from which the
user is replying to, so I can redirect him there afterwards?

In my show controller for my comments, I have this:
@commentable = find_commentable
where commentable can be article, profile, etc.

Would I do the exact same thing to pass it all throughout the comment
posting process, or is there a more efficient way to do this?

just put

redirect_to :back

at the end of the create

radhames brito wrote:

just put

redirect_to :back

at the end of the create

Hi,
When I try that with a threaded comment, it runs into an error:
Cannot redirect to nil!

I am using a polymorphic association for my articles,profiles and
comments. My comments controller marks the articles and profiles as
commentable, but I have to take a different approach when it comes to my
comments.

oh , i forgot you are doing polymorphic associations sorry,

redirect_to polymorphic_path(@commentable)

remember to catch @commentable with the find_commentable method

you can find it iterating the params hash or user a context_Type in the route , i like that last one more.

radhames brito wrote:

oh , i forgot you are doing polymorphic associations sorry,

redirect_to polymorphic_path(@commentable)

remember to catch @commentable with the find_commentable method

you can find it iterating the params hash or user a context_Type in the
route , i like that last one more.

Do you know if there is anyway for my find_commentable method to treat a
comment the same way as an article?

So, when a user tries to reply to a comment, the URL looks like this:
http://localhost:3000/comments/new?in_reply_to=56

When a user replies to an article (or profile), it looks like this:
http://localhost:3000/articles/304

The find_commentable way only works for the second method URL, since it
cannot find a commentable in the first one.

Sorry, this is my first Rails application, so my code is very cluttered,
and I don't think I thought everything through the first time.

Here's my comments controller:
http://gist.github.com/585167

radhames brito wrote:

oh , i forgot you are doing polymorphic associations sorry,

redirect_to polymorphic_path(@commentable)

remember to catch @commentable with the find_commentable method

you can find it iterating the params hash or user a context_Type in the

route , i like that last one more.

Do you know if there is anyway for my find_commentable method to treat a

comment the same way as an article?

So, when a user tries to reply to a comment, the URL looks like this:

http://localhost:3000/comments/new?in_reply_to=56

for this you will need to create another controller otherwise it will get messy
and you will have to catch the owner of the first comment. One thing you should understand is that
you keep trying to treat comments as if they were not associated and as if they were, let me explain.

comments belong to commentable, and you created this route

articles :has_many :comments

this route is nesting commentes like this

/articles/1/comments

this is to help you get the parent of the comment, the params hash will catch that id after the articles and create a key called

articles_id, take a look at the find_commentable function

def find_commentable
  params.each do |name, value|
if name =~ /(.+)_id$/
      return $1    .classify.constantize.find(value)
end
  end
  nil
end

For each value in the params hash,( note that the params hash has this format, :id=>“1”, this is , name=>value), it uses a regular
expression to see if the name ends with _id, hoping that only the commentable object will end like this i then takes the first part of

the name( thats what $1 does if there are more (.+) in the regular expresion you refer to them in order, $2 , $3 etc), in case of article_id it will take article , then with constantize it becomes Article and a find is perform passing as an id the value corresponding to

article_id, so it return the instance that has those comments as childs.

now lets say you try to do this without

articles :has_many :comments

you have to provide the parent object somehow , if you access http://localhost:3000/comments/new?in_reply_to=56

who does that object belongs to really when you access /articles/1/comments, active record will see that the comments created
that way have no commetable_type and no commetable_id, those are the 2 fields AR uses for the polymorphic associations.

What you are trying yo do is easy, but is not the rigth way , you have to have comments belong to commentable and to themself
then on the commetable show iterate the comments that have no parent . inside the iteration iterate for their children.

radhames brito wrote:

What you are trying yo do is easy, but is not the rigth way , you have
to
have comments belong to commentable and to themself

  belongs_to :commentable, :polymorphic => true
  has_many :comments, :dependent => :destroy, :as => :commentable

Is this fine inside of my comment.rb model?

then on the commetable show iterate the comments that have no parent .
inside the iteration iterate for their children.

What does this accomplish? I find the comments that have children, but
what for?

is the comment form in the commentable show view , for example, comments are displayed at the bottom of the the article, and is the same with any commentable, you dont need this

def new

@commentable = find_commentable <===== is no needed here

@comment = Comment.new <====== this is ok

@comment.parent_id = params[:in_reply_to] if params[:in_reply_to] <==== where is params[:in_reply_to] coming from?

end

since the user will never go there, that is the new view .

def create

@commentable = find_commentable <=== as explained returns @article or whatever from the /article/1 path

if @commentable.nil? # Threaded Comments <==== this will never happen under normal circumstances

@comment = Comment.new(params[:comment]) <==== you are creating orphan comments and will never be able to read them

else

@comment = @commentable.comments.build(params[:comment]) < ===== this is ok

end

@comment.user_id = current_user.id <=========== this is ok , from here to the end of create

if @comment.save

flash[:success] = “Comment saved.”

redirect_to polymorphic_path(@commentable)

else

flash[:error] = “Error in creating comment.”

@comments = @commentable.comments.paginate(:page => params[:page])

render ‘new’

end

end

now the destroy action

def destroy

@commentable = find_commentable <== no need to find the parent you are going to delete it

if @commentable.comments.find(params[:id]).destroy <== no , change this to @comment = Comment.find(params[:id])

flash[:success] = “Comment deleted.”

else

flash[:error] = “Comment could not be deleted.”

end

redirect_to @commentable

end

change the above to

def destroy
@comment = Comment.find(params[:id])

if @comment.destroy

flash[:success] = “Comment deleted.”

else

flash[:error] = “Comment could not be deleted.”

end

redirect_to @commentable

end

the edit

def edit

@commentable = find_commentable

@comment = @commentable.comments.find(params[:id])

@title = “Edit Comment”

The following code allows editing directly on the article page.

@comments = @article.comments.paginate(:page => params[:page])

render ‘articles/show’

end

same as the new , is you are editing at he show of the comentable the user will never go to the comments edit page.

now the update

def update

@commentable = find_commentable

@comment = @commentable.comments.find(params[:id])

if @comment.update_attributes(params[:comment])

flash[:success] = “Updated Comment”

redirect_to @commentable

else

flash[:error] = “Comment update failed.”

@comments = @commentable.comments.paginate(:page => params[:page])

render ‘edit’

end

end

this one is ok but pay attention to render ‘edit’, if the user will never go there.

and last

def correct_user

@commentable = find_commentable

redirect_to(@commentable) unless current_user.admin?

end

try using an authorization gem instead

by parent i mean commets where the parent_id is nil, not comments wherre comentable is nil,

Comment.rb is ok but you still need to self refer, to create the threads.

ill tell you what. i will make this tomorrow and send it to you ill try to commented well, do you want it in rails 2 or 3?

radhames brito wrote:

is the comment form in the commentable show view , for example,
comments
are displayed at the bottom of the the article, and is the same with any
commentable, you dont need this

Yes, right now. The article show page renders a partial of a comment
form.

def new
    @commentable = find_commentable <===== is no needed
here
    @comment = Comment.new <====== this is ok
    @comment.parent_id = params[:in_reply_to] if
params[:in_reply_to] <==== where is params[:in_reply_to]
coming
from?
  end

My article's show page renders a partial for comments:
<%= render 'shared/comments' %>

Here is what the comments looks like:
<% unless @comments.nil? || @comments.empty? %>
    <%= render :partial => 'shared/comment', :collection => @comments %>
    <%#= will_paginate @comments %>
<% end %>

And each comment has this link which is where the params[:in_reply_to]
comes from:
  > <%= link_to "Reply", new_comment_path(:in_reply_to => comment.id) %>

def create
    @commentable = find_commentable <=== as explained
returns
@article or whatever from the /article/1 path
    if @commentable.nil? # Threaded Comments <==== this will
never
happen under normal circumstances
      @comment = Comment.new(params[:comment]) <==== you are
creating
orphan comments and will never be able to read them
    else
      @comment = @commentable.comments.build(params[:comment])
<
===== this is ok
    end
    @comment.user_id = current_user.id
<=========== this is ok , from here to the end of create
    if @comment.save
      flash[:success] = "Comment saved."
      redirect_to polymorphic_path(@commentable)
    else
      flash[:error] = "Error in creating comment."
# @comments = @commentable.comments.paginate(:page => params[:page])
      render 'new'
    end
  end

now the destroy action

def destroy
    @commentable = find_commentable <== no
need to find the parent you are going to delete it
    if @commentable.comments.find(params[:id]).destroy <== no ,
change this to @comment = Comment.find(params[:id])
      flash[:success] = "Comment
deleted."
    else
      flash[:error] = "Comment could not be deleted."
    end
    redirect_to @commentable
  end

change the above to

def destroy
  @comment = Comment.find(params[:id])
     if @comment.destroy
       flash[:success] = "Comment
deleted."
     else
      flash[:error] = "Comment could not be deleted."
    end
    redirect_to @commentable
  end

the edit

def edit
    @commentable = find_commentable
    @comment = @commentable.comments.find(params[:id])
    @title = "Edit Comment"
# The following code allows editing directly on the article page.
# @comments = @article.comments.paginate(:page => params[:page])
# render 'articles/show'
  end

same as the new , is you are editing at he show of the comentable the
user
will never go to the comments edit page.

now the update

def update
    @commentable = find_commentable
    @comment = @commentable.comments.find(params[:id])
    if @comment.update_attributes(params[:comment])
      flash[:success] = "Updated Comment"
      redirect_to @commentable
    else
      flash[:error] = "Comment update failed."
      @comments = @commentable.comments.paginate(:page => params[:page])
      render 'edit'
    end
  end

this one is ok but pay attention to render 'edit', if the user will
never
go there.

and last

def correct_user
      @commentable = find_commentable
      redirect_to(@commentable) unless current_user.admin?
    end

try using an authorization gem instead

Sorry about the edit and destroy methods. You can ignore them for now. I
was going to change them after I learned how the create and new methods
worked. Don't worry about the edit and destroy methods.

I am using Rails 3.

ill try to have it ready by tomorrow what is going to delay me is that i will try to comment everything so that you can understand.

it will be simple just to demonstrate how to create threaded comments, one articles model and one proyects model, comments will belong to both, and will be threaded.

ill will throw in some ajax for your liking

ok its done , tomorrow ill add ajax and the comments

can you give me your email to send you the proyect?

i dont have and about at github yet

try this?
def create
  # ...
  redirect_to request.env['HTTP_REFERER']
  # ...

he is testing and some times is [‘HTTP_REFERER’] empty that why he gets an error with :back

http://github.com/rbritom/Simple_polymorphic_nested_comments

In the readme i explain a bit of where im at with this , i think is ok for now but i will add comments to the code

radhames brito wrote:

http://github.com/rbritom/Simple_polymorphic_nested_comments

In the readme i explain a bit of where im at with this , i think is ok
for
now but i will add comments to the code

Wow, thank you so much for spending your time to do this.

hum ? i didnt see any more replays from you so i stopped adding features to it. I was going to add the ajax and the rest of the comment right away , it was just last night that i thought maybe it was because you where replaying from your job and that during the weekend you could not reply that i havent heard anything from you. If you are still interest ill will finish right away and add the ajax and the rest of the comment.