Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails recurive view strange behaviour with object association

I have a table of comments that is associated with itself for replies. Basically, a comment that has a parent_id is also a reply to his parent comment.

To render this, I use a recursive view, very simple that worked in the past, but is not working with rails 3.2.0 and ruby 2.1.1

Here is simplified code:

<% x = comment.replies %>
<%= comment.id %>; <%= comment.class %><br/>
<%= comment.replies.class %><br><br>
<hr>
<br><br>
<% if x and x.is_a?(Array) %>
  <%= render :partial => "/_redesign/entry/comment", :collection => x, :as => :comment%>
<% end %>

Output is:

349223; Comment
Array
349229; Comment
Comment

At second iteration, comment.replies is a Comment, not an Array, and all falls down from there.

But, if I change the first line and add a reload:

<% x = comment.reload.replies %>

all starts working, output is:

349223; Comment
Array
349229; Comment
Array
349230; Comment
Array

I would love to understand what is going on here and how can the association to return a single object instance instead of a list of them and why is working with reload.

Added the model code:

class Comment < Response
  acts_as_deactivatable :dependencies => [:community_news_feed_items]

  has_many :replies, :class_name=>"Comment", :foreign_key=>"referring_c_id", :order=>"date ASC"

  belongs_to :parent_comment, :class_name=>"Comment", :foreign_key=>"referring_c_id"
end
like image 453
Eduard Avatar asked May 28 '14 14:05

Eduard


1 Answers

I think the issue is that when you enter the scope of the comment reply you've added an ActiveRecord::Association::CollectionProxy as a middleman between the original comment and the comment response. It says,

The @reflection object represents a :has_many macro.

Which would follow from your use of :has_many in the model code. In Rails 4, at least, the CollectionProxy contains a cache on a collection of objects that are associated via :has_many. This example from the documentation outlines that caching / reloading process.

I did a quick search for "cache" on the CollectionProxy source for both Rails 4 and Rails 3.2, and it isn't mentioned in the Rails 3.2 documentation. But it could have been implemented before the documentation was changed.

Also, maybe this is ideological, but using #is_a? could be avoided. You could just use :respond_to?(:each), so that you can use any sort of Enumerable in there.

EDIT! Check it out, from the CollectionProxy source:

# This class has most of the basic instance methods removed, and delegates
# unknown methods to <tt>@target</tt> via <tt>method_missing</tt>. As a
# corner case, it even removes the +class+ method and that's why you get
#
#   blog.posts.class # => Array
#
# though the object behind <tt>blog.posts</tt> is not an Array, but an
# ActiveRecord::Associations::HasManyAssociation.
#
# The <tt>@target</tt> object is not \loaded until needed. For example,
#
#   blog.posts.count

It looks like comment.replies is actually just giving you a HasManyAssociation, rather than an actual object. Since CollectionProxy is new in Rails 3.1, that's probably your problem.

like image 143
acsmith Avatar answered Nov 11 '22 02:11

acsmith