2023-08-02 16:05:02 -07:00
|
|
|
class Outfit < ApplicationRecord
|
2010-11-12 13:31:01 -08:00
|
|
|
has_many :item_outfit_relationships, :dependent => :destroy
|
2023-07-29 12:13:58 -07:00
|
|
|
has_many :worn_item_outfit_relationships, -> { where(is_worn: true) },
|
|
|
|
:class_name => 'ItemOutfitRelationship'
|
2010-11-13 14:26:14 -08:00
|
|
|
has_many :worn_items, :through => :worn_item_outfit_relationships, :source => :item
|
2010-11-10 13:59:54 -08:00
|
|
|
belongs_to :pet_state
|
2023-08-02 17:57:53 -07:00
|
|
|
belongs_to :user, optional: true
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2011-02-10 14:50:47 -08:00
|
|
|
validates :name, :presence => {:if => :user_id}, :uniqueness => {:scope => :user_id, :if => :user_id}
|
2010-11-10 13:59:54 -08:00
|
|
|
validates :pet_state, :presence => true
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2014-03-28 13:15:04 -07:00
|
|
|
delegate :color, to: :pet_state
|
|
|
|
|
2023-07-22 14:04:01 -07:00
|
|
|
scope :wardrobe_order, -> { order('starred DESC', :name) }
|
2012-03-15 15:00:29 -07:00
|
|
|
|
2021-05-24 20:05:25 -07:00
|
|
|
class OutfitImage
|
|
|
|
def initialize(image_versions)
|
|
|
|
@image_versions = image_versions
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2021-05-24 20:05:25 -07:00
|
|
|
def url
|
|
|
|
@image_versions[:large]
|
|
|
|
end
|
|
|
|
|
|
|
|
def large
|
|
|
|
Version.new(@image_versions[:large])
|
|
|
|
end
|
|
|
|
|
|
|
|
def medium
|
|
|
|
Version.new(@image_versions[:medium])
|
|
|
|
end
|
|
|
|
|
|
|
|
def small
|
|
|
|
Version.new(@image_versions[:small])
|
|
|
|
end
|
|
|
|
|
|
|
|
Version = Struct.new(:url)
|
|
|
|
end
|
|
|
|
|
|
|
|
def image?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def image
|
|
|
|
OutfitImage.new(image_versions)
|
2012-07-17 09:15:04 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def image_versions
|
2021-05-24 20:05:25 -07:00
|
|
|
# Now, instead of using the saved outfit to S3, we're using out the
|
2021-06-19 09:10:25 -07:00
|
|
|
# DTI 2020 API + CDN cache version. We use openneo-assets.net to get
|
|
|
|
# around a bug on Neopets petpages with openneo.net URLs.
|
|
|
|
base_url = "https://outfits.openneo-assets.net/outfits" +
|
Start serving outfit images via Impress 2020
As part of our project to get off S3 and dramatically reduce costs, we're gonna start serving outfit images that Impress 2020 generates, fronted by Vercel's CDN cache! This should hopefully be just as fast in practice, without requiring an S3 storage cost. (Outfits whose thumbnails are pretty much unused will be evicted from the cache, or never stored in the first place—and regenerated back into the cache on-demand if needed.)
One important note is that the image at the URL will no longer be guaranteed to auto-update to reflect the changes to the outfit, because we're including `updated_at` in the URL for caching. (It also isn't guaranteed to _not_ auto-update, though 😅) Our hope is that people aren't using it for that use case so much! If so, though, we have some ways we could build live URLs without putting too much pressure on image generation, e.g. redirects 🤔
This change does _not_ disable actual outfit generation, because I want to keep that running until we see these new URLs succeed for folks. Gonna wait a bit and see if we get bug reports on them! Then, if all goes well, we'll stop enqueueing outfit image jobs altogether, and maybe wind down some of the infrastructure accordingly.
2021-05-20 20:52:19 -07:00
|
|
|
"/#{CGI.escape id.to_s}" +
|
|
|
|
"/v/#{CGI.escape updated_at.to_i.to_s}"
|
|
|
|
{
|
|
|
|
large: "#{base_url}/600.png",
|
|
|
|
medium: "#{base_url}/300.png",
|
|
|
|
small: "#{base_url}/150.png",
|
|
|
|
}
|
|
|
|
|
|
|
|
# NOTE: Below is the previous code that uses the saved outfits!
|
|
|
|
# {}.tap do |versions|
|
|
|
|
# versions[:large] = image.url
|
|
|
|
# image.versions.each { |name, version| versions[name] = version.url }
|
|
|
|
# end
|
2010-11-11 10:43:22 -08:00
|
|
|
end
|
2021-05-24 20:05:25 -07:00
|
|
|
|
|
|
|
def as_json(more_options={})
|
|
|
|
serializable_hash :only => [:id, :name, :pet_state_id, :starred],
|
|
|
|
:methods => [:color_id, :species_id, :worn_and_unworn_item_ids,
|
|
|
|
:image_versions, :image_enqueued, :image_layers_hash]
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-13 14:26:14 -08:00
|
|
|
def closet_item_ids
|
|
|
|
item_outfit_relationships.map(&:item_id)
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-11 10:43:22 -08:00
|
|
|
def color_id
|
|
|
|
pet_state.pet_type.color_id
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-11 10:43:22 -08:00
|
|
|
def species_id
|
|
|
|
pet_state.pet_type.species_id
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-13 14:26:14 -08:00
|
|
|
def to_query
|
2012-08-09 15:32:33 -07:00
|
|
|
ids = self.worn_and_unworn_item_ids
|
|
|
|
|
2010-11-13 14:26:14 -08:00
|
|
|
{
|
2012-08-09 15:32:33 -07:00
|
|
|
:closet => ids[:worn] + ids[:unworn],
|
2010-11-13 14:26:14 -08:00
|
|
|
:color => color_id,
|
2012-08-09 15:32:33 -07:00
|
|
|
:objects => ids[:worn],
|
2010-11-13 14:26:14 -08:00
|
|
|
:species => species_id,
|
|
|
|
:state => pet_state_id
|
|
|
|
}.to_query
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-11 10:43:22 -08:00
|
|
|
def worn_and_unworn_item_ids
|
2010-11-10 13:59:54 -08:00
|
|
|
{:worn => [], :unworn => []}.tap do |output|
|
2010-11-11 10:43:22 -08:00
|
|
|
item_outfit_relationships.each do |rel|
|
2010-11-10 13:59:54 -08:00
|
|
|
key = rel.is_worn? ? :worn : :unworn
|
2010-11-11 10:43:22 -08:00
|
|
|
output[key] << rel.item_id
|
2010-11-10 13:59:54 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-13 05:50:37 -08:00
|
|
|
def worn_and_unworn_item_ids=(all_item_ids)
|
|
|
|
new_rels = []
|
|
|
|
all_item_ids.each do |key, item_ids|
|
|
|
|
worn = key == 'worn'
|
2010-11-14 19:31:41 -08:00
|
|
|
unless item_ids.blank?
|
|
|
|
item_ids.each do |item_id|
|
|
|
|
rel = ItemOutfitRelationship.new
|
|
|
|
rel.item_id = item_id
|
|
|
|
rel.is_worn = worn
|
|
|
|
new_rels << rel
|
|
|
|
end
|
2010-11-13 05:50:37 -08:00
|
|
|
end
|
2010-11-10 13:59:54 -08:00
|
|
|
end
|
2010-11-13 05:50:37 -08:00
|
|
|
self.item_outfit_relationships = new_rels
|
2010-11-10 13:59:54 -08:00
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2011-02-10 14:50:47 -08:00
|
|
|
def self.build_for_user(user, params)
|
|
|
|
Outfit.new.tap do |outfit|
|
|
|
|
name = params.delete(:name)
|
|
|
|
starred = params.delete(:starred)
|
2011-03-28 14:29:03 -07:00
|
|
|
anonymous = params.delete(:anonymous) == "true"
|
|
|
|
if user && !anonymous
|
2011-02-10 14:50:47 -08:00
|
|
|
outfit.user = user
|
|
|
|
outfit.name = name
|
|
|
|
outfit.starred = starred
|
|
|
|
end
|
|
|
|
outfit.attributes = params
|
|
|
|
end
|
|
|
|
end
|
2010-11-10 13:59:54 -08:00
|
|
|
end
|