0

I have two models that can be commented on, Books and Movies.

The comments are votable

In my routes file:

 resources :books, :path => '' do
    resources :comments do
      member do
    post :vote_up
  end
end

In my comments controller:

class CommentsController < ApplicationController
  def create
    book.comments.create(new_comment_params) do |comment|
      comment.user = current_user
    end
    redirect_to book_path(book)
  end

  private

  def new_comment_params
    params.require(:comment).permit(:body)
  end

  def book
    @book = Book.find(params[:book_id])
  end

  def vote_up
    begin
      current_user.vote_for(@comment = Comment.find(params[:id]))
      render :nothing => true, :status => 200
    rescue ActiveRecord::RecordInvalid
      render :nothing => true, :status => 404
    end
  end
end

In my view:

    <%= link_to('vote for this post!', vote_up_book_comment_path(comment), 
:method => :post) %>

I keep on getting this error:

No route matches {:action=>"vote_up", :controller=>"comments", :id=>nil, :book_id=>#<Comment id: 
3, body: "fantastic read!", book_id: 113, created_at: "2014-02-15 17:08:10", updated_at: 
"2014-02-15 17:08:10", user_id: 8>, :format=>nil} missing required keys: [:id]

This is the gem I am using for the voting: https://github.com/bouchard/thumbs_up

The comments can belong to either the books or movies, how do I set this up in the routes? Also, how do I set up the votes in the routes? (all the comments are votable)

Katie H
  • 2,283
  • 5
  • 30
  • 51

2 Answers2

2

If you run rake routes, you'll probably get a line in the output that reads like this:

vote_up_book_comment POST   /:book_id/comments/:id/vote_up(.:format) comments#vote_up

Pay special attention to this part — it's telling you what the vote_up_book_comment_path method expects as arguments:

/:book_id/comments/:id/vote_up(.:format)

Also, your error message is giving you a few hints:

No route matches ...
:id=>nil, :book_id=>#<Comment id: 3 ...
missing required keys: [:id]

The path helper expects an id (for the comment) and a book_id, and the order in which they are required is shown in rake routes (book_id first, then id).

So, in sum, you need to pass a book to vote_up_book_comment_path:

<%= link_to('vote for this post!', vote_up_book_comment_path(@book, comment), :method => :post) %>
Anthony Navarre
  • 297
  • 2
  • 7
  • I'm now getting this error: SQLite3::ConstraintException: votes.voteable_id may not be NULL: INSERT INTO "votes" ("created_at", "updated_at") VALUES (?, ?) – Katie H Feb 15 '14 at 19:39
  • 1
    Based on the [thumbs_up source code for `vote_for`](https://github.com/bouchard/thumbs_up/blob/master/lib/acts_as_voter.rb#L69), I'm guessing that means that `Comment.find(params[:id])` is returning `nil` within `CommentsController#vote_up` ... is `params[:id]` coming into the controller as expected? – Anthony Navarre Feb 16 '14 at 01:00
  • Thank you soooooooooooo much! Your commenting system works perfectly. It turns out thumbs_up is a piece of crap, I ended up using the acts_as_votable gem, everything now works like a charm! :) Thanks again! :) – Katie H Feb 16 '14 at 01:23
  • Hey Anthony! Can you take a look at this questions? I'm trying to add nested comments, should I just use the acts_as_commentable gem? http://stackoverflow.com/questions/21963064/adding-threading-and-email-subscriptions-to-comments – Katie H Feb 23 '14 at 14:28
1

Because you are using member in your routes, rails requires an id in the url. In vote_up_book_comment_path(comment) you supply a book id, but no comment id. It interpreted the comment argument as a book. To fix this, include a book object, changing vote_up_book_comment_path(comment) to vote_up_book_comment_path(@book, comment). In your new method of your controller, include the book variable also so your view template can access it.

To set up comments in either the books or movies:

Because comments are separate from books or videos, you do not want to nest them under books. Instead, have comments be a separate route, and only have the route nested under books or videos when you are newing or editing. That way you can have a hidden field in your view template that stores whether it's a book or a video, and then pass it along to the controller. Now the controller has the necessary information to know whether it's a book_id or a movie_id.

It would look something like this in the code:

resources :books do
    resources :comments, only: [:new, :edit]
end

You would do this for all resources you want. Finally for comments you would do something like this:

resources :comments, except: [:new, :edit]
Rashi Abramson
  • 1,127
  • 8
  • 16