From 1751de2fcc2a86f201a629671e3ebd9c5e99b241 Mon Sep 17 00:00:00 2001 From: Mikhail Kilin <1kilinmv1@gmail.com> Date: Sun, 29 Nov 2020 20:54:36 +0300 Subject: [PATCH 1/4] added stars plugin --- app/assets/javascripts/rate/rating_file.js | 167 ++++++++++++++++++++ app/assets/javascripts/rate/rating_init.js | 16 ++ app/controllers/articles_controller.rb | 10 +- app/controllers/ratings_controller.rb | 19 +++ app/decorators/article_decorator.rb | 6 + app/models/article.rb | 1 + app/models/rating.rb | 10 ++ app/policies/rating_policy.rb | 9 ++ app/views/articles/_form.html.slim | 8 + app/views/articles/show.html.slim | 4 + app/views/layouts/company.html.slim | 1 + app/views/ratings/create.js.erb | 3 + app/views/ratings/update.js.erb | 3 + config/routes.rb | 2 + db/migrate/20201129151451_create_ratings.rb | 9 ++ db/schema.rb | 9 +- spec/factories/ratings.rb | 7 + spec/features/user/ratings/create_spec.rb | 20 +++ spec/features/user/ratings/update_spec.rb | 18 +++ 19 files changed, 319 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/rate/rating_file.js create mode 100644 app/assets/javascripts/rate/rating_init.js create mode 100644 app/controllers/ratings_controller.rb create mode 100644 app/models/rating.rb create mode 100644 app/policies/rating_policy.rb create mode 100644 app/views/articles/_form.html.slim create mode 100644 app/views/ratings/create.js.erb create mode 100644 app/views/ratings/update.js.erb create mode 100644 db/migrate/20201129151451_create_ratings.rb create mode 100644 spec/factories/ratings.rb create mode 100644 spec/features/user/ratings/create_spec.rb create mode 100644 spec/features/user/ratings/update_spec.rb diff --git a/app/assets/javascripts/rate/rating_file.js b/app/assets/javascripts/rate/rating_file.js new file mode 100644 index 00000000..0a67e9f6 --- /dev/null +++ b/app/assets/javascripts/rate/rating_file.js @@ -0,0 +1,167 @@ +const defaults = { + "value": Number($("#rating_rate").val()), + "stars": 5, + "half": false, + "emptyStar": "far fa-star", + "halfStar": "fas fa-star-half-alt", + "filledStar": "fas fa-star", + "color": "#fcd703", + "readonly": false, + "click": function (e) { + console.error("No click callback provided!"); + } +}; + +jQuery.fn.extend({ + rating: function (options = {}) { + return this.each(function () { + if ($(this).attr("rating")) { + $(this).empty(); + } + + this.stars = options.value ? options.value : defaults.value; + this.readonly = options.readonly ? options.readonly : defaults.readonly; + + this.getStars = function () { + return $(this).find($("i")); + }; + + $(this).css({ + "color": options.color ? options.color : defaults.color + }) + .attr("rating", true); + + if (!this.readonly) { + $(this).off('mousemove').on('mousemove', function (e) { + let halfStars = options.half ? options.half : defaults.half; + + if (this.getStars().index(e.target) >= 0) { + if (!halfStars) { + $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); + let index = this.getStars().index(e.target) + 1; + + for (let i = 0; i < this.getStars().length; i++) { + if (i < index) + $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) + } + + } else { + $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); + let extra = 0.5; + + $(this).find("i").css({ + "width": $(this).find("i").outerWidth() + }); + + if (e.offsetX > ($(e.target).outerWidth() / 2)) + extra = 1; + + let index = this.getStars().index(e.target) + extra; + for (let i = 0; i < this.getStars().length; i++) { + if (i + 0.5 < index) { + $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) + } else if (i < index) { + $(this.getStars()[i]).attr("class", options.halfStar ? options.halfStar : defaults.halfStar) + } + } + } + } + }); + + $(this).off('mouseout').on('mouseout', function (e) { + this.printStars(); + }); + + $(this).off('click').on('click', function (e) { + let halfStars = options.half ? options.half : defaults.half; + if (!halfStars) { + this.stars = this.getStars().index(e.target) + 1; + } else { + let extra = 0.5; + if (e.offsetX > ($(e.target).outerWidth() / 2)) + extra = 1; + + this.stars = this.getStars().index(e.target) + extra; + } + + const callback = options.click ? options.click : defaults.click; + callback({ + "stars": this.stars, + "event": e + }); + }); + } + + // Add star elements to the element + const stars = options.stars ? options.stars : defaults.stars; + for (let i = 0; i < stars; i++) { + let star = $("") + .addClass(options.emptyStar ? options.emptyStar : defaults.emptyStar) + .appendTo($(this)); + + if (!this.readonly) { + star.css({ + "cursor": "pointer" + }) + } + + if (i > 1000) + return; + } + + this.printStars = function () { + let halfStars = options.half ? options.half : defaults.half; + if (!halfStars) { + $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); + for (let i = 0; i < this.stars; i++) { + $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) + } + } else { + $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); + for (let i = 0; i < this.stars; i++) { + if (i < this.stars - 0.5) { + $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) + } else { + $(this.getStars()[i]).attr("class", options.halfStar ? options.halfStar : defaults.halfStar) + } + } + } + }; + + if (this.stars > 0) { + this.printStars(); + + const callback = options.click ? options.click : defaults.click; + callback({ + "stars": this.stars + }); + } + }); + } +}) +; + +$(function () { + $("[data-rating-stars]").each(function () { + // Get all data-rating attributes + let d = {}, + re_dataAttr = /^data-rating\-(.+)$/; + + $.each($(this).get(0).attributes, function (index, attr) { + if (re_dataAttr.test(attr.nodeName)) { + let key = attr.nodeName.match(re_dataAttr)[1]; + d[key] = attr.nodeValue; + } + }); + + // Create the click event handler + if (d.input != null) { + d.click = function (e) { + $(d.input).val(e.stars); + } + } + + // Run the rating function on the element + $(this).rating(d); + }); +}); diff --git a/app/assets/javascripts/rate/rating_init.js b/app/assets/javascripts/rate/rating_init.js new file mode 100644 index 00000000..e08dfb32 --- /dev/null +++ b/app/assets/javascripts/rate/rating_init.js @@ -0,0 +1,16 @@ +if ($("#review").length > 0) { + + $("#review").rating({ + "click":function (e) { + if (e.stars > 0 && e.stars <= 5) { + if ($("#new_rating").length > 0) { + $("#rating_rate").val(e.stars); + $("#new_rating").submit(); + } else { + $("#rating_rate").val(e.stars); + $(".edit_rating").submit(); + } + } + } + }); +} diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 4e471726..b2f115c7 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -2,6 +2,7 @@ class ArticlesController < ApplicationController expose_decorated :article, -> { set_article } expose :comment, -> { set_comment } expose_decorated :company, -> { article.company } + expose :rating, -> { set_rating } layout "company" @@ -9,13 +10,18 @@ def show authorize article end + private + def set_article Article.includes(:comments).find_by(id: params["id"]) end - private - def set_comment article.comments.new end + + def set_rating + Rating.find_by(article: article, user: current_user) || + Rating.new(article: article, user: current_user) + end end diff --git a/app/controllers/ratings_controller.rb b/app/controllers/ratings_controller.rb new file mode 100644 index 00000000..fe59bf91 --- /dev/null +++ b/app/controllers/ratings_controller.rb @@ -0,0 +1,19 @@ +class RatingsController < ApplicationController + expose :rating + expose_decorated :article, -> { rating.article } + before_action -> { authorize rating } + + def create + rating.save + end + + def update + rating.update rating_params + end + + private + + def rating_params + params.require(:rating).permit(:rate, :user_id, :article_id) + end +end diff --git a/app/decorators/article_decorator.rb b/app/decorators/article_decorator.rb index 3eeb8cce..00f6a3d4 100644 --- a/app/decorators/article_decorator.rb +++ b/app/decorators/article_decorator.rb @@ -13,4 +13,10 @@ def author def sorted_comments object.comments.sorted_by_created_at end + + def rating + return "Nobody has rated on this article yet" if object.ratings.blank? + + "#{object.ratings.average(:rate).round(2)}/5" + end end diff --git a/app/models/article.rb b/app/models/article.rb index 95598af3..647e98f0 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -8,6 +8,7 @@ class Article < ApplicationRecord belongs_to :user belongs_to :company has_many :comments, dependent: :destroy + has_many :ratings paginates_per 5 diff --git a/app/models/rating.rb b/app/models/rating.rb new file mode 100644 index 00000000..5aa01db9 --- /dev/null +++ b/app/models/rating.rb @@ -0,0 +1,10 @@ +class Rating < ApplicationRecord + include PgSearch + + validates_associated :user + validates_associated :article + validates :rate, inclusion: { in: [1, 2, 3, 4, 5] } + + belongs_to :user + belongs_to :article +end diff --git a/app/policies/rating_policy.rb b/app/policies/rating_policy.rb new file mode 100644 index 00000000..777315fa --- /dev/null +++ b/app/policies/rating_policy.rb @@ -0,0 +1,9 @@ +class RatingPolicy < ApplicationPolicy + def create? + user.present? && Rating.find_by(article: record.article, user: record.user).blank? + end + + def update? + user.present? + end +end diff --git a/app/views/articles/_form.html.slim b/app/views/articles/_form.html.slim new file mode 100644 index 00000000..efd16d92 --- /dev/null +++ b/app/views/articles/_form.html.slim @@ -0,0 +1,8 @@ +.row(id="rating_data") + .medium-6.columns + = "Rating: #{article.rating}" + = simple_form_for rating, remote: true do |f| + = f.input :id, as: :hidden + = f.input :rate, as: :hidden + = f.input :user_id, as: :hidden + = f.input :article_id, as: :hidden diff --git a/app/views/articles/show.html.slim b/app/views/articles/show.html.slim index c5648094..2a428010 100644 --- a/app/views/articles/show.html.slim +++ b/app/views/articles/show.html.slim @@ -12,6 +12,10 @@ article.showed = "Published #{distance_of_time_in_words_to_now(article.updated_at)} ago" div = "#{article.author}" + - if policy(rating).update? + .medium-2.columns + div(id="review") + = render "form" = render article.sorted_comments diff --git a/app/views/layouts/company.html.slim b/app/views/layouts/company.html.slim index 5ed0efb9..ab4669ae 100644 --- a/app/views/layouts/company.html.slim +++ b/app/views/layouts/company.html.slim @@ -11,6 +11,7 @@ html class="no-js" lang="en" = csrf_meta_tags = stylesheet_link_tag :application + = stylesheet_link_tag "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" = javascript_tag "window.App = {}" diff --git a/app/views/ratings/create.js.erb b/app/views/ratings/create.js.erb new file mode 100644 index 00000000..1af9eb12 --- /dev/null +++ b/app/views/ratings/create.js.erb @@ -0,0 +1,3 @@ +$("#rating_data").html(""); + +$("#rating_data").append("<%= escape_javascript(render :partial => 'articles/form') %>"); diff --git a/app/views/ratings/update.js.erb b/app/views/ratings/update.js.erb new file mode 100644 index 00000000..1af9eb12 --- /dev/null +++ b/app/views/ratings/update.js.erb @@ -0,0 +1,3 @@ +$("#rating_data").html(""); + +$("#rating_data").append("<%= escape_javascript(render :partial => 'articles/form') %>"); diff --git a/config/routes.rb b/config/routes.rb index d09ada2f..3d234210 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,8 @@ resource :message, only: %i[new create] end + resources :ratings, only: %i[create update] + namespace :v1 do defaults format: :json do resources :companies, only: :authors do diff --git a/db/migrate/20201129151451_create_ratings.rb b/db/migrate/20201129151451_create_ratings.rb new file mode 100644 index 00000000..62d8c9c0 --- /dev/null +++ b/db/migrate/20201129151451_create_ratings.rb @@ -0,0 +1,9 @@ +class CreateRatings < ActiveRecord::Migration[5.2] + def change + create_table :ratings do |t| + t.integer :article_id + t.integer :user_id + t.integer :rate + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f00dd893..f5bec1af 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_06_17_144204) do +ActiveRecord::Schema.define(version: 2020_11_29_151451) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -61,6 +61,12 @@ t.index ["company_id"], name: "index_companies_users_on_company_id" end + create_table "ratings", force: :cascade do |t| + t.integer "article_id" + t.integer "user_id" + t.integer "rate" + end + create_table "static_pages", force: :cascade do |t| t.string "title" t.text "content" @@ -95,6 +101,7 @@ t.string "avatar" t.integer "articles_count", default: 0, null: false t.integer "comments_count", default: 0, null: false + t.integer "week_comments_count", default: 0, null: false t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true diff --git a/spec/factories/ratings.rb b/spec/factories/ratings.rb new file mode 100644 index 00000000..2b7e706f --- /dev/null +++ b/spec/factories/ratings.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :rating do + user + article + rate { 5 } + end +end diff --git a/spec/features/user/ratings/create_spec.rb b/spec/features/user/ratings/create_spec.rb new file mode 100644 index 00000000..3bf13e50 --- /dev/null +++ b/spec/features/user/ratings/create_spec.rb @@ -0,0 +1,20 @@ +require "rails_helper" + +feature "Create Rating" do + include_context "current user signed in" + let(:company) { create :company, owner: current_user } + let(:article) { create :article, :company, user: current_user, company: company } + let(:another_user) { create :user } + let!(:rating) { create :rating, user: another_user, article: article, rate: 5 } + + background do + visit article_path(article) + end + + scenario "User choose 1 point", js: true do + expect(page).to have_content("5.0/5") + star = find(:xpath, "//i[1]") + star.click + expect(page).to have_content("3.0/5") + end +end diff --git a/spec/features/user/ratings/update_spec.rb b/spec/features/user/ratings/update_spec.rb new file mode 100644 index 00000000..285ba50b --- /dev/null +++ b/spec/features/user/ratings/update_spec.rb @@ -0,0 +1,18 @@ +require "rails_helper" + +feature "Update Rating" do + include_context "current user signed in" + let!(:rating) { create :rating, user: current_user, article: article, rate: 4 } + let(:company) { create :company, owner: current_user } + let(:article) { create :article, :company, user: current_user, company: company } + + background do + visit article_path(article) + end + + scenario "User choose 3 points", js: true do + expect(page).to have_content("4.0/5") + find(:xpath, "//i[3]").click + expect(page).to have_content("3.0/5") + end +end From 804de4c8884c4b5683bdf201a135559c8735dbcc Mon Sep 17 00:00:00 2001 From: Mikhail Kilin <1kilinmv1@gmail.com> Date: Sun, 29 Nov 2020 20:56:06 +0300 Subject: [PATCH 2/4] quality fix --- spec/features/user/ratings/create_spec.rb | 2 +- spec/features/user/ratings/update_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/user/ratings/create_spec.rb b/spec/features/user/ratings/create_spec.rb index 3bf13e50..56f73714 100644 --- a/spec/features/user/ratings/create_spec.rb +++ b/spec/features/user/ratings/create_spec.rb @@ -5,9 +5,9 @@ let(:company) { create :company, owner: current_user } let(:article) { create :article, :company, user: current_user, company: company } let(:another_user) { create :user } - let!(:rating) { create :rating, user: another_user, article: article, rate: 5 } background do + create :rating, user: another_user, article: article, rate: 5 visit article_path(article) end diff --git a/spec/features/user/ratings/update_spec.rb b/spec/features/user/ratings/update_spec.rb index 285ba50b..1bbc5f7f 100644 --- a/spec/features/user/ratings/update_spec.rb +++ b/spec/features/user/ratings/update_spec.rb @@ -2,11 +2,11 @@ feature "Update Rating" do include_context "current user signed in" - let!(:rating) { create :rating, user: current_user, article: article, rate: 4 } let(:company) { create :company, owner: current_user } let(:article) { create :article, :company, user: current_user, company: company } background do + create :rating, user: current_user, article: article, rate: 4 visit article_path(article) end From f49fc3d426864582c3230cb459f13988cdfeb968 Mon Sep 17 00:00:00 2001 From: Mikhail Kilin <1kilinmv1@gmail.com> Date: Sat, 12 Dec 2020 19:05:09 +0300 Subject: [PATCH 3/4] fixes --- app/controllers/articles_controller.rb | 5 ++--- app/controllers/ratings_controller.rb | 15 ++++++++++----- app/decorators/article_decorator.rb | 2 +- app/models/article.rb | 2 +- app/models/rating.rb | 8 ++------ app/policies/rating_policy.rb | 9 --------- app/views/articles/_form.html.slim | 5 +---- app/views/articles/show.html.slim | 5 ++--- config/routes.rb | 3 +-- db/migrate/20201129151451_create_ratings.rb | 8 +++++--- db/schema.rb | 11 ++++++++--- 11 files changed, 33 insertions(+), 40 deletions(-) delete mode 100644 app/policies/rating_policy.rb diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index b2f115c7..afa2337e 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -13,7 +13,7 @@ def show private def set_article - Article.includes(:comments).find_by(id: params["id"]) + Article.includes(:comments, :ratings).find_by(id: params["id"]) end def set_comment @@ -21,7 +21,6 @@ def set_comment end def set_rating - Rating.find_by(article: article, user: current_user) || - Rating.new(article: article, user: current_user) + Rating.find_or_initialize_by(article: article, user: current_user) end end diff --git a/app/controllers/ratings_controller.rb b/app/controllers/ratings_controller.rb index fe59bf91..07b6df5d 100644 --- a/app/controllers/ratings_controller.rb +++ b/app/controllers/ratings_controller.rb @@ -1,19 +1,24 @@ class RatingsController < ApplicationController expose :rating - expose_decorated :article, -> { rating.article } - before_action -> { authorize rating } + before_action :authenticate_user! + expose_decorated :article + expose :rating, build: ->(rating_params) { Rating.new(rating_params) } def create - rating.save + redirect_to article_path(article) unless rating.save end def update - rating.update rating_params + redirect_to article_path(article) unless rating.update rating_params end private def rating_params - params.require(:rating).permit(:rate, :user_id, :article_id) + { + user: current_user, + article_id: params[:article_id], + rate: params[:rating][:rate] + } end end diff --git a/app/decorators/article_decorator.rb b/app/decorators/article_decorator.rb index 00f6a3d4..08de3e79 100644 --- a/app/decorators/article_decorator.rb +++ b/app/decorators/article_decorator.rb @@ -15,7 +15,7 @@ def sorted_comments end def rating - return "Nobody has rated on this article yet" if object.ratings.blank? + return "Nobody has rated on this article yet" unless object.ratings.any? "#{object.ratings.average(:rate).round(2)}/5" end diff --git a/app/models/article.rb b/app/models/article.rb index 647e98f0..e841ab01 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -8,7 +8,7 @@ class Article < ApplicationRecord belongs_to :user belongs_to :company has_many :comments, dependent: :destroy - has_many :ratings + has_many :ratings, dependent: :destroy paginates_per 5 diff --git a/app/models/rating.rb b/app/models/rating.rb index 5aa01db9..84342f9d 100644 --- a/app/models/rating.rb +++ b/app/models/rating.rb @@ -1,10 +1,6 @@ class Rating < ApplicationRecord - include PgSearch - - validates_associated :user - validates_associated :article validates :rate, inclusion: { in: [1, 2, 3, 4, 5] } - belongs_to :user - belongs_to :article + belongs_to :user, required: true + belongs_to :article, required: true end diff --git a/app/policies/rating_policy.rb b/app/policies/rating_policy.rb deleted file mode 100644 index 777315fa..00000000 --- a/app/policies/rating_policy.rb +++ /dev/null @@ -1,9 +0,0 @@ -class RatingPolicy < ApplicationPolicy - def create? - user.present? && Rating.find_by(article: record.article, user: record.user).blank? - end - - def update? - user.present? - end -end diff --git a/app/views/articles/_form.html.slim b/app/views/articles/_form.html.slim index efd16d92..2fe219a1 100644 --- a/app/views/articles/_form.html.slim +++ b/app/views/articles/_form.html.slim @@ -1,8 +1,5 @@ .row(id="rating_data") .medium-6.columns = "Rating: #{article.rating}" - = simple_form_for rating, remote: true do |f| - = f.input :id, as: :hidden + = simple_form_for [article, rating], remote: true do |f| = f.input :rate, as: :hidden - = f.input :user_id, as: :hidden - = f.input :article_id, as: :hidden diff --git a/app/views/articles/show.html.slim b/app/views/articles/show.html.slim index 2a428010..ff584ce0 100644 --- a/app/views/articles/show.html.slim +++ b/app/views/articles/show.html.slim @@ -12,9 +12,8 @@ article.showed = "Published #{distance_of_time_in_words_to_now(article.updated_at)} ago" div = "#{article.author}" - - if policy(rating).update? - .medium-2.columns - div(id="review") + .medium-2.columns + div(id="review") = render "form" = render article.sorted_comments diff --git a/config/routes.rb b/config/routes.rb index 3d234210..fe8955e5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,8 +6,6 @@ resource :message, only: %i[new create] end - resources :ratings, only: %i[create update] - namespace :v1 do defaults format: :json do resources :companies, only: :authors do @@ -26,6 +24,7 @@ resources :articles, only: :show do resources :comments, except: %i[index new] + resources :ratings, only: %i[create update] end namespace :admin_scope do diff --git a/db/migrate/20201129151451_create_ratings.rb b/db/migrate/20201129151451_create_ratings.rb index 62d8c9c0..f439137d 100644 --- a/db/migrate/20201129151451_create_ratings.rb +++ b/db/migrate/20201129151451_create_ratings.rb @@ -1,9 +1,11 @@ class CreateRatings < ActiveRecord::Migration[5.2] def change create_table :ratings do |t| - t.integer :article_id - t.integer :user_id - t.integer :rate + t.references :article, foreign_key: true, null: false + t.references :user, foreign_key: true, null: false + t.integer :rate, default: 0, null: false end + + add_index :ratings, [:article_id, :user_id], unique: true end end diff --git a/db/schema.rb b/db/schema.rb index f5bec1af..de904122 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -62,9 +62,12 @@ end create_table "ratings", force: :cascade do |t| - t.integer "article_id" - t.integer "user_id" - t.integer "rate" + t.bigint "article_id", null: false + t.bigint "user_id", null: false + t.integer "rate", default: 0, null: false + t.index ["article_id", "user_id"], name: "index_ratings_on_article_id_and_user_id", unique: true + t.index ["article_id"], name: "index_ratings_on_article_id" + t.index ["user_id"], name: "index_ratings_on_user_id" end create_table "static_pages", force: :cascade do |t| @@ -107,4 +110,6 @@ t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true end + add_foreign_key "ratings", "articles" + add_foreign_key "ratings", "users" end From d983707fcfa12738d0bbeb4b76348231aae8180f Mon Sep 17 00:00:00 2001 From: Mikhail Kilin <1kilinmv1@gmail.com> Date: Sat, 12 Dec 2020 19:09:24 +0300 Subject: [PATCH 4/4] fix --- app/assets/javascripts/rate/rating_file.js | 168 +-------------------- 1 file changed, 1 insertion(+), 167 deletions(-) diff --git a/app/assets/javascripts/rate/rating_file.js b/app/assets/javascripts/rate/rating_file.js index 0a67e9f6..43fa766b 100644 --- a/app/assets/javascripts/rate/rating_file.js +++ b/app/assets/javascripts/rate/rating_file.js @@ -1,167 +1 @@ -const defaults = { - "value": Number($("#rating_rate").val()), - "stars": 5, - "half": false, - "emptyStar": "far fa-star", - "halfStar": "fas fa-star-half-alt", - "filledStar": "fas fa-star", - "color": "#fcd703", - "readonly": false, - "click": function (e) { - console.error("No click callback provided!"); - } -}; - -jQuery.fn.extend({ - rating: function (options = {}) { - return this.each(function () { - if ($(this).attr("rating")) { - $(this).empty(); - } - - this.stars = options.value ? options.value : defaults.value; - this.readonly = options.readonly ? options.readonly : defaults.readonly; - - this.getStars = function () { - return $(this).find($("i")); - }; - - $(this).css({ - "color": options.color ? options.color : defaults.color - }) - .attr("rating", true); - - if (!this.readonly) { - $(this).off('mousemove').on('mousemove', function (e) { - let halfStars = options.half ? options.half : defaults.half; - - if (this.getStars().index(e.target) >= 0) { - if (!halfStars) { - $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); - let index = this.getStars().index(e.target) + 1; - - for (let i = 0; i < this.getStars().length; i++) { - if (i < index) - $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) - } - - } else { - $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); - let extra = 0.5; - - $(this).find("i").css({ - "width": $(this).find("i").outerWidth() - }); - - if (e.offsetX > ($(e.target).outerWidth() / 2)) - extra = 1; - - let index = this.getStars().index(e.target) + extra; - for (let i = 0; i < this.getStars().length; i++) { - if (i + 0.5 < index) { - $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) - } else if (i < index) { - $(this.getStars()[i]).attr("class", options.halfStar ? options.halfStar : defaults.halfStar) - } - } - } - } - }); - - $(this).off('mouseout').on('mouseout', function (e) { - this.printStars(); - }); - - $(this).off('click').on('click', function (e) { - let halfStars = options.half ? options.half : defaults.half; - if (!halfStars) { - this.stars = this.getStars().index(e.target) + 1; - } else { - let extra = 0.5; - if (e.offsetX > ($(e.target).outerWidth() / 2)) - extra = 1; - - this.stars = this.getStars().index(e.target) + extra; - } - - const callback = options.click ? options.click : defaults.click; - callback({ - "stars": this.stars, - "event": e - }); - }); - } - - // Add star elements to the element - const stars = options.stars ? options.stars : defaults.stars; - for (let i = 0; i < stars; i++) { - let star = $("") - .addClass(options.emptyStar ? options.emptyStar : defaults.emptyStar) - .appendTo($(this)); - - if (!this.readonly) { - star.css({ - "cursor": "pointer" - }) - } - - if (i > 1000) - return; - } - - this.printStars = function () { - let halfStars = options.half ? options.half : defaults.half; - if (!halfStars) { - $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); - for (let i = 0; i < this.stars; i++) { - $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) - } - } else { - $(this).find("i").attr("class", options.emptyStar ? options.emptyStar : defaults.emptyStar); - for (let i = 0; i < this.stars; i++) { - if (i < this.stars - 0.5) { - $(this.getStars()[i]).attr("class", options.filledStar ? options.filledStar : defaults.filledStar) - } else { - $(this.getStars()[i]).attr("class", options.halfStar ? options.halfStar : defaults.halfStar) - } - } - } - }; - - if (this.stars > 0) { - this.printStars(); - - const callback = options.click ? options.click : defaults.click; - callback({ - "stars": this.stars - }); - } - }); - } -}) -; - -$(function () { - $("[data-rating-stars]").each(function () { - // Get all data-rating attributes - let d = {}, - re_dataAttr = /^data-rating\-(.+)$/; - - $.each($(this).get(0).attributes, function (index, attr) { - if (re_dataAttr.test(attr.nodeName)) { - let key = attr.nodeName.match(re_dataAttr)[1]; - d[key] = attr.nodeValue; - } - }); - - // Create the click event handler - if (d.input != null) { - d.click = function (e) { - $(d.input).val(e.stars); - } - } - - // Run the rating function on the element - $(this).rating(d); - }); -}); +!function(t){var e={};function r(a){if(e[a])return e[a].exports;var s=e[a]={i:a,l:!1,exports:{}};return t[a].call(s.exports,s,s.exports,r),s.l=!0,s.exports}r.m=t,r.c=e,r.d=function(t,e,a){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var s in t)r.d(a,s,function(e){return t[e]}.bind(null,s));return a},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e){const r={value:Number($("#rating_rate").val()),stars:5,half:!1,emptyStar:"far fa-star",halfStar:"fas fa-star-half-alt",filledStar:"fas fa-star",color:"#fcd703",readonly:!1,click:function(t){console.error("No click callback provided!")}};jQuery.fn.extend({rating:function(t={}){return this.each((function(){$(this).attr("rating")&&$(this).empty(),this.stars=t.value?t.value:r.value,this.readonly=t.readonly?t.readonly:r.readonly,this.getStars=function(){return $(this).find($("i"))},$(this).css({color:t.color?t.color:r.color}).attr("rating",!0),this.readonly||($(this).off("mousemove").on("mousemove",(function(e){let a=t.half?t.half:r.half;if(this.getStars().index(e.target)>=0)if(a){$(this).find("i").attr("class",t.emptyStar?t.emptyStar:r.emptyStar);let a=.5;$(this).find("i").css({width:$(this).find("i").outerWidth()}),e.offsetX>$(e.target).outerWidth()/2&&(a=1);let s=this.getStars().index(e.target)+a;for(let e=0;e$(e.target).outerWidth()/2&&(t=1),this.stars=this.getStars().index(e.target)+t}else this.stars=this.getStars().index(e.target)+1;(t.click?t.click:r.click)({stars:this.stars,event:e})})));const e=t.stars?t.stars:r.stars;for(let a=0;a").addClass(t.emptyStar?t.emptyStar:r.emptyStar).appendTo($(this));if(this.readonly||e.css({cursor:"pointer"}),a>1e3)return}if(this.printStars=function(){if(t.half?t.half:r.half){$(this).find("i").attr("class",t.emptyStar?t.emptyStar:r.emptyStar);for(let e=0;e0){this.printStars();(t.click?t.click:r.click)({stars:this.stars})}}))}}),$((function(){$("[data-rating-stars]").each((function(){let t={},e=/^data-rating\-(.+)$/;$.each($(this).get(0).attributes,(function(r,a){if(e.test(a.nodeName)){let r=a.nodeName.match(e)[1];t[r]=a.nodeValue}})),null!=t.input&&(t.click=function(e){$(t.input).val(e.stars)}),$(this).rating(t)}))}))}]); \ No newline at end of file