Carl Edwards
Carl Edwards

Reputation: 14444

Voting for nested objects using the Acts As Votable gem

I have a Restaurant model that has_many :dishes, through: dish_categories. I found a post that shows how to write the view code necessary to get things going for the Acts As Votable gem. My situation differs being that the dish model is the nested resource that's being voted upon.

I tried translating the provided code but to no avail. At this point should I create a new controller for dishes and place the votable actions there? If so how would I setup my route so I can accomplish this on my restaurant's show page?

Models

class Restaurant < ActiveRecord::Base
  has_many :dish_categories, dependent: :destroy
  has_many :dishes, through: :dish_categories
end

class DishCategory < ActiveRecord::Base
  belongs_to :restaurant

  has_many :dishes, dependent: :destroy

  delegate :name, to: :dish_category, prefix: "category"
  delegate :restaurant, to: :dish_category
end

class Dish < ActiveRecord::Base
  belongs_to :dish_category
end

Restaurants Controller

...
def upvote
  @restaurant = Restaurant.find(params[:id])
  @dish = Dish.find(params[:id])
  @dish.liked_by current_user
  redirect_to @restaurant
end

def downvote
  @restaurant = Restaurant.find(params[:id])
  @dish = Dish.find(params[:id])
  @dish.disliked_by current_user
  redirect_to @restaurant
end
...

Routes

resources :restaurants do
  member do
    put "upvote", to: "restaurants#upvote"
    put "downvote", to: "restaurants#downvote"
  end
end

Restaurants - Show View

...
<% @restaurant.dishes.each do |dish| %>
    <div>
        <h2><%= dish.category_name %></h2>

        <b><%= dish.name %></b>

        <%= link_to "Upvote", like_restaurant_path(dish), method: :put %>

        <%= link_to "Downvote", dislike_restaurant_path(dish), method: :put %>
    </div>
<% end %>

Upvotes: 1

Views: 336

Answers (1)

Carl Edwards
Carl Edwards

Reputation: 14444

A number of things needed to be done to get this to work. The first order of business was moving my controller action to my dishes controller. I also added two more actions: unlike and undislike for toggle functionailty.

NOTE: Logic for authenticating non-registered for users to liking/disliking dishes would still need to be written but this should help get you started.

Dishes Controller

class DishesController < ApplicationController
  before_action :load_restaurant_and_dish, only: [:like, :unlike, :dislike, :undislike]

  def like
    @dish.liked_by current_user
    redirect_to @restaurant
  end

  def unlike
    @dish.unliked_by current_user
    redirect_to @restaurant
  end

  def dislike
    @dish.disliked_by current_user
    redirect_to @restaurant
  end

  def undislike
    @dish.undisliked_by current_user
    redirect_to @restaurant
  end

  private
    def load_restaurant_and_dish
      @dish       = Dish.find(params[:id])
      @restaurant = @dish.restaurant
    end
end

Next was configuring my routes to correspond with my restaurant and dish models:

Routes

resources :restaurants do
  resources :dishes, only: [:like, :unlike, :dislike, :undislike] do
    member do
      put "like",      to: "dishes#like"
      put "unlike",    to: "dishes#unlike"
      put "dislike",   to: "dishes#dislike"
      put "undislike", to: "dishes#undislike"
    end
  end
end

I ended up refactoring my show view and created a few partials to reduce clutter now that there's a little bit of logic involved:

Restaurants - Show View

...
<%= render "restaurants/dish_partials/dishes" %>
...

Dishes Partial

<% @dishes.each do |dish| %>
  <div>
    <h2><%= dish.category_name %></h2>

    <span><b><%= dish.name %></b></span>

    <%= render "restaurants/dish_partials/like_toggle", dish: dish %>
  </div>
<% end %>

Like Toggle Partial

<% if current_user.liked? dish %>
    <%= link_to "Unlike", unlike_restaurant_dish_path(@restaurant, dish), method: :put %>
<% else %>
    <%= link_to "Like", like_restaurant_dish_path(@restaurant, dish), method: :put %>
<% end %>   

<% if current_user.disliked? dish %>
    <%= link_to "Undislike", undislike_restaurant_dish_path(@restaurant, dish), method: :put %>
<% else %>
    <%= link_to "Dislike", dislike_restaurant_dish_path(@restaurant, dish), method: :put %>
<% end %>

Upvotes: 2

Related Questions