Andrew Lauer Barinov
Andrew Lauer Barinov

Reputation: 5754

Utilizing rails helper and controller methods

Suppose I have a Rails app that deals with Posts and Comment objects. A Post has_many Comments and each Comment belongs_to a Post.

Each Comment has a word_count property. The Post object has an average_comment_word_count property which is an average of each of the Comment's word_count.

First question is if the Post object gets modified asynchronously (comments get added which affects the average word count), at what point should I recalculate the property? When the object is returned? Or each time a new comment is added? Does it go into the comment or post helper methods? Which controller function should call this method?

Also when I include the following Post helper method, I get a NULL value returned as JSON.

def average_word_count
  @average_word_count = 0
  # current_user returns the current user object
  # user has_many posts and each post belongs_to a user
  current_user.posts.find(params[:id]).comments.each do |comment|
        @average_word_count += comment.word_count / current_user.posts.find(params[:id]).comments.count
  end

  @average_word_count
end

Upvotes: 0

Views: 335

Answers (2)

Mori
Mori

Reputation: 27779

class Comment < ActiveRecord::Base
  belongs_to :post

  after_save :update_post_word_count

  def update_post_word_count
    average_wc = post.comments.average(:word_count)
    post.update_attributes average_comment_word_count: average_wc
  end      
end

Or, derive it only when you need it:

class Post < ActiveRecord::Base
  has_many :comments

  def average_comment_word_count
    comments.average :word_count
  end
end

Or, if it's just used once somewhere with low traffic, brazenly flout the Law of Demeter and just calculate it as needed from a post object:

Average Comment Word Count: <%= @post.comments.average :word_count %>

Update: As @coreward notes, the first part of this answer isn't useful for asynchronous updates, but the rest of the answer may still be helpful.

Upvotes: 2

coreyward
coreyward

Reputation: 80041

You would be a lot better off just building a custom counter cache based on what's already in ActiveModel that keeps track of the total number of words, then just count comments to do math manually.

# you need a comments_count column and a words_count column in this table
class Post < ActiveRecord::Base
  has_many :comments

  def avg_words_per_comment
    words_count / comments_count
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post, :counter_cache => true
  after_save { update_counters(post.id, :words => word_count }
  before_destroy { update_counters(post.id, :words => -word_count }
end

# And in your view:

<p> 
  The average comment for this post has <%= @post.avg_words_per_comment %> words.
</p>

Then you don't need to worry about asynchonicity and the calculation on view is minimal.

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/counter_cache.rb#L65

Upvotes: 1

Related Questions