require "safe_html" require 'tag_id_validator' class Tag include Mongoid::Document field :tag_id, type: String field :title, type: String field :tag_type, type: String #TODO: list of accepted types? field :description, type: String field :short_description, type: String field :parent_id, type: String field :state, type: String, default: 'live' GOVSPEAK_FIELDS = [] STATES = ['draft', 'live'] index :tag_id index [ [:tag_id, Mongo::ASCENDING], [:tag_type, Mongo::ASCENDING] ], unique: true index :tag_type validates_presence_of :tag_id, :title, :tag_type validates_uniqueness_of :tag_id, scope: :tag_type validates_with TagIdValidator validates_with SafeHtml validates :state, inclusion: { in: STATES } class MissingTags < RuntimeError attr_reader :tag_ids def initialize(tag_ids) super("Missing tags: #{tag_ids.join(", ")}") @tag_ids = tag_ids end end # This doesn't get set automatically: the code that loads tags # should go through them and set this attribute manually attr_accessor :uniquely_named def as_json(options = {}) { id: self.tag_id, title: self.title, type: self.tag_type, description: self.description, short_description: self.short_description } end def has_parent? parent_id.present? end def parent Tag.by_tag_id(parent_id, self.tag_type) if has_parent? end def unique_title self.uniquely_named ? self.title : "#{self.title} [#{self.tag_id}]" end def to_s title end def self.by_tag_id(tag_id, tag_type = nil) by_tag_ids([tag_id], tag_type).first end def self.by_tag_ids(tag_id_list, tag_type = nil) tag_scope = tag_type ? Tag.where(tag_type: tag_type) : Tag tag_scope = tag_scope.any_in(tag_id: tag_id_list) # Sort by id list because MongoID 2.x doesn't preserve order tags_by_id = tag_scope.each_with_object({}) do |tag, hash| hash[tag.tag_id] = tag end tag_id_list.map { |tag_id| tags_by_id[tag_id] }.compact end def self.by_tag_types_and_ids(tag_types_and_ids) list = tag_types_and_ids.map {|hash| hash.slice(:tag_id, :tag_type) } any_of(list) end # Retrieve a list of tags by tag ID. Any missing tags raise an exception. def self.validate_tag_ids(tag_id_list, tag_type = nil) found_tags = by_tag_ids(tag_id_list, tag_type) missing_tag_ids = tag_id_list - found_tags.map(&:tag_id) raise MissingTags.new(missing_tag_ids) if missing_tag_ids.any? end end