class ChallengeSignup < ApplicationRecord include TagTypeHelper # -1 represents all matching ALL = -1 belongs_to :pseud belongs_to :collection has_many :prompts, dependent: :destroy, inverse_of: :challenge_signup has_many :requests, dependent: :destroy, inverse_of: :challenge_signup has_many :offers, dependent: :destroy, inverse_of: :challenge_signup has_many :offer_potential_matches, class_name: "PotentialMatch", foreign_key: 'offer_signup_id', dependent: :destroy has_many :request_potential_matches, class_name: "PotentialMatch", foreign_key: 'request_signup_id', dependent: :destroy has_many :offer_assignments, class_name: "ChallengeAssignment", foreign_key: 'offer_signup_id' has_many :request_assignments, class_name: "ChallengeAssignment", foreign_key: 'request_signup_id' has_many :request_claims, class_name: "ChallengeClaim", foreign_key: 'request_signup_id' before_destroy :clear_assignments_and_claims def clear_assignments_and_claims # remove this signup reference from any existing assignments offer_assignments.each {|assignment| assignment.offer_signup = nil; assignment.save} request_assignments.each {|assignment| assignment.request_signup = nil; assignment.save} request_claims.each {|claim| claim.destroy} end # we reject prompts if they are empty except for associated references accepts_nested_attributes_for :offers, :prompts, :requests, {allow_destroy: true, reject_if: proc { |attrs| attrs[:url].blank? && attrs[:description].blank? && (attrs[:tag_set_attributes].nil? || attrs[:tag_set_attributes].all? {|k,v| v.blank?}) && (attrs[:optional_tag_set_attributes].nil? || attrs[:optional_tag_set_attributes].all? {|k,v| v.blank?}) } } scope :by_user, lambda {|user| select("DISTINCT challenge_signups.*"). joins(pseud: :user). where('users.id = ?', user.id) } scope :by_pseud, lambda {|pseud| where('pseud_id = ?', pseud.id) } scope :pseud_only, -> { select(:pseud_id) } scope :order_by_pseud, -> { joins(:pseud).order("pseuds.name") } scope :order_by_date, -> { order("updated_at DESC") } scope :in_collection, lambda {|collection| where('challenge_signups.collection_id = ?', collection.id)} scope :no_potential_offers, -> { where("id NOT IN (SELECT offer_signup_id FROM potential_matches)") } scope :no_potential_requests, -> { where("id NOT IN (select request_signup_id FROM potential_matches)") } # Scopes used to include extra data when loading. scope :with_request_tags, -> { includes( requests: [tag_set: :tags, optional_tag_set: :tags] ) } scope :with_offer_tags, -> { includes( offers: [tag_set: :tags, optional_tag_set: :tags] ) } ### VALIDATION validates_presence_of :pseud, :collection # only one signup per challenge! validates_uniqueness_of :pseud_id, scope: [:collection_id], message: ts("^You seem to already have signed up for this challenge.") # we validate number of prompts/requests/offers at the challenge validate :number_of_prompts def number_of_prompts if (challenge = collection.challenge) errors_to_add = [] %w(offers requests).each do |prompt_type| allowed = self.send("#{prompt_type}_num_allowed") required = self.send("#{prompt_type}_num_required") count = eval("@#{prompt_type}") ? eval("@#{prompt_type}.size") : eval("#{prompt_type}.size") unless count.between?(required, allowed) if allowed == 0 errors_to_add << ts("You cannot submit any #{prompt_type.pluralize} for this challenge.") elsif required == allowed errors_to_add << ts("You must submit exactly %{required} #{required > 1 ? prompt_type.pluralize : prompt_type} for this challenge. You currently have %{count}.", required: required, count: count) else errors_to_add << ts("You must submit between %{required} and %{allowed} #{prompt_type.pluralize} to sign up for this challenge. You currently have %{count}.", required: required, allowed: allowed, count: count) end end end unless errors_to_add.empty? # yuuuuuck :( but so much less ugly than define-method'ing these all self.errors.add(:base, errors_to_add.join("