diff --git a/app/models/post.rb b/app/models/post.rb index de81fb53e..882f1f399 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -200,13 +200,21 @@ def reaction_list private - # Updates the tags association from the tags_cache. + ## + # Before-validation callback. Update the tags association from the tags_cache. def update_tag_associations tags_cache.each do |tag_name| - tag = Tag.find_or_create_by name: tag_name, tag_set: category.tag_set + tag, name_used = Tag.find_or_create_synonymized name: tag_name, tag_set: category.tag_set unless tags.include? tag tags << tag end + + # If the tags_cache doesn't include name_used then tag_name was a synonym - remove the synonym from tags_cache + # and add the primary for it instead. + unless tags_cache.include? name_used + tags_cache.delete tag_name + tags_cache << name_used + end end tags.each do |tag| unless tags_cache.include? tag.name @@ -215,10 +223,12 @@ def update_tag_associations end end + ## + # Helper method for #check_attribution_notice validator. Produces a text-only attribution notice either based on + # values given or the current state of the post for use in post histories. # @param source [String, Nil] where the post originally came from # @param name [String, Nil] the name of the license # @param url [String, Nil] the url of the license - # # @return [String] an attribution notice corresponding to this post def attribution_text(source = nil, name = nil, url = nil) "Source: #{source || att_source}\nLicense name: #{name || att_license_name}\n" \ diff --git a/app/models/tag.rb b/app/models/tag.rb index 3fc1dff71..873a0ea95 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -38,6 +38,36 @@ def self.search(term) .distinct end + ## + # Find or create a tag within a given tag set, considering synonyms. If a synonym is given as +name+ then the primary + # tag for it is returned instead. + # @param name [String] A tag name to find or create. + # @param tag_set [TagSet] The tag set within which to search for or create the tag. + # @return [Array(Tag, String)] The found or created tag, and the final name used. If a synonymized name was given as + # +name+ then this will be the primary tag name. + # + # @example +name+ does not yet exist: a new Tag is created + # Tag.find_or_create_synonymized name: 'new-tag', tag_set: ... + # # => [Tag, 'new-tag'] + # + # @example +name+ already exists: the existing Tag is returned + # Tag.find_or_create_synonymized name: 'existing-tag', tag_set: ... + # # => [Tag, 'existing-tag'] + # + # @example +name+ is a synonym of 'other-tag': the Tag for 'other-tag' is returned + # Tag.find_or_create_synonymized name: 'synonym', tag_set: ... + # # => [Tag, 'other-tag'] + def self.find_or_create_synonymized(name:, tag_set:) + existing = Tag.find_by(name: name, tag_set: tag_set) + if existing.nil? + synonyms = TagSynonym.joins(:tag).where(name: name, tags: { tag_set: tag_set }) + synonymized_name = synonyms.exists? ? synonyms.first.tag.name : name + [Tag.find_or_create_by(name: synonymized_name, tag_set: tag_set), synonymized_name] + else + [existing, name] + end + end + def all_children query = File.read(Rails.root.join('db/scripts/tag_children.sql')) query = query.gsub('$ParentId', id.to_s)