0

so I'm trying to create a video game review website for practice.

A game has many reviews, and votes. The idea is, in order to post a review, you must vote "Good" or "Bad" first, THEN submit a review. You can't post a text review without voting.

I'm trying to do this without the acts_as_voteable gem...

The data format for votes is boolean. "Good" is true, "Bad" is false.

How do I get the votes to save? below are my routes.rb, _review partial, reviews controller, and show page.

many thanks guys :)

edit****: also I'm trying to only one vote per user. I was thinking of using a token variable which equals to 1, and when a vote is cast, the token is -1. Is that a good approach? But the data type for vote is boolean, so how would that work -- or should I change the data type for vote from boolean to integer?

edit#2 -- so I added :vote into my params.

routes.rb

upvote_game_review_path 
POST    /games/:game_id/reviews/:id/upvote(.:format)    reviews#upvote

downvote_game_review_path   
POST    /games/:game_id/reviews/:id/downvote(.:format)  reviews#downvote

Rails.application.routes.draw do

  devise_for :users

  root "games#index"

  resources :games do
    resources :news
    resources :reviews, except: [:show, :index] do
        member do
            post "upvote"
            post "downvote"
        end
    end
  end

  resources :platforms
  resources :genres

end

reviews_controller.rb

class ReviewsController < ApplicationController

    before_action :set_review, only: [:show, :update, :edit, :destroy]
    before_action :set_game
    before_action :authenticate_user!

    def new
        @review = Review.new
    end

    def create
        @review = Review.new(review_params)
        @review.user_id = current_user.id
        @review.game_id = @game.id

        if @review.save
            redirect_to @game
        else
            render "review"
        end
    end

    def upvote
        @review.vote.create = true
        redirect_to @game
    end

    def downvote
        @review.vote.create
        @review.vote = false
        redirect_to @game
    end

    def edit
        @review.update(review.params)
    end

    def destroy
        @review.destroy
        redirect_to @game
    end

    private

    def set_review
        @review = Review.find(params[:id])
    end

    def set_game
        @game = Game.find(params[:game_id])
    end

    def review_params
        params.require(:review).permit(:comment, :vote)
    end

end

_review partial <-- to create a new review

<%= form_for [@game, @reviews.new] do |r| %>

    <h3 class="post_review">Review this game</h3>

    <p>
        <%= r.text_area :comment %>
    </p>

    <p>
        <%= button_to "Good", upvote_game_review_path(@game.id, r) %>
    </p>

    <p>
        <%= button_to "Bad", downvote_game_review_path(@game.id, r) %>
    </p>

    <p>
        <%= r.hidden_field :game_id, value: @game.id %>
    <p>

    <%= r.submit %>

<% end %>

show.html.erb

<p><%= link_to "<< Home", games_path %></p>
<span><%= link_to "Edit", edit_game_path(@game) %></span>
<span><%= link_to "Delete", game_path(@game), method: :delete %></span>

<div class="game_summary">
    <h2><%= @game.title %></h2>
    <%= image_tag @game.image %>
    <p>Release Date: <%= @game.release_date %> </p>
        <p>Genre: <%= @game.genre_id %> </p>
    <p>Platforms: <%= @game.platform_id %></p>
</div>


<%= link_to "Add News", new_game_news_path(@game) %>
<h2>News & Articles</h2>
<%= link_to "view all", game_news_index_path(@game) %>
<% @news.each do |n| %>
    <ol>
        <li><%= link_to n.title, game_news_path(@game.id, n.id) %></li>
    </ol>
<% end %>


<div class="game_review submit">
    <%= render "review" %>
</div>

<% @reviews.each do |review| %>
    <p><%= review.comment %></p>
    <p><%= link_to "delete", game_review_path(@game.id, review.id), method: :delete %></p>
<% end %>

1 Answers1

0

You don't specify which review you're loading in. The reason is here:

before_action :set_review, only: [:show, :update, :edit, :destroy]

You don't pull in the request's review instance when you go to either of those actions. Further, it doesn't look like you're actually saving them.

So, two things I'd recommend:

  • Add those methods to your before_action:

    before_action :set_review, only: [:show, :update, :edit,
                                      :destroy, :upvote, :downvote]
    
  • (May not be necessary, write tests to confirm this!) Actually save the entity after you've changed its value.

    def upvote
      @review.vote.create = true
      @review.save
      redirect_to @game
    end
    
    def downvote
      @review.vote.create unless @review.vote
      @review.vote = false
      @review.save
      redirect_to @game
    end
    
Makoto
  • 104,088
  • 27
  • 192
  • 230
  • Not sure you addressed the idea of one vote per reviewer. This post looked promising http://stackoverflow.com/questions/4836897/validate-the-number-of-has-many-items-in-ruby-on-rails. – steve klein Jul 04 '15 at 00:31
  • hi guys, thanks so much for the inputs. I tried both suggestions and neither worked. upvoting still won't save -- and downvote now gives an error.. "Couldn't find Review with 'id'=#" –  Jul 04 '15 at 08:26
  • Are you providing the id to the action? What is the path that is being generated for that route? – Makoto Jul 04 '15 at 19:53