require 'ostruct' class Pool < ActiveRecord::Base validates_uniqueness_of :name validates_format_of :name, :with => /\A[^\s;,]+\Z/, :on => :create, :message => "cannot have whitespace, commas, or semicolons" belongs_to :creator, :class_name => "User" belongs_to :updater, :class_name => "User" has_many :versions, :class_name => "PoolVersion", :dependent => :destroy, :order => "pool_versions.id ASC" before_validation :normalize_name before_validation :normalize_post_ids before_validation :initialize_creator, :on => :create after_save :create_version attr_accessible :name, :description, :post_ids, :is_active, :post_count def self.name_to_id(name) if name =~ /^\d+$/ name.to_i else select_value_sql("SELECT id FROM pools WHERE name = ?", name.downcase).to_i end end def self.id_to_name(id) select_value_sql("SELECT name FROM pools WHERE id = ?", id) end def self.create_anonymous(creator, creator_ip_addr) Pool.new do |pool| pool.name = "TEMP:#{Time.now.to_f}.#{rand(1_000_000)}" pool.creator = creator pool.save pool.name = "anon:#{pool.id}" pool.save end end def self.normalize_name(name) name.downcase.gsub(/\s+/, "_") end def self.normalize_post_ids(post_ids) post_ids.gsub(/\s{2,}/, " ").strip end def initialize_creator self.creator_id = CurrentUser.id end def normalize_name self.name = self.class.normalize_name(name) end def normalize_post_ids self.post_ids = self.class.normalize_post_ids(post_ids) end def revert_to!(version) self.post_ids = version.post_ids synchronize! end def contains?(post_id) post_ids =~ /(?:\A| )#{post_id}(?:\Z| )/ end def add!(post) return if contains?(post.id) update_attributes(:post_ids => add_number_to_string(post.id, post_ids), :post_count => post_count + 1) post.add_pool!(self) clear_post_id_array end def remove!(post) return unless contains?(post.id) update_attributes(:post_ids => remove_number_from_string(post.id, post_ids), :post_count => post_count - 1) post.remove_pool!(self) clear_post_id_array end def add_number_to_string(number, string) "#{string} #{number}" end def remove_number_from_string(number, string) string.gsub(/(?:\A| )#{number}(?:\Z| )/, " ") end def posts(options = {}) offset = options[:offset] || 0 limit = options[:limit] || Danbooru.config.posts_per_page slice = post_id_array.slice(offset, limit) if slice && slice.any? Post.where("id in (?)", slice).order(arbitrary_sql_order_clause(slice, "posts")) else Post.where("false") end end def synchronize! added = post_id_array - post_id_array_was removed = post_id_array_was - post_id_array added.each do |post_id| post = Post.find(post_id) post.add_pool!(self) end removed.each do |post_id| post = Post.find(post_id) post.remove_pool!(self) end self.post_count = post_id_array.size save end def post_id_array @post_id_array ||= post_ids.scan(/\d+/).map(&:to_i) end def post_id_array_was @post_id_array_was ||= post_ids_was.scan(/\d+/).map(&:to_i) end def clear_post_id_array @post_id_array = nil @post_id_array_was = nil end def neighbors(post) @neighbor_posts ||= begin post_ids =~ /\A#{post.id} (\d+)|(\d+) #{post.id} (\d+)|(\d+) #{post.id}\Z/ if $2 && $3 OpenStruct.new(:previous => $2.to_i, :next => $3.to_i) elsif $1 OpenStruct.new(:next => $1.to_i) elsif $4 OpenStruct.new(:previous => $4.to_i) else OpenStruct.new end end end def create_version last_version = versions.last if last_version && CurrentUser.ip_addr == last_version.updater_ip_addr && CurrentUser.id == last_version.updater_id last_version.update_attribute(:post_ids, post_ids) else versions.create(:post_ids => post_ids) end end def reload(options = {}) super @neighbor_posts = nil clear_post_id_array end end