How do I create a numerical rating system in Rails? - ruby-on-rails

I'd like to create a numerical rating system in rails where users can rate a post from 1 - 10.
I've looked on Google but I only find outdated tutorials and star rating gems which simply don't do the job for me.
Perhaps someone can point me to a gem that can help me achieve this?

Ruby Toolbox lists several, although most are DOA. Mongoid_ratings seemed to be the most recently updated, although you may not want to go the Mongo route.
https://www.ruby-toolbox.com/categories/rails_ratings
I would suggest building from scratch. Heres a quick (and probably non-functional/non-secure) hack that might help get you started:
Routes
resources :articles do
resources :ratings
end
Models
class Article < ActiveRecord::Base
has_many :ratings, :dependent => :destroy
end
class Rating < ActiveRecord::Base
belongs_to :article
validates_presence_of :article
validates_inclusion_of :value, :in => 1..10
end
Controllers
class RatingsController < ApplicationController
before_filter :set_article
def create
#rating = #article.ratings.new :value => params[:value]
if #rating.save
redirect_to article_ratings_path(#article), :notice => "Rating successful."
else
redirect_to article_ratings_path(#article), :notice => "Something went wrong."
end
end
def update
#rating = Rating.find(params[:id])
#rating.update_attribute :value, params[:value]
end
private
def set_article
#article = Article.find(parms[:article_id])
end
end
In an article view somewhere:
form_for [#article,#rating] do |f|
f.select("rating", "value", (1..10))
f.submit "Rate this Article"
end

Have a look at the Letsrate gem: https://github.com/muratguzel/letsrate
Works great for me.

Related

Rails 5 adding Likes to Articles and Comments

Rails 5, I am trying to add Likes from scratch without a gem dependency. I am so close to having it down but am being completely stumped by what's happening when Comments get involved.
Storing and saving article.likes worked perfectly. Then I got the comment.likes to work. Now, when I like a Comment, they individually store a new Like (great!), except now Article is not properly saving any likes, and even weirder: article.likes.count gives a TOTAL sum of all it's comments' likes.. I am sure this is an easy fix but I am just totally missing it and I've tried everything. I've gone down some deep rabbit holes for this.
I think the problem lies in routing, or how the models relate.
Articles have many Comments, and both of them have many Likes.
Here are the models starting with like.rb:
class Like < ApplicationRecord
belongs_to :user
belongs_to :article
belongs_to :comment
# Make sure that one user can only have one like per post or comment
validates :user_id, uniqueness: { scope: [:article_id, :comment_id] }
end
article.rb
class Article < ApplicationRecord
belongs_to :user
...
# destroy associated comments on article deletion
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
end
comment.rb
class Comment < ApplicationRecord
belongs_to :article
belongs_to :user
...
has_many :likes, dependent: :destroy
end
routes.rb
...
resources :articles, :path => 'blog' do
resources :likes, only: [:create, :destroy]
resources :comments do
resources :likes, only: [:create, :destroy]
end
end
the meaty likes_controller.rb. Mind the #{}, EVERYTHING checks out the way it should, so why does the comment.likes.create save correctly, but the article.likes.create does not?? Help, please.
class LikesController < ApplicationController
before_action :get_signed_in_user
before_action :comment_or_article
def create
if #comment_like
like_resource(comment)
else
like_resource(article)
end
end
def destroy
if #comment_like
comment.likes.where(user: current_user).destroy_all
else
article.likes.where(user: current_user).destroy_all
end
flash[:success] = "Unliked! :("
redirect_to article_redirect(article)
end
private
def like_resource(obj)
if obj.likes.where(user: current_user.id).present?
flash[:error] = "You've already upvoted this! + #{like_params} + #{#comment_like} + #{obj}"
if #comment_like
if obj.likes.create(user: current_user, article: #article)
flash[:success] = "Upvoted Comment! + #{like_params} + #{#comment_like} + #{obj}"
else
flash[:error] = "An error prevented you from upvoting."
end
elsif obj.likes.create(user: current_user)
flash[:success] = "Upvoted Blog! + #{like_params} + #{#comment_like} + #{obj}"
else
flash[:error] = "An error prevented you from upvoting."
end
redirect_to article_path(article)
end
def get_signed_in_user
unless user_signed_in?
flash[:error] = "Sign in to upvote!"
redirect_back(fallback_location: articles_path)
end
end
# decide what we are liking
def comment_or_article
if comment
#comment_like = true
else
#comment_like = false
end
end
def article
#article = Article.find(params[:article_id])
end
def comment
unless params[:comment_id].nil?
#comment = article.comments.find(params[:comment_id])
end
end
def like_params
params.permit( :article_id, :comment_id).merge(user_id: current_user.id)
end
end
Finally the like buttons in my articles/show.html.erb:
<%= link_to "Like", article_likes_path(#article), method: :post %>
<%= "#{#article.likes.count}" %>
... # inside loop to show comments:
<%= link_to "Like", article_comment_likes_path(#article, comment), method: :post %>
<%= "#{comment.likes.count}" %>
TLDR:
Comment likes work fine, save fine, count individually fine.
Article likes do not save, but article.likes.count === article.comments.likes.count. Why?
I want article.likes to be completely unique, like it's own comments are.
Thank you in advance.
EDIT: took out belongs_to :comments in like.rb and refactored like_controller.rb main function to
private
def like_resource(obj)
if obj.likes.where(user: current_user.id).present?
flash[:error] = "You've already upvoted this!"
elsif obj.likes.create(user: current_user, article: #article)
flash[:success] = "Upvoted!"
else
flash[:error] = "An error prevented you from upvoting."
end
redirect_to article_path(article)
end
Always supplying the #article helps when liking a comment. An Article like would not need a comment_id to save.
Sorry for the long post, hopes this helps someone.
I just figured it out, actually.
class Like < ApplicationRecord
belongs_to :user
belongs_to :article
# belongs_to :comment
end
Commenting the above out, it allows the #article "like" to save without a comment being referenced. Article likes are saved properly. However, article.likes.count still increments whenever an article.comment is liked. This means article.likes is always >= article.comments.likes; which is completely fine.
I just changed the #article.likes to:
<%= "#{#article.likes.where(comment_id: nil).count}" %>
Filtering out all the strictly article.likes. The comment.likes still work perfectly.

set current user when creating a comment with inherited_resource rails

I am using Rails Inherited_resource gem in my comments controller, and comments is a nested resource so:
resources :projects do
resources :comments do
end
I also have a belongs_to in the comments controller:
belongs_to :project, :finder => :find_by_project_uuid!, :class_name => "Thfz::Project", :polymorphic => true
How can I set the comment's user association to the current_user(user_id) when its created? As user_id is not suppose to be massive assigned.
I tried following:
def begin_of_association_chain
current_user
end
This does set the user id correctly, but I cannot get nested resource working for Project with this.
Same question come when destroy a comment, I will need to find the comment through current_user, so how to achieve this?
So do I have to write my own create and destroy actions?
Thanks :)
Have you tried the following inside comments_controller?
class CommentsController < InheritedResources::Base
before_filter :authenticate_user! # Assuming you are using Devise for authentication
respond_to :html, :xml, :json
belongs_to :project, :finder => :find_by_project_uuid!, :class_name => "Thfz::Project"
def create
#comment = build_resource
#comment.author = current_user
create!
end
end

Rails has_and_belongs_to_many association

so I have two models:
class User < ActiveRecord::Base
has_and_belongs_to_many :followed_courses, :class_name => "Course"
end
class Course < ActiveRecord::Base
has_and_belongs_to_many :followers, :class_name => "User"
end
in User.rb, I also have:
def following_course?(course)
followed_courses.include?(course)
end
def follow_course!(course)
followed_courses<<course
end
def unfollow_course!(course)
followed_courses.delete(course)
end
I don't have a courses_users model, just a join table(courses_users). I guess I'll have to follow/unfollow a course in CoursesController. Do I create a new action in the controller?
Right now, I have a follow form in the courses/show page when the course is not followed
= form_for #course, :url => { :action => "follow" }, :remote => true do |f|
%div= f.hidden_field :id
.actions= f.submit "Follow"
And I have in CoursesController:
def follow
#course = Course.find(params[:id])
current_user.follow_course!(#course)
respond_to do |format|
format.html { redirect_to #course }
format.js
end
end
But looks like it was never activated. Do I need to modify the route so the action will be activated? How to modify it? Or is there a better way to do this? Can I replace the form with just a link? Thanks in advance!
This is a follow-up of a related question rails polymorphic model implementation
THe routing system invokes CoursesController#follow? If not you have to write in the routes.rb file the following line:
map.resources :courses, :member => {:follow => :post}
#You'll have the map.resources :courses, just add the second argument.
After that the routing system could redirect to that action, and it will you give the follow_course_url(#course) helper

How to insert rows in a many-to-many relationship

I am having an issue trying to save into an intermediate table. I am new on Rails and I have spent a couple of hours on this but can't make it work, maybe I am doing wrong the whole thing. Any help will be appreciated. =)
The app is a simple book store, where a logged-in user picks books and then create an order.
This error is displayed:
NameError in OrderController#create
uninitialized constant Order::Orderlist
These are my models:
class Book < ActiveRecord::Base
has_many :orderlists
has_many :orders, :through => :orderlists
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :orderlists
has_many :books, :through => :orderlists
end
class OrderList < ActiveRecord::Base
belongs_to :book
belongs_to :order
end
This is my Order controller:
class OrderController < ApplicationController
def add
if session[:user]
book = Book.find(:first, :conditions => ["id = #{params[:id]}"])
if book
session[:list].push(book)
end
redirect_to :controller => "book"
else
redirect_to :controller => "user"
end
end
def create
if session[:user]
#order = Order.new
if #order.save
session[:list].each do |b|
#order.orderlists.create(:book => b) # <-- here is my prob I cant make it work
end
end
end
redirect_to :controller => "book"
end
end
Thnx in advance!
Manuel
Only got time to look at this briefly, I'm afraid, but the first thing I spot is that your has_many relations are called :orderlists. I think that needs to be :order_lists, with an underscore.
This is not directly associated with your question but this query:
book = Book.find(:first, :conditions => ["id = #{params[:id]}"])
...is vulnerable to sql injection. In this case content of params[:id] gets passed to sql without proper escaping. I would suggest changing this line to something like this:
book = Book.find(:first, :conditions => ["id = ?, params[:id]])
Here's explanation: http://wiki.rubyonrails.org/howtos/security/sql_injection
Yes that was one of the problems. Then I could make it work with this line in the 'create' method:
def create
if session[:user]
#order = Order.new
if #order.save
session[:list].each do |b|
OrderList.create(:book => b, :order => #order)
end
end
end
redirect_to :controller => "book"
end
Thanks Chris

Error creating a new survey as the associated model(s) are invalid (questions)

I have a survey as part of an application I'm building. The user can create a survey and specify questions dynamically (can have as many as they want), so I've used an associated model with:
#survey.rb
has_many :survey_questions, :dependent => :destroy
has_many :survey_answers, :dependent => :destroy
after_update :save_survey_questions
validates_associated :survey_questions
def save_survey_questions
survey_questions.each do |t|
if t.should_destroy?
t.destroy
else
t.save(false)
end
end
end
def survey_question_attributes=(survey_question_attributes)
survey_question_attributes.each do |attributes|
if attributes[:id].blank?
survey_questions.build(attributes)
else
survey_question = survey_questions.detect { |e| e.id == attributes[:id].to_i }
survey_question.attributes = attributes
end
end
end
#surveys_controller.rb
def new
#survey = Survey.new
if(#survey.survey_questions.empty?)
#survey.survey_questions.build
end
respond_to do |format|
format.html # new.html.erb
end
end
def create
#survey = Survey.new(params[:survey])
respond_to do |format|
if #survey.save
format.html { redirect_to(survey_path(:id => #survey)) }
else
format.html { render :action => "new" }
end
end
end
#survey_question.rb
class SurveyQuestion < ActiveRecord::Base
belongs_to :survey
attr_accessor :should_destroy
def should_destroy?
should_destroy.to_i == 1
end
validates_presence_of :question, :survey_id
end
The problem is when I submit I get an error on the questions:
#errors={"survey_questions"=>["is invalid", "is invalid", "is invalid"]}
I believe it is because the survey_id I have linking surveys to survey_questions is not being filled in.
Any ideas how I can overcome this?
If I create the survey with no questions, then add them afterwards via edit, then it works perfectly.
I'm pretty sure that accepts_nested_attributes can help you a lot, there you'll find some examples building the associated objects wich seems to be your problem (since the survey_id in survey_questions is not being filled in), basically you should define in your models something like:
class Survey < ActiveRecord::Base
has_many :survey_questions
accepts_nested_attributes_for :survey_questions, :allow_destroy => true
...
end
It will handle all the SurveyQuestions validations through Survey.
Your code looks close to the standard --> http://railscasts.com/episodes/75-complex-forms-part-3
Are you sure you are getting the question parameter back correctly? I only ask because that is the other thing you are validating against and you don't have the form code in there so I can't see what's coming back to the controller.
OK,
I've managed to fix it and it was a really silly mistake on my part.
In survey_question.rb I had the line :
validates_presence_of :question, :survey_id
However, rails automatically deals with survey_id because of the has_many belongs_to relationship!
So this should be validates_presence_of :question
I also in the process of finding this out upgraded rails to 2.3.4 and started using:
accepts_nested_attributes_for :survey_questions, :allow_destroy => true
which dealt with all the attributes etc.

Resources