I have a WorkSpace model that has_many Reviews. The Reviews model has a list of attributes, each with their own average rating calculated. I'd like to allow users to find WorkSpaces with the highest rated attribute of their choice.
I have been able to achieve this using scopes and keeping the logic inside the WorkSpace model.
This is my first attempt to do any kind of logic in Rails and I'm wondering if this logic would be better in the controller instead. It works well, but the info it generates is attached to every WorkSpace and I'm thinking this a little redundant as the only time a user would need to access this data is when they're using the filtering system not every time they click on a WorkSpace.
WorkSpace Model(The half with the logic I'm discussing)
class WorkSpace < ApplicationRecord
belongs_to :user
has_many :reviews, dependent: :delete_all
scope :max_rating, ->(rating) { joins(:reviews)
.group('work_spaces.id')
.order('AVG(reviews.rating) desc')
.having('AVG(reviews.rating) > ?', rating) if rating }
scope :max_bathroom, ->(bathroom) { joins(:reviews)
.group('work_spaces.id')
.order('AVG(reviews.bathroom) desc')
.having('AVG(reviews.bathroom) > ?', bathroom) if bathroom }
scope :max_noise, ->(noise) { joins(:reviews)
.group('work_spaces.id')
.order('AVG(reviews.noise) desc')
.having('AVG(reviews.noise) > ?', noise) if noise }
scope :max_wifi, ->(wifi) { joins(:reviews)
.group('work_spaces.id')
.order('AVG(reviews.wifi) desc')
.having('AVG(reviews.wifi) > ?', wifi) if wifi }
scope :max_seating, ->(seating) { joins(:reviews)
.group('work_spaces.id')
.order('AVG(reviews.seating) desc')
.having('AVG(reviews.seating) > ?', seating) if seating }
def top_avg_rating
WorkSpace.max_rating(2).limit(5)
end
def top_avg_bathroom
WorkSpace.max_bathroom(2).limit(5)
end
def top_avg_noise
WorkSpace.max_noise(2).limit(5)
end
def top_avg_wifi
WorkSpace.max_wifi(2).limit(5)
end
def top_avg_seating
WorkSpace.max_seating(2).limit(5)
end
Review Model
# frozen_string_literal: true
class Review < ApplicationRecord
belongs_to :work_space
belongs_to :user
end
WorkSpace Serializer
class WorkSpaceSerializer < ActiveModel::Serializer
attributes :id,
:place_id,
:lat,
:lng,
:name,
:address,
:photo,
:reviews,
:user,
:count_reviews,
:avg_rating,
:avg_noise,
:avg_wifi,
:avg_bathroom,
:avg_food,
:avg_coffee,
:avg_seating,
:avg_outlet,
:bool_outlet,
:bool_seating,
:bool_coffee,
:bool_food,
:bool_bathroom,
:bool_wifi,
:top_avg_rating,
:top_avg_bathroom,
:top_avg_noise,
:top_avg_wifi,
:top_avg_seating
has_one :user
has_many :reviews
end
Could this or should this type of logic be done in the WorkSpace controller instead? And only accessed when an Axios GET request is made? Or... am I way off base and I should just give up now?
Update
So far I was able to Dry up the scopes with this bit of code.
scope :by_average_for, ->(column) {
joins(:reviews)
.group('work_spaces.id')
.order("AVG(reviews.#{column}) desc")
.having("AVG(reviews.#{column}) > 4", column) if column
}
Thank you, https://stackoverflow.com/users/14660/schwern.
I'm working on implementing the class method next. Can't seem to get that working...