I'm trying to create a simple Like/unlike button with Rails 4. I tried to do this with socialization gem, but after one day of struggling I gave up and decided to modify M. Hartl's 'foollow' mechanism from Rails Tutorial. Here is what i got so far:
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :validatable
has_many :questions
has_many :answers
has_many :likes, foreign_key: "liker_id", dependent: :destroy
has_many :liked_answers, through: :likes, source: :liked, source_type: "Answer"
def like?(answer)
likes.find_by(liked_id: answer.id)
end
def like!(answer)
likes.create!(liked_id: answer.id)
end
def unlike!(answer)
likes.find_by(liked_id: answer.id).destroy
end
end
Answer.rb:
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
has_many :likes, foreign_key: "liked_id",
class_name: "Like",
dependent: :destroy
has_many :likers, through: :likes, source: :liker
end
Like.rb:
class Like < ActiveRecord::Base
belongs_to :liker, class_name: "User"
belongs_to :liked, class_name: "Answer"
validates :liker_id, presence: true
validates :liked_id, presence: true
end
likes_controller.rb:
class LikesController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authenticate_user!
respond_to :html, :js
def create
@answer = Answer.find(params[:like][:liked_id])
current_user.like!(@answer)
respond_with @answer.question
end
def destroy
@answer = Like.find(params[:id]).liked
current_user.unlike!(@answer)
respond_with @answer.question
end
end
_like.html.erb:
<%= form_for(current_user.likes.build(liked_id: @answer.id), remote: true) do |f| %>
<div><%= f.hidden_field :liked_id %></div>
<%= f.submit "Like!", class: "btn btn-large btn-primary" %>
<% end %>
_unlike.html.erb:
<%= form_for(current_user.likes.find_by(liked_id: @answer.id),
html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unlike.", class: "btn btn-large" %>
<% end %>
routes.rb:
Rails.application.routes.draw do
devise_for :users
scope "/admin" do
resources :users
end
resources :questions do
resources :answers do
get :likes
end
end
resources :likes, only: [:create, :destroy]
root to: 'questions#index'
end
I also have jQuery and jQuery_ujs required in application.js, and relevant js.erb files ("create" and "destroy") in views.
The 'like/unlike' mechanism itself seems to work pretty well - in the console, with my 'like!' and 'unlike!' methods, I'm able to create and destroy "Like" objets with id, liker_id and liked_id.
The problem begins with the button itself.
I can see the "Like!" button next to each answer, but when I click it - I get this error:
ActiveRecord::RecordNotFound in LikesController#create
Couldn't find Answer with 'id'=
The error points on this line in LikesController:
@answer = Answer.find(params[:like][:liked_id])
so I suppose my @answer.id results to be 'nil', but I have no idea where did I make mistake. My first guess would be routes file - I'm still not sure if everything is correct there.
I've spent whole day looking for solution, I also found some similar questions on SO, but none of the answers could help me.
Any ideas would be greatly appreciated.
EDIT: Params from the error
Parameters:
{"utf8"=>"✓",
"like"=>{"liked_id"=>""},
"commit"=>"Like!"}
You're using the hidden field tag wrong. http://api.rubyonrails.org/v4.1.1/classes/ActionView/Helpers/FormHelper.html#method-i-hidden_field shows you need to supply two values into your tag. Change your like ERB file to this:
_like.html.erb:
and that should get you what you want.