ruby-on-railsvotecounter-cache

Rails: How to increment counter_cache?


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

::: SOLVED :::

Was able to figure out counter_cache on the Vote model and use it in Topics. Updated question below. Once the Topic model had a votes_count, all I had to do was put that in the view and update my counting in the controllers. Turned out to be pretty simple. However finding information was not, so this should help others out, in all its confusing glory. Go way way down for the answer.

Cheers

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:::EDIT:::

:::See below for history:::

This is an edit to the original post. I'm really close with what I have, before I try a gem solution. I used a comment_cache on the vote model but now I don't know how to increment the cache.

class Vote < ActiveRecord::Base
    belongs_to :topic, :counter_cache => true
end

and called topic.votes.size in the view and it now shows the count without an Active Record enumerable.

<td><%= pluralize(topic.votes.size, "vote") %></td>
        <td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
        <td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>

index.html.erb

Listing Topics

Title   
test    1 vote  [+1] [-1]   Delete

New Topic

But comment_cache doesn't allow negative numbers. I'm still using create/destroy on the records rather than adding and subtracting a number to the count. How to increment and decrement? Those methods say I'm missing values. Currently it makes sense @topic.votes.last.destroy fails in that there are no more votes to destroy thus the tally cannot be less than zero.

How to setup so that I'm incrementing/decrementing the comment_cache value such that negative numbers are possible? I tried @topic.votes.increment and @topic.votes.increment_counter but they ask for 2 params that I'm not sure what to use. Tried

@topic.increment_counter(:votes_count, params[:id])

but no luck.

NoMethodError in TopicsController#upvote
undefined method `increment_counter' for #<Topic:0x007f87d974a4b0>

Extracted source (around line #433):
431
432
433
434
435
436

      else
        match = match_attribute_method?(method.to_s)
        match ? attribute_missing(match, *args, &block) : super
      end
    end

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:::HISTORY BELOW THIS LINE:::

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Revisiting Rails again and I'm trying to do a simple vote counter on topics. What I want to do is increment/decrement the tally (integer) column in the votes record.

Topic has_may :votes, and Vote belongs_to :topic

In the topics_controller.rb I've used

def upvote
    @topic = Topic.find(params[:id])
    @topic.votes.increment_counter(:tally, params[:id])
    redirect_to(topics_path)
  end

  def downvote
    @topic = Topic.find(params[:id])
    @topic.votes.decrement_counter(:tally, params[:id])
    redirect_to(topics_path)
  end

But I get an interesting ActiveRecord key instead of a value in the view instead of the count of votes.

index.html.erb:

from the view
<td><%= pluralize(topic.votes(:tally), "vote") %></td>
<td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
<td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>

result of view
    test    #<Vote::ActiveRecord_Associations_CollectionProxy:0x007fbb16451f68> votes   

        Delete

From Rails Console though the increment doesn't show up:

  2.1.5 :022 > Vote.first
  Vote Load (0.5ms)  SELECT  "votes".* FROM "votes"  ORDER BY "votes"."id" ASC LIMIT 1
 => nil 

2.1.5 :023 > Vote
 => Vote(id: integer, topic_id: integer, created_at: datetime, updated_at: datetime, tally: integer) 

I think I've setup the routes.rb ok but the double do I'm not sure about

Rails.application.routes.draw do
  resources :topics do
    member do
      post 'upvote'
      post 'downvote'
    end
  end

  root 'topics#index'  
end

Anyway I think it's either the wrong method to increment or something I'm missing. Any help appreciated. Cheers


Solution

  • ::SOLVED::

    I was able to fix the problem using counter_cache in the Vote model. Voting is now possible with both positive and negative values. Hooray!

    Added a new column in topic.

    class AddVotesCount < ActiveRecord::Migration
      def change
        add_column :topics, :votes_count, :integer, :default => 0
    
      end
    end
    

    Update Vote model

    class Vote < ActiveRecord::Base
            belongs_to :topic, :counter_cache :true
    end
    

    Now in the topics controller I can increment the up/down methods with

    @topic.votes_count += 1
    .
    .
    .
    @topic.votes_count -= 1
    

    I can use @topic.update_attributes but I found it unreliable for some reason so I opted for @topic.save in each method.

    The view now uses just topics.votes_count and works great.

    <td><%= pluralize(topic.votes_count, "vote") %></td>
            <td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
            <td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>
    

    This works without needing a User model and/or the acts_as_votable gem. Although I will probably use a gem in future it was good to figure out how to make this work without it.

    Cheers