Adding acts_as_votable voting to posts and comments, routing issue? - ruby-on-rails

I am trying to use acts_as_votable voting system on both posts and comments in my rails app. I am currently generating some obviously improper routes for my comments#upvote and comments#downvote, Here they are below:
upvote_post PUT /posts/:id/upvote(.:format) comments#upvote
downvote_post PUT /posts/:id/downvote(.:format) comments#downvote
But they routes need to be something like /posts/comment/:id/downvote. Here is how I am currently doing my routes
resources :posts do
member do
put "like", to: "posts#upvote"
put "dislike", to: "posts#downvote"
end
resources :comments
member do
put "upvote", to: "comments#upvote"
put "downvote", to: "comments#downvote"
end
end
Also, will I need two votes tables since I want both comments and posts to be votable?
Here is my my comments controller if needed:
class CommentsController < ApplicationController
before_filter :authenticate_user!, :except => [:index, :show]
def index
#post = Post.find(params[:post_id])
#user = User.find(params[:user_id])
#comments = #post.comments.order('created_at desc')
end
def new
#post = Post.find(params[:post_id])
#comment = #post.comments.new(params[:id])
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
#comment.post_id = #post.id
#comment.user_id = current_user.id
if #comment.save
redirect_to post_comments_path(#post)
else
redirect_to new_post_comment_path(post)
end
end
def destroy
end
def upvote
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.upvote_by current_user
redirect_to post_comments_path(#post)
end
def downvote
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.downvote_by current_user
redirect_to post_comments_path(#post)
end
private
def comment_params
params.require(:comment).permit(:body, :post_id, :user_id)
end
end
Thanks for the help

As a rule you probably don't want to nest your routes so deep. And the comment vote doesn't need to know the id of the post, just the comment. You also might find that rather than have a separate method for up and down voting in the controller having the votes go to the VotesController and just handle it there
So, I would do something like this:
resources :posts do
resources :comments, only: [:new, :create, :destroy]
resources :votes, only: [:create, :update, :destroy]
end
resources :comment do
resources :votes, only: [:create, :update, :destroy]
end
That's if you want to have votes be a polymorphic relation on both in which case in your ApplicationsController you'd want a method like:
def find_model
params.each do |name, value|
if name =~ /(.+)_id\z/
return $1.classify.constantize.find(value)
end
end
nil
end
# VotesController
def create
model = find_model
if model
# do stuff
end
end
If you have separate votes tables you can skip this last part.

Had a similar problem fixed it by changing
put "like", to: "posts#upvote"
put "dislike", to: "posts#downvote
to
get "like", to: "posts#upvote"
get "dislike", to: "posts#downvote"
posts_controller
def upvote
#post.upvote_by current_user
redirect_to :back
end
def downvote
#post.downvote_by current_user
redirect_to :back
end
post show page
<%= link_to "like", like_post_path(#post), method: :get %>
<%= link_to "dislike", dislike_post_path(#post), method: :get %>
hope this helps

Related

How should I update a resource that is fully depend on other resource

Let's say I have those two models:
class Post < ApplicationRecord
belongs_to :site
end
class Site < ApplicationRecord
has_many :posts
end
In order to create a post, I need to know the site id. Right now I have a route that points to PostsController#create:
post 'posts', to: 'posts#update'
Should I expect the user to send the site_id in the body of the request?
# config/routes.rb
resources :sites do
resources :posts
end
This creates nested routes. Run $ rails routes to see the routes created.
Should I expect the user to send the site_id in the body of the request?
No. A nested route describes the relationship between the two resources. Its very obvious by looking at the path that POST /sites/1/posts will create a post belonging to a site.
It would be ok to pass a site id in the params if you are using shallow nesting and the user can change which site a post belongs to when updating.
# app/controllers/posts_controller.rb
class PostsController
before_action :set_site
before_action :set_post, only: [:show, :edit, :update]
# GET /sites/1/posts/1
def show
end
# GET /sites/1/posts
def index
#posts = #site.posts
end
# GET /sites/1/posts/new
def new
#post = #site.posts.new
end
# POST /sites/1/posts
def create
#post = #site.posts.new(post_params)
if #post.save
redirect_to #post
else
render :new
end
end
# PATCH|PUT /sites/1/posts
def update
if #post.update(post_params)
redirect_to #post
else
render :edit
end
end
# GET /sites/1/posts/edit
def edit
end
private
def set_site
#site = Site.includes(:posts).find(params[:site_id])
end
def set_post
#post = #site.posts.find(params[:id])
end
def post_params
params.require(:post).permit(:title) # ...
end
end
# app/views/posts/_form.html.erb
<%= form_for [#site, #post] do |f| %>
# ...
<% end %>
# app/views/posts/new.html.erb
<%= render partial: 'form' %>
# app/views/posts/edit.html.erb
<%= render partial: 'form' %>

Resources inside scope have wrong update path

I have the following routes:
namespace :admin do
scope 'users/:user_id' do
resources :orders
end
end
In my controller I have the following:
before_action :find_user
before_action :find_order, only: [:show, :edit, :update, :destroy]
...
def new
#order = #user.orders.new
end
def edit
end
def create
#order = #user.orders.new(order_params)
...
end
def update
if #order.update(order_params)
...
end
...
def find_user
#user = User.find(params[:user_id])
end
def find_order
#order = Order.find(params[:id])
end
My form_with is looking like this:
= form_with model: [:admin, #order], local: true do |f|
etc
The new and create paths are working correctly, and generating the good path: /admin/user/(user_id)/orders
However, the update_path isn't working correctly, and generates the following path:
/admin/user/(order_id)/orders/(order_id).
How can I fix this?
Try to add your #user to a model parameter:
= form_with model: [:admin, #user, #order], local: true do |f|
Too instead of scope you can use nested resources:
namespace :admin do
resources :users do
resources :orders
end
end

undefined local variable or method `product' for #<#<Class:0x007fe77c4f3c68>:0x007fe77c69cb78>

I´m having a problem with an app that I´m building.
in products/show.html.erb I have this code to add product to cart.
<%= button_to product_items_path(product_id: product) do %>
<i class="fa fa-shopping-cart"></i>Add to Cart
<% end %>
And it always gives me this error undefined local variable or method 'product' for #<#<Class:0x007fe77c4f3c68>:0x007fe77c69cb78>
This error is happening in the first line According to Better Error gem
I'am using ActiveAdmin but I'm pretty sure that the error is not appearing because of that.
I'm not sure why this is happening, to me the code seems good but I must be overseeing something.
It would be great if someone could take look and maybe see what I´m not seeing.
This is the `ProductItemsController.rb``
class ProductItemsController < ApplicationController
include CurrentCart
before_action :set_cart, only: [:create]
before_action :set_product_item, only: [:show, :destroy]
def create
product = Product.find(params[:product_id])
#product_item = #cart.add_product(product.id)
if #product_item.save
redirect_to root_url, notice:'Product added to Cart'
else
render :new
end
end
private
def set_product_items
#product_item = ProductItem.find(params[:id])
end
def product_item_params
params.require(:product_item).permit(:product_id)
end
end
And here is the ProductsController.rb
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
def show
end
private
def set_product
#product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:name, :description, :price_usd, :price_isl, :image, :category_id)
end
end
this is the routes.rbfile
Rails.application.routes.draw do
resources :categories
resources :labels
resources :products
resources :carts
resources :product_items
devise_for :admin_users, ActiveAdmin::Devise.config
ActiveAdmin.routes(self)
root 'pages#index'
Only instance variables are available to the view.
def create
#product = Product.find(params[:product_id]) # Prefix variable name with #
#product_item = #cart.add_product(product.id)
if #product_item.save
redirect_to root_url, notice:'Product added to Cart'
else
render :new
end
end
And your view:
<%= button_to product_items_path(#product) do %>
<i class="fa fa-shopping-cart"></i>Add to Cart
<% end %>
You should be able to just pass in the object to the _path helper.

Activerecord reputation system - nested routes

I am trying to follow this video railscast #364 but I am having a lot of trouble with my nested routes. When I use this code:
<%= link_to "up", vote_movie_review_path(#movie, #reviews, type: "up"), method: "post" %>
I get this error when I select up vote:
ActiveRecord::RecordNotFound in ReviewsController#vote
Couldn't find Review with 'id'=# <Review::ActiveRecord_Relation:0x007f0358c1e550>
This is my route:
vote_movie_review POST /movies/:movie_id/reviews/:id/vote(.:format) genre_linkers#vote
I created another model that was not nested using this code:
<%= link_to "up", vote_movie_path(movie, type: "up"), method: "post" %>
and that one worked. So I am thinking it has to be something wrong with my path or how I am calling the objects. I have spent almost all day working on this, I really need help.
review_controller.rb
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :set_movie
before_action :authenticate_user!
respond_to :html
def index
#reviews = Review.all
respond_with(#reviews)
end
def show
end
def vote
value = params[:type] == "up" ? 1 : -1
#review = Review.find(params[:id])
#review.add_evaluation(:vote, value, current_user)
redirect_to :back, notice: "thanks for the vote"
end
def new
#review = Review.new
respond_with(#review)
end
def edit
end
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
#review.movie_id = #movie.id
if #review.save
redirect_to #movie
else
render 'new'
end
end
def update
#review.update(review_params)
respond_with(#review)
end
def destroy
#review.destroy
respond_with(#review)
end
private
def set_review
#review = Review.find(params[:id])
end
def set_movie
#movie = Movie.find(params[:movie_id])
end
def review_params
params.require(:review).permit(:genre, :description, :vote)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users
resources :movies do
resources :reviews do
member { post :vote }
end
end
root 'movies#index'
end
and the model
review.rb
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :movie
has_reputation :votes, source: :user, aggregated_by: :sum
end
The culprit is this part of your link_to: vote_movie_review_path(#movie, #reviews, type: "up"). #reviews is an ActiveRecord::Relation and not a Review record, hence no record with an ID can be found.

Display all comments and order them

I want to display all comments (total number) on 'all' page. So, not all comments for a specific Post, but all comments in the entire app. I've tried with Comment.all, but it says it can't find post without an ID...
.../comments/all
routes
resources :posts do
resources :comments do
member do
put "like", to: "comments#upvote"
put "dislike", to: "comments#downvote"
end
end
end
comments_controller
def all
?
end
def index
#post = Post.find(params[:post_id])
#comments = #auto.comments.order("cached_votes_score DESC")
end
def show
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
end
...
You need a not nested route to comments:
routes
resources :posts do
resources :comments do
member do
put "like", to: "comments#upvote"
put "dislike", to: "comments#downvote"
end
end
end
get "comments#all"
comments_controller
def all
#comments=Comment.all
end
The problem is in your PostsController because what you want is not actually directly relevant to a post.
Try adding a collection route for comment
resources :comments do
member do
..
end
collection do
get :all # actually that is index
end
end
or simpler
#config/routes.rb
resources :comments, only: :index
and then a
#app/controllers/comments_controller.rb
def index
Comment.all
end
on your CommentsController will do.

Resources