Как реализовать Counter Cache в Rails?

У меня есть контроллер сообщений и контроллер комментариев. Пост имеет много комментариев, и комментарии принадлежат Посту. Ассоциат настроен с включенной опцией counter_cache:

#Inside post.rb
has_many :comments

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

У меня есть столбец comments_count в моей таблице posts, который по умолчанию равен нулю, как таковой:

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

В действии create моего контроллера комментариев у меня есть следующий код:

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

Моя проблема: когда вызывается @comment.save, я получаю следующую ошибку:

ArgumentError in CommentsController#create

wrong number of arguments (2 for 0)

Удаление :counter_cache => true из comment.rb полностью решает проблему, поэтому я предполагаю, что это является причиной этой неопределенной ошибки. Что мне здесь не хватает? Как я могу сохранить свой комментарий и при этом заставить rails позаботиться о моем counter_cache для моего сообщения?

Спасибо!


Обновление - трассировка приложения прилагается:

/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'

Обратите внимание на единственную строку, которая не имеет прямого отношения к фреймворку Rails:

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

Строка 6 следующая:

if @comment.save

EDIT 2: @posts/@post — это опечатка с моей стороны при написании упрощенного примера для StackOverflow, в моем реальном приложении нет ошибок.

Проверяя @comment, он кажется полностью действительным - он возвращает именно то, что я ожидаю - новый экземпляр комментария с любой информацией, которую я передал ему с build. Он взрывается только тогда, когда на нем вызывается метод save.

Спасибо за все предложения. Любые другие идеи?


person Yuval Karmi    schedule 02.06.2010    source источник
comment
Можете ли вы опубликовать хотя бы часть трассировки приложения? Будет полезно увидеть, где на самом деле возникает ошибка (т.е. какой метод вызывается с 2 аргументами вместо 0).   -  person Slobodan Kovacevic    schedule 02.06.2010
comment
обновлено. похоже, это метод сохранения...   -  person Yuval Karmi    schedule 02.06.2010
comment
В вашей трассировке есть указание на модель канала. Это не с этим. проблема ? /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'`   -  person shingara    schedule 11.06.2010
comment
обновлен, чтобы отразить комментарии/предложения.   -  person Yuval Karmi    schedule 12.06.2010
comment
Так это уже не вопрос о награде?   -  person Joshua Pinter    schedule 12.06.2010
comment
Спасибо за все усилия. Похоже, награда за него закончилась... но ответа я так и не получил...   -  person Yuval Karmi    schedule 14.06.2010
comment
Недавно я обнаружил гем counter_culture и добавил его в свой проект. Насколько я могу судить, это работает хорошо.   -  person JJD    schedule 17.08.2012


Ответы (3)


Я прогнал образец приложения rails с вашим кодом, и у меня все сработало.

Я бы предложил отладить немного больше, как предлагает hurikhan77, и посмотреть, не является ли это просто проблемой @posts / @post, которую предложил dain.

Кроме того, попробуйте создать пост и комментарий в консоли с очень простым содержимым, чтобы проверить, работает ли он.

$ 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

Посмотрите, что это вам даст.

/ JP

person Joshua Pinter    schedule 11.06.2010

Это может быть совершенно неуместно, но вы используете @posts, а затем @post?

person dain    schedule 09.06.2010

ArgumentError в CommentController#create

неправильное количество аргументов (2 вместо 0)

Вы получаете эту ошибку, потому что @comment не является ожидаемым объектом. Попробуйте отладить это, вставив:

logger.debug @comment.inspect

Вы увидите что-то неожиданное и это должно поднять как минимум бровь. Это должно быть облако, которое вы назначили Post.find(...) @posts, но позже пытались работать с @post.

person hurikhan77    schedule 11.06.2010