diff --git a/app/models/concerns/has_tags.rb b/app/models/concerns/has_tags.rb index 5ea9a8fcd..bf507bfcc 100644 --- a/app/models/concerns/has_tags.rb +++ b/app/models/concerns/has_tags.rb @@ -77,4 +77,45 @@ destroy all tags of an object true end + class_methods do + +=begin + +Lists references to objects of this type with certain tag. +Returns array containing object IDs. + +@param [String] tag name + +@example + +Model.tag_references('Tag') # [1, 4, ...] + +@return [Array] + +=end + + def tag_references(tag) + Tag + .tag_references(tag: tag, object: name) + .map { |elem| elem[1] } + end + +=begin + +Lists objects of this type with certain tag + +@param [String] tag name + +@example + +Model.tag_objects('tag') + +@return [ActiveRecord::Relation] + +=end + + def tag_objects(tag) + where id: tag_references(tag) + end + end end diff --git a/app/models/tag.rb b/app/models/tag.rb index 2014181f3..7f29bb54e 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -152,4 +152,37 @@ returns .order(:id) .pluck('tag_items.name') end + +=begin + +Lists references to objects with certain tag. Optionally filter by type. +Returns array containing object class name and ID. + +@param tag [String] tag to lookup +@param object [String] optional name of the class to search in + +@example + +references = Tag.tag_references( + tag: 'Tag', + object: 'Ticket' +) + +references # [['Ticket', 1], ['Ticket', 4], ...] + +@return [Array>] + +=end + + def self.tag_references(tag:, object: nil) + tag_item = Tag::Item.find_by name: tag + + return [] if tag_item.nil? + + output = Tag.where(tag_item: tag_item).joins(:tag_object) + + output = output.where(tag_objects: { name: object }) if object.present? + + output.pluck(:'tag_objects.name', :o_id) + end end diff --git a/spec/models/concerns/has_tags_examples.rb b/spec/models/concerns/has_tags_examples.rb index 651768feb..bbc2e83ba 100644 --- a/spec/models/concerns/has_tags_examples.rb +++ b/spec/models/concerns/has_tags_examples.rb @@ -65,4 +65,37 @@ RSpec.shared_examples 'HasTags' do subject.tag_list end end + + shared_context 'with subject and another object being tagged', current_user_id: 1 do + before do + subject.tag_add(tag) + Tag.tag_add(object: 'AnotherObject', o_id: 123, item: tag) + end + + let(:tag) { 'tag_name' } + end + + describe '.tag_references' do + include_context 'with subject and another object being tagged' do + it 'returns reference to subject' do + expect(described_class.tag_references(tag)).to match_array [subject.id] + end + + it 'does not return reference to subject when called with other tag' do + expect(described_class.tag_references('other')).to be_blank + end + end + end + + describe '.tag_objects' do + include_context 'with subject and another object being tagged' do + it 'returns subject' do + expect(described_class.tag_objects(tag)).to match_array [subject] + end + + it 'does not return subject when called with other tag' do + expect(described_class.tag_objects('other')).to be_blank + end + end + end end diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index a21e6464b..af040f720 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -156,4 +156,32 @@ RSpec.describe Tag, type: :model do end end end + + describe '.tag_references' do + context 'with objects with a tag', current_user_id: 1 do + before do + [object_1, object_2].each do |elem| + described_class.tag_add object: elem.class.name, o_id: elem.id, item: tag + end + end + + let(:object_1) { create(:ticket) } + let(:object_2) { create(:knowledge_base_answer) } + let(:tag) { 'foo' } + + it 'returns references' do + expect(described_class.tag_references(tag: tag)).to match_array [ + [object_1.class.name, object_1.id], + [object_2.class.name, object_2.id] + ] + end + + it 'returns references for the given object type' do + expect(described_class.tag_references(tag: tag, object: 'Ticket')).to match_array [ + [object_1.class.name, object_1.id] + ] + end + end + + end end