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