From f07996d762025eb97305e634b0bf9d5a28102e06 Mon Sep 17 00:00:00 2001 From: Matchu Date: Thu, 5 Dec 2013 15:22:43 -0600 Subject: [PATCH] cache pet images on items#show, in case that's what's being a super-slow jerkface --- app/assets/javascripts/items/show.js | 37 +++++++++++++++++++---- app/assets/stylesheets/items/_show.sass | 12 +++++--- app/controllers/items_controller.rb | 5 ++++ app/helpers/items_helper.rb | 40 ++++++++++--------------- app/models/item.rb | 16 +++++----- app/models/pet_type.rb | 22 +++++--------- app/models/pet_type_observer.rb | 8 +++++ app/views/items/show.html.haml | 7 +++-- config/application.rb | 2 +- 9 files changed, 88 insertions(+), 61 deletions(-) create mode 100644 app/models/pet_type_observer.rb diff --git a/app/assets/javascripts/items/show.js b/app/assets/javascripts/items/show.js index bc5548a3..71025148 100644 --- a/app/assets/javascripts/items/show.js +++ b/app/assets/javascripts/items/show.js @@ -2,7 +2,8 @@ var PREVIEW_SWF_ID = 'item-preview-swf', PREVIEW_SWF = document.getElementById(PREVIEW_SWF_ID), - speciesList = $('#item-preview a'), + speciesEls, + petTypeEls, customize_more_el = $('#customize-more'), MainWardrobe; @@ -46,7 +47,7 @@ function PetType() { this.setAsCurrent = function () { PetType.current = this; - speciesList.filter('.current').removeClass('current'); + petTypeEls.filter('.current').removeClass('current'); this.link.addClass('current'); customize_more_el.attr('href', 'http://impress.openneo.net/wardrobe?species=' + this.species_id + @@ -213,16 +214,40 @@ Preview.embed(PREVIEW_SWF_ID); Item.createFromLocation().setAsCurrent(); Item.current.name = $('#item-name').text(); -PetType.createFromLink(speciesList.eq(Math.floor(Math.random()*speciesList.length))).setAsCurrent(); +// Choose only supported species, and remove the unsupported. +var supportedSpeciesIds = $('#item-preview-species').attr('data-supported-species-ids').split(','); +var supportedSpeciesIdPresenceMap = {}; +for(var i = 0; i < supportedSpeciesIds.length; i++) { + supportedSpeciesIdPresenceMap[supportedSpeciesIds[i]] = true; +} +speciesEls = $('#item-preview-species > li').filter(function() { + var supported = supportedSpeciesIdPresenceMap[this.getAttribute('data-id')]; + if(!supported) this.parentNode.removeChild(this); + return supported; +}); -speciesList.each(function () { +// Choose random pet type for each species. +speciesEls.each(function() { + var speciesPetTypeEls = $(this).find('.pet-type'); + var chosen = speciesPetTypeEls.eq(Math.floor(Math.random()*speciesPetTypeEls.length)); + speciesPetTypeEls.not(chosen).remove(); +}); + +petTypeEls = speciesEls.find('.pet-type'); + +// Choose random starting pet type +PetType.createFromLink(petTypeEls.eq(Math.floor(Math.random()*petTypeEls.length))).setAsCurrent(); + +// Setup pet type click behavior +petTypeEls.each(function () { var el = $(this); PetType.createFromLink(el); -}).live('click', function (e) { - e.preventDefault(); +}).click(function (e) { PetType.all[$(this).data('id')].setAsCurrent(); }); +// Load the other pet type data in 5 seconds, to save database effort in case +// the user decides to bounce. setTimeout($.proxy(Item.current, 'loadAllStandard'), 5000); window.MainWardrobe = {View: {Outfit: {setFlashIsReady: previewSWFIsReady}}} diff --git a/app/assets/stylesheets/items/_show.sass b/app/assets/stylesheets/items/_show.sass index f47a78bb..9264f3c4 100644 --- a/app/assets/stylesheets/items/_show.sass +++ b/app/assets/stylesheets/items/_show.sass @@ -36,17 +36,18 @@ body.items-show #item-preview +clearfix - div + > div, > ul float: left #item-preview-species + display: block width: 400px - a, img + .pet-type, img height: 50px width: 50px - a + .pet-type +inline-block &.current background: $module-bg-color @@ -58,7 +59,10 @@ body.items-show &.current background: transparent outline-color: $error_border_color - + + ul, li + display: inline + #item-preview-error display: none padding: 20px 10px 0 diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb index f29a2d11..1a43e021 100644 --- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -73,6 +73,11 @@ class ItemsController < ApplicationController @contributors_with_counts = @item.contributors_with_counts end + @supported_species_ids = @item.supported_species_ids + unless localized_fragment_exist?("items/show standard_species_images special_color=#{@item.special_color_id}") + @basic_colored_pet_types_by_species_id = PetType.special_color_or_basic(@item.special_color).includes_child_translations.group_by(&:species) + end + @trading_closet_hangers_by_owned = { true => @item.closet_hangers.owned_trading.newest.includes(:user), false => @item.closet_hangers.wanted_trading.newest.includes(:user) diff --git a/app/helpers/items_helper.rb b/app/helpers/items_helper.rb index e27c49f1..bee6f618 100644 --- a/app/helpers/items_helper.rb +++ b/app/helpers/items_helper.rb @@ -27,31 +27,21 @@ module ItemsHelper end end - def standard_species_images_key_for(item) - versions_count = 1 # TODO - "foo" # TODO - end - - def standard_species_images_for(item) - build_on_pet_types(item.supported_species, item.special_color) do |pet_type| - image = pet_type_image(pet_type, :happy, :face) - attributes = { - 'data-id' => pet_type.id, - 'data-body-id' => pet_type.body_id - } - [:color, :species].each do |pet_type_attribute_name| - pet_type_attribute = pet_type.send(pet_type_attribute_name) - [:id, :name].each do |subattribute_name| - attributes["data-#{pet_type_attribute_name}-#{subattribute_name}"] = - pet_type_attribute.send(subattribute_name) - end - end - link_to( - image, - '#', - attributes - ) - end + def standard_species_images_for(pet_types_by_species_id) + pet_types_by_species_id.to_a.sort_by { |s, pt| s.name }.map { |species, pet_types| + pet_type_images = pet_types.map { |pet_type| + image = pet_type_image(pet_type, :happy, :face) + content_tag(:li, image, 'class' => 'pet-type', + 'data-id' => pet_type.id, + 'data-body-id' => pet_type.body_id, + 'data-color-id' => pet_type.color.id, + 'data-color-name' => pet_type.color.name, + 'data-species-id' => pet_type.species.id, + 'data-species-name' => pet_type.species.name) + }.join.html_safe + content_tag(:li, content_tag(:ul, pet_type_images), + 'data-id' => species.id) + }.join.html_safe end def closet_list_verb(owned) diff --git a/app/models/item.rb b/app/models/item.rb index 69b5f860..e467b3d4 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -154,6 +154,10 @@ class Item < ActiveRecord::Base @special_color ||= determine_special_color end + def special_color_id + special_color.try(:id) + end + protected def determine_special_color I18n.with_locale(I18n.default_locale) do @@ -186,22 +190,18 @@ class Item < ActiveRecord::Base replacement = replacement.join(',') if replacement.is_a?(Array) write_attribute('species_support_ids', replacement) end - - def supported_species + + def supported_species_ids body_ids = swf_assets.select([:body_id]).map(&:body_id) - return Species.all if body_ids.include?(0) + return Species.select([:id]).map(&:id) if body_ids.include?(0) pet_types = PetType.where(:body_id => body_ids).select('DISTINCT species_id') species_ids = pet_types.map(&:species_id) # If there are multiple known supported species, it probably supports them # all. (I've never heard of only a handful of species being supported :P) - species_ids.size > 1 ? Species.all : Species.find(species_ids) - end - - def supported_species_ids - supported_species.map(&:id) + species_ids.size >= 2 ? Species.select([:id]).map(&:id) : species_ids end def support_species?(species) diff --git a/app/models/pet_type.rb b/app/models/pet_type.rb index c92d9ac6..bceedffb 100644 --- a/app/models/pet_type.rb +++ b/app/models/pet_type.rb @@ -21,23 +21,16 @@ class PetType < ActiveRecord::Base scope :includes_child_translations, lambda { includes({:color => :translations, :species => :translations}) } - - def self.standard_pet_types_by_species_id - # If we include Color.basic in the query, rather than getting the ID array - # first, it'll do this as a big fat JOIN unnecessarily. On production, - # there are tons of pet types and translations to send down, so tons of - # duplicate data is sent and must be merged together properly, both of - # which take serious time. So, this is a surprisingly significant - # performance boost - though I was surprised and impressed that Rails - # includes relations as subqueries. That's cool. - basic_color_ids = Color.basic.select([:id]).map(&:id) - PetType.where(color_id: basic_color_ids).includes_child_translations. - group_by(&:species_id) + + def self.special_color_or_basic(special_color) + color_ids = special_color ? [special_color.id] : Color.basic.select([:id]).map(&:id) + where(color_id: color_ids) end def self.standard_body_ids [].tap do |body_ids| - standard_pet_types_by_species_id.each do |species_id, pet_types| + # TODO: the nil hack is lame :P + special_color_or_basic(nil).group_by(&:species_id).each do |species_id, pet_types| body_ids.concat(pet_types.map(&:body_id)) end end @@ -45,7 +38,8 @@ class PetType < ActiveRecord::Base def self.random_basic_per_species(species_ids) random_pet_types = [] - standards = self.standard_pet_types_by_species_id + # TODO: omg so lame :P + standards = special_color_or_basic(nil).group_by(&:species_id) species_ids.each do |species_id| pet_types = standards[species_id] random_pet_types << pet_types[rand(pet_types.size)] if pet_types diff --git a/app/models/pet_type_observer.rb b/app/models/pet_type_observer.rb new file mode 100644 index 00000000..2d5b4260 --- /dev/null +++ b/app/models/pet_type_observer.rb @@ -0,0 +1,8 @@ +class PetTypeObserver < ActiveRecord::Observer + include FragmentExpiration + + def after_create(pet_type) + images_key = "items/show standard_species_images special_color=#{pet_type.color_id}" + expire_fragment_in_all_locales(images_key) + end +end diff --git a/app/views/items/show.html.haml b/app/views/items/show.html.haml index da40a2f2..b0002b44 100644 --- a/app/views/items/show.html.haml +++ b/app/views/items/show.html.haml @@ -78,9 +78,10 @@ :class => 'button' #item-preview - #item-preview-species - -# localized_cache random_standard_species_images_key do - = standard_species_images_for(@item) + %ul#item-preview-species{'data-supported-species-ids' => @supported_species_ids.join(',')} + -# TODO: filter by compatibility + - localized_cache "items/show standard_species_images special_color=#{@item.special_color_id}" do + = standard_species_images_for(@basic_colored_pet_types_by_species_id) #item-preview-error #item-preview-swf= t '.preview.requirements_not_met' diff --git a/config/application.rb b/config/application.rb index 80cb0467..435f2a02 100644 --- a/config/application.rb +++ b/config/application.rb @@ -20,7 +20,7 @@ module OpenneoImpressItems # config.plugins = [ :exception_notification, :ssl_requirement, :all ] # Activate observers that should always be running - config.active_record.observers = :contribution_observer, :item_observer, :user_sweeper + config.active_record.observers = :contribution_observer, :item_observer, :pet_type_observer, :user_sweeper # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.