Rails 5: Best way to iterate through a polymorphic model and find the top level parent?
<p>Edit: In an effort to remain as transparent as possible and provide a working solution to people in the future I wanted to explain how I used @John Hayes-Reed's answer with my issue. I'll update the methods and models below.</p>

<p>Updated method:</p>

<pre><code>#User.rb
def self.find_all_user_comments(user)
  @comment_hash = []
  @user_comments = Comment.where(user_id: user.id)
  @user_comments.all.each do |co|
     post = co.find_parent_post
    @comment_hash.push( { comment: co, post: post} )
  end
  @comment_hash
end
</code></pre>

<p>New method I got from the answer below:</p>

<pre><code>#comment.rb
def find_parent_post
  return self.commentable if self.commentable.is_a?(Post)
  self.commentable.find_parent_post
end
</code></pre>

<p>My controller where I call the method:</p>

<pre><code>#users_controller.rb
def control_panel
  @comments = User.find_all_user_comments(current_user)
end
</code></pre>

<h2>End edit</h2>

<p>I'm trying to implement a "most recent comments" for a user in their control panel. A comment can be left on a <code>Post</code> or another <code>comment</code>.</p>

<p>In my view I want to display the <code>:body</code> of the <code>comment</code> along with the <code>post</code> it was created in via a hash. That goes for a <code>comment</code>s <code>comment</code> as well. Luckily, Comment's can only go two deep like so</p>

<pre><code>Post
  Comment
    Child Comment # Would like to find the parent post.
    Child Comment
  Comment
</code></pre>

<p>I had a method, which I will input below, which worked well for <code>comment</code>s that were left on a post, but would break when trying to find a <code>comment</code> had a <code>commentable_type: "Comment"</code>.</p>

<p>My only solution would be to have an <code>if</code> statement that said <code>if commentable_type == "Comment"</code>, find <em>that</em> Comments parent, grab the Post, then take the Post` and bring it back down to that child comment and push it into the hash that way.</p>

<p>I was wondering if there is a more efficient way (either AR or SQL) to do such a thing so that in the end, I can find the <code>comment</code>'s parent <code>Post</code> is in even if it's a <code>comment</code> of a <code>comment</code>.</p>

<p>Here is all the information that I think will help:</p>

<p>Schema:</p>

<pre><code>create_table "comments", force: :cascade do |t|
  t.text     "body"
  t.datetime "created_at",       null: false
  t.datetime "updated_at",       null: false
  t.integer  "commentable_id"
  t.string   "commentable_type"
  t.integer  "user_id"
end

create_table "forums", force: :cascade do |t|
  t.string   "name"
  t.text     "description"
  t.integer  "user_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.index ["user_id"], name: "index_forums_on_user_id", using: :btree
end

create_table "posts", force: :cascade do |t|
  t.string   "title"
  t.text     "description"
  t.integer  "user_id"
  t.integer  "forum_id"
  t.datetime "created_at",  null: false
  t.datetime "updated_at",  null: false
  t.index ["forum_id"], name: "index_posts_on_forum_id", using: :btree
  t.index ["user_id"], name: "index_posts_on_user_id", using: :btree
end

...User table omitted for brevity...
</code></pre>

<p>Models: </p>

<pre><code>#post model
belongs_to :user
belongs_to :forum

has_many :comments, as: :commentable

#comment model
belongs_to :user
belongs_to :commentable, polymorphic: true
has_many :comments, as: :commentable
</code></pre>

<p>Method as it stands now. Wont work when it finds a comment with <code>commentable_type: "Comment"</code>:</p>

<pre><code>  def self.find_all_user_comments(user)
    @comment_hash = []
    @user_comments = Comment.where(user_id: user.id)
    @user_comments.all.each do |co|
      post = Post.find(co.commentable_id)
      @comment_hash.push( { comment: co, post: post} )
    end
    @comment_hash
  end
</code></pre>

<p>View:</p>

<pre><code>&lt;div class="col-md-10 col-md-offset-1"&gt;
&lt;h3&gt;Recent comments:&lt;/h3&gt;
  &lt;% if @comments.any? %&gt;
  &lt;table class="table"&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Comment&lt;/th&gt;
        &lt;th&gt;Post&lt;/th&gt;
        &lt;th&gt;Actions&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;% @comments.each do |co| %&gt;
        &lt;tr class="comment-&lt;%= co[:comment].id %&gt;"&gt;
          &lt;td&gt;&lt;%= truncate(co[:comment].body, length: 50) %&gt;&lt;/td&gt;
          &lt;td&gt;&lt;%= truncate(co[:post].title, length: 50) %&gt;&lt;/td&gt;
          &lt;td&gt;&lt;%= link_to "destroy", comment_path(co[:comment]), remote: true, method: :delete, data: {confirm: "Are you sure you want to delete this comment?"} %&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;% end %&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
  &lt;% end %&gt;
&lt;/div&gt;
</code></pre>