2011-07-12 17:03:04 -07:00
|
|
|
class ClosetHangersController < ApplicationController
|
2023-08-02 16:05:02 -07:00
|
|
|
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]
|
2011-07-15 20:14:26 -07:00
|
|
|
|
|
|
|
def destroy
|
2015-07-19 09:35:49 -07:00
|
|
|
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 {
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t("closet_hangers.destroy_all.success")
|
2015-07-19 09:35:49 -07:00
|
|
|
redirect_back!(user_closet_hangers_path(current_user))
|
|
|
|
}
|
|
|
|
|
|
|
|
format.json { render :json => true }
|
|
|
|
end
|
2015-09-26 19:55:09 -07:00
|
|
|
elsif params[:ids]
|
|
|
|
ClosetHanger.transaction do
|
|
|
|
current_user.closet_hangers.where(id: params[:ids]).destroy_all
|
|
|
|
end
|
|
|
|
render json: true
|
2015-07-19 09:35:49 -07:00
|
|
|
else
|
|
|
|
@closet_hanger = current_user.closet_hangers.find params[:id]
|
|
|
|
@closet_hanger.destroy
|
|
|
|
@item = @closet_hanger.item
|
|
|
|
closet_hanger_destroyed
|
|
|
|
end
|
2011-07-15 20:14:26 -07:00
|
|
|
end
|
2011-07-14 09:50:24 -07:00
|
|
|
|
2011-07-12 17:03:04 -07:00
|
|
|
def index
|
2023-08-03 17:40:52 -07:00
|
|
|
is_user = user_signed_in? && current_user == @user
|
|
|
|
@public_perspective = params.has_key?(:public) || !is_user
|
2012-04-08 13:59:51 -07:00
|
|
|
@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)
|
2011-07-30 20:03:43 -07:00
|
|
|
|
2024-02-20 18:42:42 -08:00
|
|
|
@items = []
|
2013-12-27 18:48:28 -08:00
|
|
|
|
|
|
|
@closet_lists_by_owned.each do |owned, lists|
|
|
|
|
lists.each do |list|
|
|
|
|
list.hangers.each do |hanger|
|
2024-02-20 18:42:42 -08:00
|
|
|
@items << hanger.item
|
2011-07-30 20:03:43 -07:00
|
|
|
end
|
|
|
|
end
|
2013-12-27 18:48:28 -08:00
|
|
|
end
|
2011-07-30 20:03:43 -07:00
|
|
|
|
2013-12-27 18:48:28 -08:00
|
|
|
@unlisted_closet_hangers_by_owned.each do |owned, hangers|
|
|
|
|
hangers.each do |hanger|
|
2024-02-20 18:42:42 -08:00
|
|
|
@items << hanger.item
|
2011-07-30 20:03:43 -07:00
|
|
|
end
|
2013-12-27 18:48:28 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
if @public_perspective && user_signed_in?
|
2024-02-20 18:42:42 -08:00
|
|
|
current_user.assign_closeted_to_items!(@items)
|
2011-07-30 20:03:43 -07:00
|
|
|
end
|
2015-09-05 10:53:15 -07:00
|
|
|
|
2024-02-18 20:29:31 -08:00
|
|
|
@campaign = Fundraising::Campaign.current
|
2011-07-12 17:03:04 -07:00
|
|
|
end
|
2011-07-14 09:50:24 -07:00
|
|
|
|
2011-07-30 23:48:16 -07:00
|
|
|
def petpage
|
2012-04-08 13:59:51 -07:00
|
|
|
# Find all closet lists, and also the hangers of the visible closet lists
|
2023-11-06 12:55:03 -08:00
|
|
|
closet_lists = @user.closet_lists.select([
|
|
|
|
:id, :name, :hangers_owned, :description]).alphabetical
|
2012-04-08 13:59:51 -07:00
|
|
|
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
|
2023-07-29 11:37:46 -07:00
|
|
|
visible_closet_lists = closet_lists.publicly_visible
|
2012-04-08 13:59:51 -07:00
|
|
|
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)
|
2011-07-30 23:48:16 -07:00
|
|
|
end
|
|
|
|
|
2011-10-10 19:43:46 -07:00
|
|
|
def create
|
2023-07-29 11:25:25 -07:00
|
|
|
@closet_hanger = current_user.closet_hangers.build(closet_hanger_params)
|
2011-10-10 19:43:46 -07:00
|
|
|
@closet_hanger.item = @item
|
|
|
|
|
|
|
|
if @closet_hanger.save
|
2013-01-02 19:03:45 -08:00
|
|
|
closet_hanger_saved
|
2011-10-10 19:43:46 -07:00
|
|
|
else
|
2013-01-02 19:03:45 -08:00
|
|
|
closet_hanger_invalid
|
2011-10-10 19:43:46 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-07-15 13:15:57 -07:00
|
|
|
def update
|
2015-09-26 19:55:09 -07:00
|
|
|
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])
|
2023-07-29 11:25:25 -07:00
|
|
|
@closet_hanger.attributes = closet_hanger_params
|
2015-09-26 19:55:09 -07:00
|
|
|
@item = @closet_hanger.item
|
2011-07-15 13:15:57 -07:00
|
|
|
|
2015-09-26 19:55:09 -07:00
|
|
|
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
|
2011-07-15 13:15:57 -07:00
|
|
|
end
|
2011-07-15 19:52:53 -07:00
|
|
|
end
|
2011-07-14 09:50:24 -07:00
|
|
|
end
|
2011-10-10 19:43:46 -07:00
|
|
|
|
|
|
|
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
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t('closet_hangers.update_quantities.success',
|
2013-01-02 19:03:45 -08:00
|
|
|
:item_name => @item.name)
|
2011-10-10 19:43:46 -07:00
|
|
|
end
|
|
|
|
rescue ActiveRecord::RecordInvalid => e
|
2013-01-02 19:03:45 -08:00
|
|
|
flash[:alert] = t('closet_hangers.update_quantities.invalid',
|
|
|
|
:errors => e.message)
|
2011-10-10 19:43:46 -07:00
|
|
|
end
|
|
|
|
redirect_to @item
|
|
|
|
end
|
2011-07-15 13:15:57 -07:00
|
|
|
|
2013-01-02 19:03:45 -08:00
|
|
|
private
|
2023-07-29 11:25:25 -07:00
|
|
|
|
|
|
|
def closet_hanger_params
|
|
|
|
params.require(:closet_hanger).permit(:list_id, :owned, :quantity)
|
|
|
|
end
|
2013-01-02 19:03:45 -08:00
|
|
|
|
|
|
|
def closet_hanger_destroyed
|
|
|
|
respond_to do |format|
|
|
|
|
format.html {
|
|
|
|
ownership_key = @closet_hanger.owned? ? 'owned' : 'wanted'
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t("closet_hangers.destroy.success.#{ownership_key}",
|
2013-01-02 19:03:45 -08:00
|
|
|
: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
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t("closet_hangers.create.success.#{ownership_key}.in_list",
|
2013-01-02 19:03:45 -08:00
|
|
|
:item_name => @item.name,
|
|
|
|
:list_name => @closet_hanger.list.name,
|
|
|
|
:count => @closet_hanger.quantity)
|
|
|
|
else
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t("closet_hangers.create.success.#{ownership_key}.unlisted",
|
2013-01-02 19:03:45 -08:00
|
|
|
:item_name => @item.name,
|
|
|
|
:count => @closet_hanger.quantity)
|
|
|
|
end
|
|
|
|
redirect_back!(@item)
|
|
|
|
}
|
|
|
|
|
|
|
|
format.json { render :json => true }
|
|
|
|
end
|
|
|
|
end
|
2011-07-15 13:15:57 -07:00
|
|
|
|
2011-07-15 20:14:26 -07:00
|
|
|
def find_item
|
|
|
|
@item = Item.find params[:item_id]
|
|
|
|
end
|
|
|
|
|
2011-07-30 23:48:16 -07:00
|
|
|
def find_user
|
2011-07-31 15:45:53 -07:00
|
|
|
if params[:user_id]
|
|
|
|
@user = User.find params[:user_id]
|
2011-07-31 19:17:59 -07:00
|
|
|
elsif user_signed_in?
|
2011-07-31 15:45:53 -07:00
|
|
|
redirect_to user_closet_hangers_path(current_user)
|
2011-07-31 19:17:59 -07:00
|
|
|
else
|
2023-08-06 15:52:05 -07:00
|
|
|
redirect_to new_auth_user_session_path(:return_to => request.fullpath)
|
2011-07-31 15:45:53 -07:00
|
|
|
end
|
2011-07-30 23:48:16 -07:00
|
|
|
end
|
|
|
|
|
Do preloading manually on user list pages, to reduce memory usage
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!
2023-10-27 19:42:02 -07:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
lists.group_by(&:hangers_owned)
|
2012-04-08 13:59:51 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def find_unlisted_closet_hangers_by_owned(visible_groups)
|
2011-07-30 23:48:16 -07:00
|
|
|
unless visible_groups.empty?
|
Do preloading manually on user list pages, to reduce memory usage
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!
2023-10-27 19:42:02 -07:00
|
|
|
hangers = @user.closet_hangers.unlisted.
|
2013-01-11 15:56:33 -08:00
|
|
|
owned_before_wanted.alphabetical_by_item_name.
|
Do preloading manually on user list pages, to reduce memory usage
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!
2023-10-27 19:42:02 -07:00
|
|
|
where(:owned => [visible_groups])
|
|
|
|
ClosetHanger.preload_items(
|
|
|
|
hangers,
|
|
|
|
items_scope: items_scope,
|
|
|
|
)
|
|
|
|
hangers.group_by(&:owned)
|
2011-07-30 23:48:16 -07:00
|
|
|
else
|
2012-04-08 13:59:51 -07:00
|
|
|
{}
|
2011-07-30 23:48:16 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
Do preloading manually on user list pages, to reduce memory usage
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!
2023-10-27 19:42:02 -07:00
|
|
|
def hangers_scope
|
|
|
|
ClosetHanger.select(:id, :item_id, :list_id, :quantity)
|
|
|
|
end
|
|
|
|
|
|
|
|
def items_scope
|
2024-02-20 15:36:20 -08:00
|
|
|
Item.select(:id, :name, :description, :thumbnail_url, :rarity_index,
|
|
|
|
:is_manually_nc)
|
Do preloading manually on user list pages, to reduce memory usage
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!
2023-10-27 19:42:02 -07:00
|
|
|
end
|
|
|
|
|
2011-07-22 14:55:05 -07:00
|
|
|
def owned
|
|
|
|
owned = true
|
2023-07-29 11:25:25 -07:00
|
|
|
if closet_hanger_params
|
|
|
|
owned = case closet_hanger_params[:owned]
|
2011-07-22 14:55:05 -07:00
|
|
|
when 'true', '1' then true
|
|
|
|
when 'false', '0' then false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-07-12 17:03:04 -07:00
|
|
|
end
|
|
|
|
|