Matchu
7948974949
I used the new profiler tools on this page, and noticed a lot of allocations in the Globalize library, which we use for translating database records. I realized that we were loading all of the fields of not just all of the items on the page, but all of their translation records in all locales! We used to scrape data for lots of languages, so that can be quite a lot! Unfortunately, Rails's `includes` method to efficiently preload related records always loads all fields, and simply can't be overridden. So, in this change we write manual preloading code, to identify the records we need, load them in big bulk queries, and assign them back to the appropriate associations. Basically just what `includes` does, but written out a bit more, to give us the chance to specify SELECT and WHERE clauses!
281 lines
8.7 KiB
Ruby
281 lines
8.7 KiB
Ruby
class ClosetHangersController < ApplicationController
|
|
before_action :authorize_user!, :only => [:destroy, :create, :update, :update_quantities, :petpage]
|
|
before_action :find_item, :only => [:create, :update_quantities]
|
|
before_action :find_user, :only => [:index, :petpage, :update_quantities]
|
|
|
|
def destroy
|
|
if params[:list_id]
|
|
@closet_list = current_user.find_closet_list_by_id_or_null_owned params[:list_id]
|
|
@closet_list.hangers.destroy_all
|
|
respond_to do |format|
|
|
format.html {
|
|
flash[:notice] = t("closet_hangers.destroy_all.success")
|
|
redirect_back!(user_closet_hangers_path(current_user))
|
|
}
|
|
|
|
format.json { render :json => true }
|
|
end
|
|
elsif params[:ids]
|
|
ClosetHanger.transaction do
|
|
current_user.closet_hangers.where(id: params[:ids]).destroy_all
|
|
end
|
|
render json: true
|
|
else
|
|
@closet_hanger = current_user.closet_hangers.find params[:id]
|
|
@closet_hanger.destroy
|
|
@item = @closet_hanger.item
|
|
closet_hanger_destroyed
|
|
end
|
|
end
|
|
|
|
def index
|
|
is_user = user_signed_in? && current_user == @user
|
|
@public_perspective = params.has_key?(:public) || !is_user
|
|
@perspective_user = current_user unless @public_perspective
|
|
closet_lists = @user.closet_lists
|
|
unless @perspective_user == @user
|
|
# If we run this when the user matches, we'll end up with effectively:
|
|
# WHERE belongs_to_user AND (is_public OR belongs_to_user)
|
|
# and it's a bit silly to put the SQL server through a condition that's
|
|
# always true.
|
|
closet_lists = closet_lists.visible_to(@perspective_user)
|
|
end
|
|
@closet_lists_by_owned = find_closet_lists_by_owned(closet_lists)
|
|
|
|
visible_groups = @user.closet_hangers_groups_visible_to(@perspective_user)
|
|
@unlisted_closet_hangers_by_owned = find_unlisted_closet_hangers_by_owned(visible_groups)
|
|
|
|
items = []
|
|
|
|
@closet_lists_by_owned.each do |owned, lists|
|
|
lists.each do |list|
|
|
list.hangers.each do |hanger|
|
|
items << hanger.item
|
|
end
|
|
end
|
|
end
|
|
|
|
@unlisted_closet_hangers_by_owned.each do |owned, hangers|
|
|
hangers.each do |hanger|
|
|
items << hanger.item
|
|
end
|
|
end
|
|
|
|
if @public_perspective && user_signed_in?
|
|
current_user.assign_closeted_to_items!(items)
|
|
end
|
|
|
|
@campaign = Campaign.current
|
|
end
|
|
|
|
def petpage
|
|
# Find all closet lists, and also the hangers of the visible closet lists
|
|
closet_lists = @user.closet_lists.select([:id, :name, :hangers_owned]).alphabetical
|
|
if params[:filter]
|
|
# If user specified which lists should be visible, restrict to those
|
|
if params[:lists] && params[:lists].respond_to?(:keys)
|
|
visible_closet_lists = closet_lists.where(:id => params[:lists].keys)
|
|
else
|
|
visible_closet_lists = []
|
|
end
|
|
else
|
|
# Otherwise, default to public lists
|
|
visible_closet_lists = closet_lists.publicly_visible
|
|
end
|
|
@closet_lists_by_owned = closet_lists.group_by(&:hangers_owned)
|
|
@visible_closet_lists_by_owned = find_closet_lists_by_owned(visible_closet_lists)
|
|
|
|
# Find which groups (own/want) should be visible
|
|
if params[:filter]
|
|
# If user specified which groups should be visible, restrict to those
|
|
# (must be either true or false)
|
|
@visible_groups = []
|
|
if params[:groups] && params[:groups].respond_to?(:keys)
|
|
@visible_groups << true if params[:groups].keys.include?('true')
|
|
@visible_groups << false if params[:groups].keys.include?('false')
|
|
end
|
|
else
|
|
# Otherwise, default to public groups
|
|
@visible_groups = @user.public_closet_hangers_groups
|
|
end
|
|
|
|
@visible_unlisted_closet_hangers_by_owned =
|
|
find_unlisted_closet_hangers_by_owned(@visible_groups)
|
|
end
|
|
|
|
def create
|
|
@closet_hanger = current_user.closet_hangers.build(closet_hanger_params)
|
|
@closet_hanger.item = @item
|
|
|
|
if @closet_hanger.save
|
|
closet_hanger_saved
|
|
else
|
|
closet_hanger_invalid
|
|
end
|
|
end
|
|
|
|
def update
|
|
if params[:ids]
|
|
ClosetHanger.transaction do
|
|
@closet_hangers = current_user.closet_hangers.includes(:list).find params[:ids]
|
|
@closet_hangers.each do |h|
|
|
h.possibly_null_list_id = params[:list_id]
|
|
h.save!
|
|
end
|
|
end
|
|
redirect_back!(user_closet_hangers_path(current_user))
|
|
else
|
|
@closet_hanger = current_user.closet_hangers.find(params[:id])
|
|
@closet_hanger.attributes = closet_hanger_params
|
|
@item = @closet_hanger.item
|
|
|
|
unless @closet_hanger.quantity == 0 # save the hanger, new record or not
|
|
if @closet_hanger.save
|
|
closet_hanger_saved
|
|
else
|
|
closet_hanger_invalid
|
|
end
|
|
else # delete the hanger since the user doesn't want it
|
|
@closet_hanger.destroy
|
|
closet_hanger_destroyed
|
|
end
|
|
end
|
|
end
|
|
|
|
def update_quantities
|
|
begin
|
|
ClosetHanger.transaction do
|
|
params[:quantity].each do |key, quantity|
|
|
ClosetHanger.set_quantity!(quantity, :user_id => @user.id,
|
|
:item_id => @item.id, :key => key)
|
|
end
|
|
flash[:notice] = t('closet_hangers.update_quantities.success',
|
|
:item_name => @item.name)
|
|
end
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
flash[:alert] = t('closet_hangers.update_quantities.invalid',
|
|
:errors => e.message)
|
|
end
|
|
redirect_to @item
|
|
end
|
|
|
|
private
|
|
|
|
def closet_hanger_params
|
|
params.require(:closet_hanger).permit(:list_id, :owned, :quantity)
|
|
end
|
|
|
|
def closet_hanger_destroyed
|
|
respond_to do |format|
|
|
format.html {
|
|
ownership_key = @closet_hanger.owned? ? 'owned' : 'wanted'
|
|
flash[:notice] = t("closet_hangers.destroy.success.#{ownership_key}",
|
|
:item_name => @item.name)
|
|
redirect_back!(@item)
|
|
}
|
|
|
|
format.json { render :json => true }
|
|
end
|
|
end
|
|
|
|
def closet_hanger_invalid
|
|
respond_to do |format|
|
|
format.html {
|
|
ownership_key = @closet_hanger.owned? ? 'owned' : 'wanted'
|
|
flash[:alert] = t("closet_hangers.create.invalid.#{ownership_key}",
|
|
:item_name => @item.name,
|
|
:errors => @closet_hanger.errors.full_messages.to_sentence)
|
|
redirect_back!(@item)
|
|
}
|
|
|
|
format.json { render :json => {:errors => @closet_hanger.errors.full_messages}, :status => :unprocessable_entity }
|
|
end
|
|
end
|
|
|
|
def closet_hanger_saved
|
|
respond_to do |format|
|
|
format.html {
|
|
ownership_key = @closet_hanger.owned? ? 'owned' : 'wanted'
|
|
if @closet_hanger.list
|
|
flash[:notice] = t("closet_hangers.create.success.#{ownership_key}.in_list",
|
|
:item_name => @item.name,
|
|
:list_name => @closet_hanger.list.name,
|
|
:count => @closet_hanger.quantity)
|
|
else
|
|
flash[:notice] = t("closet_hangers.create.success.#{ownership_key}.unlisted",
|
|
:item_name => @item.name,
|
|
:count => @closet_hanger.quantity)
|
|
end
|
|
redirect_back!(@item)
|
|
}
|
|
|
|
format.json { render :json => true }
|
|
end
|
|
end
|
|
|
|
def find_item
|
|
@item = Item.find params[:item_id]
|
|
end
|
|
|
|
def find_user
|
|
if params[:user_id]
|
|
@user = User.find params[:user_id]
|
|
elsif user_signed_in?
|
|
redirect_to user_closet_hangers_path(current_user)
|
|
else
|
|
redirect_to new_auth_user_session_path(:return_to => request.fullpath)
|
|
end
|
|
end
|
|
|
|
def find_closet_lists_by_owned(lists_scope)
|
|
return {} if lists_scope == []
|
|
lists = lists_scope.alphabetical
|
|
ClosetList.preload_items(
|
|
lists,
|
|
hangers_scope: hangers_scope,
|
|
items_scope: items_scope,
|
|
item_translations_scope: item_translations_scope,
|
|
)
|
|
lists.group_by(&:hangers_owned)
|
|
end
|
|
|
|
def find_unlisted_closet_hangers_by_owned(visible_groups)
|
|
unless visible_groups.empty?
|
|
hangers = @user.closet_hangers.unlisted.
|
|
owned_before_wanted.alphabetical_by_item_name.
|
|
where(:owned => [visible_groups])
|
|
ClosetHanger.preload_items(
|
|
hangers,
|
|
items_scope: items_scope,
|
|
item_translations_scope: item_translations_scope,
|
|
)
|
|
hangers.group_by(&:owned)
|
|
else
|
|
{}
|
|
end
|
|
end
|
|
|
|
def hangers_scope
|
|
ClosetHanger.select(:id, :item_id, :list_id, :quantity)
|
|
end
|
|
|
|
def items_scope
|
|
Item.select(:id, :thumbnail_url, :rarity_index)
|
|
end
|
|
|
|
def item_translations_scope
|
|
Item::Translation.select(:id, :item_id, :locale, :name, :description).
|
|
where(locale: I18n.locale)
|
|
end
|
|
|
|
def owned
|
|
owned = true
|
|
if closet_hanger_params
|
|
owned = case closet_hanger_params[:owned]
|
|
when 'true', '1' then true
|
|
when 'false', '0' then false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|