0

So I've developed the model associations and a counter cache column as shown in the first answer here: Model association question

Now I want to know, after creating two buttons, one for upvote and downvote, as well as display the counter cache for the number of votes: <%= pluralize video.video_votes.size, 'vote' %> how should I go about creating the controller method/s so that the vote count will be incremented by 1 or decreased by 1? I'm pretty sure I could figure out the AJAX part myself, so I'm primarily curious about initially doing this as a normal HTTP request. (Of course, I wouldn't mind help with the AJAX as well...)

Community
  • 1
  • 1
Justin Meltzer
  • 13,318
  • 32
  • 117
  • 182

2 Answers2

3

Controller

class VideoVotes < ApplicationController
  ...
  def create
    @vote = VideoVote.new(params[:video_vote])
    @vote.user = current_user
    @vote.save
  end

  # OR if you want

  def create
    # you can use params[:video_id] instead of params[:video_vote][:video_id]
    # but you should specify it in your view
    @video = Video.find(params[:video_vote][:video_id])
    @vote = @video.video_votes.new
    @vote.user = current_user
    @vote.save
  end
  ..
end

AJAX magick, /app/viwes/video_votes/create.js.erb
I will use jQuery for that, so check out how to install it

$("#votes").html("Votes: <%= @vote.video.video_votes.size %>")
# or with @video variable
$("#votes").html("Votes: <%= @video.video_votes.size %>")

View:

<%= @video.description or whatever %>
<p id='votes'>Votes: <%= @video.video_votes.size %></p>
<p><%= link_to "+1", video_votes_path('video_vote[video_id]' => @video.id), :method => :post, :remote => true %></p>

That's all. Here can be some mistypes

fl00r
  • 82,987
  • 33
  • 217
  • 237
  • what about the fact that a video has_many video_votes? Would it have to be `@vote = @video.video_votes.new(params[:video_vote]`? – Justin Meltzer Mar 17 '11 at 20:28
  • or is that taken care of in create.js.erb? – Justin Meltzer Mar 17 '11 at 20:29
  • it is the same thing. You can do as `@video = Video.find params[:video_vote][:video_id]; @vote = @video.video_votes.new;` as a way I show – fl00r Mar 17 '11 at 20:30
  • Let's say I want to have a downvote button and method too. How would I do that? – Justin Meltzer Mar 17 '11 at 20:33
  • so counter_cache isn't something you need :) – fl00r Mar 17 '11 at 20:34
  • and do I have to do `@video = Video.find params[:video_vote][:video_id];` before `@vote = @video.video_votes.new;`? – Justin Meltzer Mar 17 '11 at 20:35
  • ehhhmmm? why not? you should specify @video if you want to use it – fl00r Mar 17 '11 at 20:36
  • I was just wondering if `@vote = @video.video_votes.new;` would work without specifying `@video`? – Justin Meltzer Mar 17 '11 at 20:38
  • you have just specified VIDEO in your chain: __@video__.video_votes.new. You should read API for `has_many` association. You have called `video_votes.new` for your `@video` instance, ok? – fl00r Mar 17 '11 at 20:41
  • and could I do `$("#votes").html("Votes: <%= @vote.size %>")` in create.js.erb? – Justin Meltzer Mar 17 '11 at 20:41
  • but didn't we set @vote = @video.video_votes.new in the controller? – Justin Meltzer Mar 17 '11 at 20:47
  • so what? we set @vote. This is instance of VideoVote class. So? – fl00r Mar 17 '11 at 20:52
  • Hmm I guess it's because we don't have the instance variable in create.js.erb? – Justin Meltzer Mar 17 '11 at 20:56
  • Our `@video` (or whatever) is visible from views (and create.js.erb is actually a view) – fl00r Mar 17 '11 at 20:57
  • Right, right! So `@video` is the current video, and `@video.video_votes` is that video's votes in the view! I get it now :) Is `@video.video_votes.size` going to hurt performance if there are a lot of votes? – Justin Meltzer Mar 17 '11 at 21:05
  • @video.video_votes.size will return counter_cache `video_votes_counter` – fl00r Mar 17 '11 at 21:07
  • To have downvotes, I'm thinking of adding another column to the video_votes table named "value" which is set to either 1 or -1 depending on which button is pressed, and then the total number of votes is the sum of that column. Is that a good idea? This would mean getting rid of counter cache. – Justin Meltzer Mar 17 '11 at 21:07
  • this is what ordinary people do :). you're right. but you still save your total vote in video model in additional column and update it every time you add new VideoVote – fl00r Mar 17 '11 at 21:11
  • I'm glad I'm turning from a beginner to a normal programmer :) How and where would I sum up the entire 'value' column in VideoVote and set it equal to the total_vote of that video? – Justin Meltzer Mar 17 '11 at 21:17
  • callback into VideoVote, sum of votes you can put as in Video model and as VideoVote model – fl00r Mar 17 '11 at 21:22
  • I have to put sum of votes in both Video and VideoVote? or can I choose one? – Justin Meltzer Mar 17 '11 at 21:24
  • use DRY princple: Don't Repeat Yourself – fl00r Mar 17 '11 at 21:25
1

The counter cache is incremented when an object of this class is created and decremented when it’s destroyed.

from ruby on rails api

update

@video = Video.find(params[:id])
@video.increment!(:votes_count)
#or
@video.decrement!(:votes_count)
emrahbasman
  • 2,003
  • 10
  • 19
  • Counter cache is usually used for counting existing child objects. But you can always use decrement_counter method – emrahbasman Mar 17 '11 at 20:32
  • It's more like a hack, because @video.video_votes.count will be different to @video.video_votes.size. Not good – fl00r Mar 17 '11 at 20:43
  • I updated the answer. I agree that he doesn't need a counter cache. – emrahbasman Mar 17 '11 at 20:48
  • If I don't use a counter cache, then won't I have to do like `.count` or something which will hurt performance? – Justin Meltzer Mar 17 '11 at 20:57
  • Of course using count decreases performance of your application. What i say is don't use counter_cache magic. Increment or decrement which column you want with the methods that i posted above. You can update column anytime you want. – emrahbasman Mar 17 '11 at 21:17