158 lines
4.1 KiB
Ruby
158 lines
4.1 KiB
Ruby
class ClosetList < ApplicationRecord
|
|
belongs_to :user
|
|
has_many :hangers, class_name: 'ClosetHanger', foreign_key: 'list_id', dependent: :destroy
|
|
|
|
validates :name, :presence => true, :uniqueness => {:scope => :user_id}
|
|
validates :user, :presence => true
|
|
validates :hangers_owned, :inclusion => {:in => [true, false], :message => "can't be blank"}
|
|
validates :description, format: {
|
|
# This isn't a very careful email filter! It's easy to avoid. But the
|
|
# purpose is to communicate the rules, more than actual prevention.
|
|
without: /\b([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\b/i, # from emailregex.com
|
|
message: "must not contain email addresses"
|
|
}
|
|
|
|
delegate :log_trade_activity, to: :user
|
|
|
|
scope :alphabetical, -> { order(:name) }
|
|
scope :publicly_visible, -> {
|
|
where(arel_table[:visibility].gteq(ClosetVisibility[:public].id))
|
|
}
|
|
scope :trading, -> {
|
|
where(arel_table[:visibility].gteq(ClosetVisibility[:trading].id))
|
|
}
|
|
scope :visible_to, ->(user) {
|
|
condition = arel_table[:visibility].gteq(ClosetVisibility[:public].id)
|
|
condition = condition.or(arel_table[:user_id].eq(user.id)) if user
|
|
where(condition)
|
|
}
|
|
|
|
after_save :sync_hangers_owned!
|
|
after_save :log_trade_activity, if: :trading?
|
|
after_destroy :log_trade_activity, if: :trading?
|
|
|
|
def trading?
|
|
visibility >= ClosetVisibility[:trading].id
|
|
end
|
|
|
|
def sync_hangers_owned!
|
|
if hangers_owned_changed?
|
|
hangers.each do |hanger|
|
|
hanger.owned = hangers_owned
|
|
hanger.save!
|
|
end
|
|
end
|
|
end
|
|
|
|
def try_non_null(method_name)
|
|
send(method_name)
|
|
end
|
|
|
|
def self.preload_items(
|
|
lists,
|
|
hangers_scope: ClosetHanger.all,
|
|
items_scope: Item.all
|
|
)
|
|
# Preload the records we need. (This is like `includes`, but `includes`
|
|
# always selects all fields for all records, and we give the caller the
|
|
# opportunity to specify which fields it actually wants via scope!)
|
|
hangers = hangers_scope.where(list_id: lists.map(&:id))
|
|
|
|
# Group the records by relevant IDs.
|
|
hangers_by_list_id = hangers.group_by(&:list_id)
|
|
|
|
# Assign the preloaded records to the records they belong to. (This is like
|
|
# doing e.g. h.item = ..., but that's a database write - we actually just
|
|
# want to set the `item` field itself directly! Hacky, ripped from how
|
|
# `ActiveRecord::Associations::Preloader` does it!)
|
|
lists.each do |list|
|
|
list.association(:hangers).target = hangers_by_list_id[list.id]
|
|
end
|
|
|
|
# Then, do similar preloading for the hangers and their items.
|
|
ClosetHanger.preload_items(
|
|
hangers,
|
|
items_scope: items_scope,
|
|
)
|
|
end
|
|
|
|
module VisibilityMethods
|
|
delegate :trading?, to: :visibility_level
|
|
|
|
def visibility_level
|
|
ClosetVisibility.levels[visibility]
|
|
end
|
|
|
|
def trading_changed?
|
|
return false unless visibility_changed?
|
|
level_change = visibility_change.map { |v| ClosetVisibility.levels[v] }
|
|
old_trading, new_trading = level_change.map(&:trading?)
|
|
old_trading != new_trading
|
|
end
|
|
end
|
|
|
|
def self.group_by_owned
|
|
{true => [], false => []}.tap do |h|
|
|
all.each { |list| h[list.hangers_owned?] << list}
|
|
end
|
|
end
|
|
|
|
include VisibilityMethods
|
|
|
|
class Null
|
|
include VisibilityMethods
|
|
attr_reader :user
|
|
|
|
def initialize(user)
|
|
@user = user
|
|
end
|
|
|
|
def hangers
|
|
user.closet_hangers.unlisted.where(owned: hangers_owned)
|
|
end
|
|
|
|
def hangers_owned?
|
|
hangers_owned
|
|
end
|
|
|
|
def try_non_null(method_name)
|
|
nil
|
|
end
|
|
end
|
|
|
|
class NullOwned < Null
|
|
def hangers_owned
|
|
true
|
|
end
|
|
|
|
def visibility
|
|
user.owned_closet_hangers_visibility
|
|
end
|
|
|
|
def visibility_changed?
|
|
user.owned_closet_hangers_visibility_changed?
|
|
end
|
|
|
|
def visibility_change
|
|
user.owned_closet_hangers_visibility_change
|
|
end
|
|
end
|
|
|
|
class NullWanted < Null
|
|
def hangers_owned
|
|
false
|
|
end
|
|
|
|
def visibility
|
|
user.wanted_closet_hangers_visibility
|
|
end
|
|
|
|
def visibility_changed?
|
|
user.wanted_closet_hangers_visibility_changed?
|
|
end
|
|
|
|
def visibility_change
|
|
user.wanted_closet_hangers_visibility_change
|
|
end
|
|
end
|
|
end
|