-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Reviews to Subjects #597
base: add-shoulda-matchers-gem
Are you sure you want to change the base?
Changes from all commits
79197b9
02a523e
7182369
deaa99e
10f1c23
925652e
c3adc20
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
.rating-container { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
width: 100%; | ||
gap: 12px; | ||
} | ||
|
||
.rating-item { | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.interactive-star { | ||
transition: transform 0.25s ease; | ||
|
||
&:hover { | ||
transform: scale(1.3); | ||
} | ||
} | ||
|
||
.login-container { | ||
display: flex; | ||
gap: 4px; | ||
align-items: center; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
class ReviewsController < ApplicationController | ||
before_action :authenticate_user! | ||
before_action :set_review, only: [:update, :destroy] | ||
|
||
def create | ||
@review = current_user.reviews.create!(subject_id: params[:subject_id], rating: params[:rating]) | ||
|
||
redirect_to subject_path(@review.subject) | ||
end | ||
|
||
def update | ||
@review.update!(rating: params[:rating]) | ||
|
||
redirect_to subject_path(@review.subject) | ||
end | ||
Comment on lines
+5
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you think about combining these two actions and doing an "update or create"? |
||
|
||
def destroy | ||
@review.destroy! | ||
|
||
redirect_to subject_path(@review.subject) | ||
end | ||
|
||
private | ||
|
||
def set_review | ||
@review = current_user.reviews.find(params[:id]) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
class Review < ApplicationRecord | ||
belongs_to :user | ||
belongs_to :subject | ||
|
||
validates :user_id, uniqueness: { scope: :subject_id, message: "You can only review a subject once." } | ||
validates :rating, presence: true, inclusion: { in: 1..5 } | ||
|
||
after_commit :update_subject_rating, if: :saved_change_to_rating? | ||
after_destroy :update_subject_rating | ||
|
||
private | ||
|
||
def update_subject_rating | ||
subject.update_rating | ||
end | ||
end |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,33 @@ | ||||||
<label class="mdc-deprecated-list-item"> | ||||||
Puntuación: | ||||||
<div class="rating-container"> | ||||||
<div class="rating-item"> | ||||||
<button class="material-icons mdc-icon-button">star</button> | ||||||
<%= @subject.average_rating.present? ? @subject.average_rating : "Sin calificar" %> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
</div> | ||||||
<% if current_user %> | ||||||
<div class="rating-item"> | ||||||
<% for value in 1..5 %> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<% filled = @user_review.present? && @user_review.rating >= value %> | ||||||
<% selected = @user_review.present? && @user_review.rating == value %> | ||||||
<% url = selected || @user_review.present? ? review_path(@user_review) : reviews_path %> | ||||||
<% method = selected ? :delete : (@user_review.present? ? :put : :post) %> | ||||||
<%= form_with(url:, method:, local: true) do |f| %> | ||||||
<%= f.hidden_field :subject_id, value: @subject.id %> | ||||||
<%= f.hidden_field :rating, value: %> | ||||||
<%= f.button filled ? 'star' : 'star_outline', class: "material-icons mdc-icon-button interactive-star" %> | ||||||
<% end %> | ||||||
<% end %> | ||||||
</div> | ||||||
<% else %> | ||||||
<%= link_to new_user_session_path do %> | ||||||
<div class="login-container"> | ||||||
Inicia sesión para calificar | ||||||
<span class="material-symbols-outlined"> | ||||||
login | ||||||
</span> | ||||||
</div> | ||||||
<% end %> | ||||||
Comment on lines
+23
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if we show the same stars, but when an unauthenticated user clicks them we redirect them to log in? |
||||||
<% end %> | ||||||
</div> | ||||||
</label> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
class CreateReviews < ActiveRecord::Migration[7.1] | ||
def change | ||
create_table :reviews do |t| | ||
t.references :user, null: false, foreign_key: true | ||
t.references :subject, null: false, foreign_key: true | ||
t.integer :rating | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we make this |
||
|
||
t.timestamps | ||
end | ||
|
||
add_index :reviews, [:user_id, :subject_id], unique: true | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
class AddAverageRatingToSubjects < ActiveRecord::Migration[7.1] | ||
def change | ||
add_column :subjects, :average_rating, :float | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm ok with adding this, but i wonder if we should start with calculating it on the fly |
||
end | ||
end |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe ReviewsController, type: :request do | ||
let(:user) { create(:user) } | ||
let(:subject) { create(:subject) } | ||
let(:review) { create(:review, user: user, subject: subject) } | ||
|
||
before do | ||
# https://github.com/heartcombo/devise/issues/5705 | ||
Rails.application.reload_routes_unless_loaded | ||
end | ||
|
||
before do | ||
sign_in user | ||
end | ||
|
||
describe 'POST #create' do | ||
it 'creates a new review' do | ||
expect { | ||
post reviews_path, params: { subject_id: subject.id, rating: 5 } | ||
}.to change(Review, :count).by(1) | ||
end | ||
|
||
it 'redirects to the subject page' do | ||
post reviews_path, params: { subject_id: subject.id, rating: 5 } | ||
|
||
expect(response).to redirect_to(subject_path(subject)) | ||
end | ||
end | ||
|
||
describe 'PATCH #update' do | ||
it 'updates the review' do | ||
patch review_path(review), params: { rating: 4 } | ||
|
||
expect(review.reload.rating).to eq(4) | ||
end | ||
|
||
it 'redirects to the subject page' do | ||
patch review_path(review), params: { rating: 4 } | ||
|
||
expect(response).to redirect_to(subject_path(review.subject)) | ||
end | ||
end | ||
|
||
describe 'DELETE #destroy' do | ||
it 'deletes the review' do | ||
review | ||
|
||
expect { | ||
delete review_path(review) | ||
}.to change(Review, :count).by(-1) | ||
end | ||
|
||
it 'redirects to the subject page' do | ||
delete review_path(review) | ||
|
||
expect(response).to redirect_to(subject_path(review.subject)) | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
FactoryBot.define do | ||
factory :review do | ||
user | ||
subject | ||
rating { 3 } | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe Review, type: :model do | ||
describe 'associations' do | ||
it { is_expected.to belong_to(:user) } | ||
it { is_expected.to belong_to(:subject) } | ||
end | ||
|
||
describe 'validations' do | ||
subject { create :review } | ||
|
||
it { | ||
is_expected.to validate_uniqueness_of(:user_id) | ||
.scoped_to(:subject_id) | ||
.with_message("You can only review a subject once.") | ||
} | ||
it { is_expected.to validate_presence_of(:rating) } | ||
it { is_expected.to validate_inclusion_of(:rating).in_range(1..5) } | ||
end | ||
|
||
describe 'callbacks' do | ||
context 'calls update_rating on subject after commit' do | ||
let(:review) { build :review } | ||
|
||
it 'calls update_rating on subject after create' do | ||
expect(review.subject).to receive(:update_rating) | ||
review.save! | ||
end | ||
|
||
it 'calls update_rating on subject after update' do | ||
review.save! | ||
expect(review.subject).to receive(:update_rating) | ||
review.update!(rating: 5) | ||
end | ||
|
||
it 'calls update_rating on subject after destroy' do | ||
review.save! | ||
expect(review.reload.subject).to receive(:update_rating) | ||
review.destroy! | ||
end | ||
|
||
it 'does not call update_rating on subject if rating was not changed' do | ||
review.save! | ||
expect(review.subject).not_to receive(:update_rating) | ||
review.update!(rating: review.rating) | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it easy/worth it highlighting all the stars to the left of the one that is on hover?