How to implement a Counter Cache in Rails?
Asked Answered
F

3

5

I have a posts controller and a comments controller. Post has many comments, and comments belong to Post. The associate is set up with the counter_cache option turned on as such:

#Inside post.rb
has_many :comments

#Inside comment.rb
belongs_to :post, :counter_cache => true

I have a comments_count column in my posts table that is defaulted to zero, as such:

add_column :posts, :comments_count, :integer, :default => 0

In the create action of my comments controller, I have the following code:

def create
  @posts = Post.find(params[:post_id])
  @comment = @post.comments.build(params[:comment])
  if @comment.save
    redirect_to root
  else
    render :action => 'new'
  end
end

My problem: when @comment.save is called, I get the following error:

ArgumentError in CommentsController#create

wrong number of arguments (2 for 0)

Removing :counter_cache => true from comment.rb completely solves the problem, so I'm assuming that it is the cause of this vague error. What am I missing here? How can I save my comment and still have rails take care of my counter_cache for my post?

Thanks!


Update - Application trace attached:

/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:987:in `update_all'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:987:in `update_counters_without_lock'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/locking/optimistic.rb:176:in `update_counters'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:1006:in `increment_counter'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations.rb:1367:in `belongs_to_counter_cache_after_create_for_feed_entry'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:178:in `send'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:178:in `evaluate_method'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:166:in `call'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:93:in `run'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:92:in `each'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:92:in `send'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:92:in `run'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/callbacks.rb:276:in `run_callbacks'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/callbacks.rb:344:in `callback'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/callbacks.rb:267:in `create'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2915:in `create_or_update_without_callbacks'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/callbacks.rb:250:in `create_or_update'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2573:in `save_without_validation'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/validations.rb:1090:in `save_without_dirty'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/dirty.rb:79:in `save_without_transactions'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:229:in `send'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:229:in `with_transaction_returning_status'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:182:in `transaction'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:228:in `with_transaction_returning_status'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:196:in `save'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:208:in `rollback_active_record_state!'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/transactions.rb:196:in `save'
/Users/yuval/Sites/rails/blog/app/controllers/comments_controller.rb:6:in `create'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:1331:in `send'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:1331:in `perform_action_without_filters'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/filters.rb:617:in `call_filters'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/filters.rb:610:in `perform_action_without_benchmark'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/core_ext/benchmark.rb:17:in `ms'
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/core_ext/benchmark.rb:17:in `ms'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/rescue.rb:160:in `perform_action_without_flash'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/flash.rb:151:in `perform_action'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:532:in `send'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:532:in `process_without_filters'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/filters.rb:606:in `process'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:391:in `process'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/base.rb:386:in `call'
/Library/Ruby/Gems/1.8/gems/actionpack-2.3.8/lib/action_controller/routing/route_set.rb:438:in `call'

Notice the only line that doesn't have to do directly with the Rails framework is:

/Users/yuval/Sites/rails/blog/app/controllers/comments_controller.rb:6:in `create'

Line 6 is the following:

if @comment.save

EDIT 2: the @posts/@post is a typo on my end when writing a simplified example for StackOverflow, my actual application doesn't have mistake.

Inspecting @comment, it seems to be completely valid - it returns back exactly what I expect it to - a new instance of comment with whatever information I passed to it with build. It only blows up when the save method is called on it.

Thank you for all the suggestions so far. Any other ideas?

Fibrin answered 2/6, 2010 at 7:43 Comment(7)
Can you post at least part of application trace? It will be helpful to see where error actually occurs (i.e. which method is called with 2 arguments instead of 0).Advocation
updated. looks like it's the save method...Fibrin
In your backtrace, there are indication about a Feed model. It's not with it. the problem ? /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations.rb:1367:in belongs_to_counter_cache_after_create_for_feed_entry'`Sharice
updated to reflect the comments/suggestions.Fibrin
So this is no longer a bounty question?Ungula
Thank you for all the effort so far. It looks like the bounty on it ended... but I still didn't get an answer...Fibrin
I recently discovered the gem counter_culture and added it to my project. It works good as far as I can tell.Chide
U
9

I ran through a sample rails app with your code and it all worked fine for me.

I would suggest debugging a little more like hurikhan77 is suggesting and see if it is just the @posts / @post issue that dain suggested.

Also, try creating a post and a comment in the console with some very simple content to see if it is working.

$ ruby script/console

# add whatever fields are necessary to create     
> @p = Post.create(:title => "TestPost1")
  # => #<Post id: 3, ...

# again, add whatever is necessary to create
> @c = @p.comments.create(:comment => "TestComment1")
  # => #<Comment id: 8, ...

> Post.find(:last).comments_count
  # => 1

See what that gets you.

/ JP

Ungula answered 11/6, 2010 at 17:55 Comment(0)
A
2

This might be completely off the mark, but you use @posts then @post?

Arvad answered 9/6, 2010 at 10:55 Comment(0)
B
2

ArgumentError in CommentsController#create

wrong number of arguments (2 for 0)

You get this error because @comment is not the object you expect. Try to debug this by inserting:

logger.debug @comment.inspect

You'll see something unexpected and it should raise at least an eyebrow. This should be the clou that you assigned the Post.find(...) to @posts but later tried to work with @post.

Biometry answered 11/6, 2010 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.