Skip to content

Commit

Permalink
Replace ContainerLabelTagMapping.controls_tag? with Tag.controlled_by…
Browse files Browse the repository at this point in the history
…_mapping scope

More efficient, and can be used in graph refresh.
  • Loading branch information
cben committed Nov 12, 2017
1 parent 543bf24 commit c4e76ed
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
14 changes: 7 additions & 7 deletions app/models/container_label_tag_mapping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
class ContainerLabelTagMapping < ApplicationRecord
belongs_to :tag

scope :any_value, -> { where(:label_value => nil) }
scope :specific_value, -> { where.not(:label_value => nil) }

require_nested :Mapper

# Return ContainerLabelTagMapping::Mapper instance that holds all current mappings,
Expand All @@ -31,24 +34,21 @@ def self.mapper

# Assigning/unassigning should be possible without Mapper instance, perhaps in another process.

# Checks whether a Tag record is under mapping control.
# TODO: expensive.
# Checks whether a Tag record is under mapping control. TODO: Remove? Only used by tests.
def self.controls_tag?(tag)
return false unless tag.classification.try(:read_only) # never touch user-assignable tags.
tag_ids = [tag.id, tag.category.tag_id].uniq
where(:tag_id => tag_ids).any?
Tag.controlled_by_mapping.where(:id => tag.id).exists?
end

# Assign/unassign mapping-controlled tags, preserving user-assigned tags.
# All tag references must have been resolved first by Mapper#find_or_create_tags.
def self.retag_entity(entity, tag_references)
mapped_tags = Mapper.references_to_tags(tag_references)
existing_tags = entity.tags
existing_tags = entity.tags.controlled_by_mapping
Tagging.transaction do
(mapped_tags - existing_tags).each do |tag|
Tagging.create!(:taggable => entity, :tag => tag)
end
(existing_tags - mapped_tags).select { |tag| controls_tag?(tag) }.tap do |tags|
(existing_tags - mapped_tags).tap do |tags|
Tagging.where(:taggable => entity, :tag => tags.collect(&:id)).destroy_all
end
end
Expand Down
25 changes: 24 additions & 1 deletion app/models/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@ class Tag < ApplicationRecord
virtual_has_one :category, :class_name => "Classification"
virtual_has_one :categorization, :class_name => "Hash"

has_many :container_label_tag_mappings
has_many :container_label_tag_mappings # see also controlled_by_mapping scope

before_destroy :remove_from_managed_filters

# Note those scopes exclude Tags that don't have a Classification.
scope :visible, -> { joins(:classification).merge(Classification.visible) }
scope :read_only, -> { joins(:classification).merge(Classification.read_only) }
scope :writable, -> { joins(:classification).merge(Classification.writable) }

def self.list(object, options = {})
ns = get_namespace(options)
if ns[0..7] == "/virtual"
Expand Down Expand Up @@ -152,6 +157,24 @@ def categorization
end
end

# @return [ActiveRecord::Relation] Scope for tags controlled by ContainerLabelTagMapping.
# May include not only "entry" tags but also some parent "category" tags.
def self.controlled_by_mapping
# TODO: complex query, can we simply select by prefixes e.g. '/managed/kubernetes:%'?
# User can create categories with such prefix, but they won't be read_only.

# Entry tags from specific value->tag mappings.

mapped_specific_tags = read_only.where(:id => ContainerLabelTagMapping.specific_value.pluck(:tag_id))

# Entry tags for name->category mappings.
mapped_categories = Classification.where(:tag => ContainerLabelTagMapping.any_value.pluck(:tag_id))
mapped_entries = Classification.where(:parent => mapped_categories)
mapped_child_tags = read_only.where(:id => mapped_entries.select(:tag_id))

mapped_specific_tags.or(mapped_child_tags)
end

private

def remove_from_managed_filters
Expand Down

0 comments on commit c4e76ed

Please sign in to comment.