Search code examples
ruby-on-railsrubyruby-on-rails-4undefinednomethoderror

NoMethodError for undefined method `votes_against'


I am getting a NoMethodError for undefined method `votes_against' while trying to display the vote count in my show view. I have implemented the acts_as_votable gem however, I am having some difficulty getting the votes to be displayed. Meaning if a user clicks thumbs_up you see a figure of 1, if another user clicks the image thumbs_up the figure increases to 2.

Specifically the error arises for line: <%= @post.votes_against %> within my show.html page.

Thank you in advance.

Post Rb

class Post < ActiveRecord::Base

    belongs_to :user

    has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }

    validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]

    validates :description, presence: true
    validates :image, presence: true

  scope :subscribed, ->(following) { where user_id: following }

  acts_as_votable

    # Returns posts from the users being followed by the given user.
  def self.from_users_followed_by(user)
    followed_user_ids = user.followed_user_ids
    where("user_id IN (:followed_user_ids) OR user_id = :user_id",
          followed_user_ids: followed_user_ids, user_id: user)
  end

end

User Rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_many :posts, dependent: :destroy
  has_many :relationships, foreign_key: "follower_id", dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :reverse_relationships, foreign_key: "followed_id",
                                   class_name:  "Relationship",
                                   dependent:   :destroy
  has_many :followers, through: :reverse_relationships, source: :follower
  has_many :following, :through => :relationships, :source => :followed

  has_many :subscribed, class_name: "Relationship", foreign_key: "follower_id"


  validates :name, presence: true

  acts_as_voter

  has_one :profile

  belongs_to :user

  has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "150x150>" }, :default_url => "/assets/avatar.png"
  validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/


  def feed
    Post.from_users_followed_by(self)
  end

  def following?(other_user)
    relationships.find_by(followed_id: other_user.id)
  end

  def follow!(other_user)
    relationships.create!(followed_id: other_user.id)
  end

  def unfollow!(other_user)
    relationships.find_by(followed_id: other_user.id).destroy
  end

end

Post Controller

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  before_action :correct_user, only: [:edit, :update, :destroy]
  before_action :authenticate_user!, except: [:index, :show]


  def index
    @posts = Post.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 40)
  end


  def show
  end


  def new
    @post = current_user.posts.build
  end


  def edit
  end


  def create
    @post = current_user.posts.build(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render action: 'show', status: :created, location: @post }
      else
        format.html { render action: 'new' }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end


  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end


  def destroy
    @post.destroy
    respond_to do |format|
      format.html { redirect_to posts_url }
      format.json { head :no_content }
    end
  end


 def vote_for
      @post = Post.find(params[:id])
      current_user.vote_for(@post)
      respond_to do |format|
        format.js
      end
  end


  private
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = Post.find(params[:id])
    end

    def correct_user
      @post = current_user.posts.find_by(id: params[:id])
      redirect_to posts_path, notice: "Not authorized to edit this post" if @post.nil?
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def post_params
      params.require(:post).permit(:description, :image)
    end

  end

Routes

devise_for :admins
  devise_for :users

  resources :posts

  resources :users do
   member do
    get :following
    get :followers
   end
  end

  resources :relationships, only: [:create, :destroy]
  resources :user_friendships do 
      member do
        put :accept
      end
     end   


resources :posts do 
    member do
      post :vote_for 
      post :vote_against
    end
  end

Show.html.erb

<div class="row">
    <div class="col-md-offset-4 col-med-8">
        <div class="panel panel-default">
        <div class="panel-heading center">

        <% if @post.image.url %>
            <%= image_tag @post.image.url(:medium) %>

        <% elsif @post.video.url %>
            <%= video_tag @post.video.url(:medium), controls: true, type: "video/mp4" %>
        <% end %>


        <p>
            <strong>Votes For:</strong>
            <%= @post.votes_for %>
        </p>


        <%=link_to image_tag('thumbs_up', :border => 0), vote_for_post_path(@post) %>
        <%=link_to image_tag('thumbs_down', :border => 0), vote_against_post_path(@post) %>

        </div>
        <div class="panel-body">
        <p><%= @post.description %></p>

        <% user = @post.user %>
        <p><strong><%= link_to(user.name, user_path(user)) if @post.user %></strong></p>

        <% if @post.user == current_user %>
            <%= link_to edit_post_path(@post) do %>
            <span class="glyphicon glyphicon-edit"></span>
            Edit
          <% end %>
        <% end %>
        <%= link_to 'Back', posts_path %>
        </div>
</div>

Solution

  • As i mentioned in my comment you are getting this error because there is no method "votes_against". If you look at acts_as_votables repo, you have "get_likes" and "get_upvotes" methods to get your up votes and "get_dislikes" and "get_downvotes" methods for getting down votes

    # tally them up!
    @post.votes_for.size # => 5
    @post.get_likes.size # => 3
    @post.get_upvotes.size # => 3
    @post.get_dislikes.size # => 2
    @post.get_downvotes.size # => 2
    

    How can I get the vote count to be displayed? Do I need to add get_upvotes and .get_downvotes into my controller?

    No you don't need to define these methods as acts_as_votable gem already does this for you. To show upvote and downvote count you can do:

    <p>
       <strong>Votes For:</strong>
       <%= @post.get_likes.size%>
    </p>
    
    <p>
       <strong>Votes Against:</strong>
       <%= @post.get_dislikes.size%>
    </p>