Tidying up and documenting Tag model (#15949)

* Tidying up and documenting Tag model

Prior to this commit we had a custom `where(alias_for: [nil, ""])`
call.  That call highlighted that we lacked a term for a Tag that was
not an alias.  As part of this commit, I named that a "concrete" tag.

Further, I added scopes to assist in helping "name" those concepts.

This commit also adds a data migration and utilization of
StringAttributeCleaner to hopefully get away from `alias_for == ""`
situations.

As of writing this commit <2022-01-04 Tue 17:22 UTC>, in DEV.to we had 5
tags with `alias_for == ""`:

- actionshackathon21
- regex
- atlashackathon
- hotwire
- foremfest

In https://dev.to/admin/blazer I ran the following:

```sql
SELECT name FROM tags WHERE alias_for = ''
```

* Renaming concrete to direct
This commit is contained in:
Jeremy Friesen 2022-01-07 09:14:31 -05:00 committed by GitHub
parent 775bdb7810
commit 5b10addbb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 62 additions and 1 deletions

View file

@ -18,7 +18,7 @@ class TagsController < ApplicationController
def index
skip_authorization
@tags_index = true
@tags = Tag.where(alias_for: [nil, ""]).includes(:sponsorship).order(hotness_score: :desc).limit(100)
@tags = Tag.direct.includes(:sponsorship).order(hotness_score: :desc).limit(100)
end
def edit

View file

@ -1,3 +1,18 @@
# We allow content creators to "tag" their content. This model helps
# define what we mean when we "tag" something.
#
# These tags can be arbitrary or supported (e.g. `tag.supported ==
# true`). We allow for sponsorship of tags (see `belongs_to
# :sponsorship`). Some tags have moderators. These tags can create a
# defacto "sub-community" within a Forem.
#
# Sometimes we need to consolidate tags (e.g. rubyonrails and rails).
# In this case, we mark one of those tags as an alias for the other
# (via `alias_for`). The direct tags is the "preferred" tag
# (e.g. not the alias).
#
# @note models with `acts_as_taggable_on` declarations (e.g., Article and Listing)
# @see https://developers.forem.com/technical-overview/architecture/#tags for more discussion
class Tag < ActsAsTaggableOn::Tag
self.ignored_columns = %w[mod_chat_channel_id].freeze
@ -10,6 +25,13 @@ class Tag < ActsAsTaggableOn::Tag
include Purgeable
include PgSearch::Model
# @note Even though we have a data migration script (see further
# comments below), as of <2022-01-04 Tue> we had 5 tags where
# the `alias_for == ""` (ideally they should be nil). This
# change will help us achieve that goal.
#
# @see https://github.com/forem/forem/blob/72bb284ba73c3df8aa11525427b1dfa1ceba39df/lib/data_update_scripts/20211115154021_nullify_invalid_tag_fields.rb
include StringAttributeCleaner.for(:alias_for)
ALLOWED_CATEGORIES = %w[uncategorized language library tool site_mechanic location subcommunity].freeze
HEX_COLOR_REGEXP = /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
@ -37,6 +59,22 @@ class Tag < ActsAsTaggableOn::Tag
after_commit :bust_cache
# @note Even though we have a data migration script (see further
# comments below), as of <2022-01-04 Tue> we had 5 tags where
# the alias_for was "" (ideally they should be nil). Once we
# have the StringAttributeCleaner (see above) in place, and
# our next data migration runs, we can remove the [nil, ""]
# and favor `where(alias_for: nil)`.
#
# @see https://github.com/forem/forem/blob/72bb284ba73c3df8aa11525427b1dfa1ceba39df/lib/data_update_scripts/20211115154021_nullify_invalid_tag_fields.rb
scope :aliased, -> { where.not(alias_for: [nil, ""]) }
# @note We had named the concept of a tag that was an alias;
# however, prior to adding this scope, we didn't have a name
# for a non-aliased tag (aside from "not an alias"). With
# this scope we have a name.
scope :direct, -> { where(alias_for: [nil, ""]) }
pg_search_scope :search_by_name,
against: :name,
using: { tsearch: { prefix: true } }

View file

@ -0,0 +1,7 @@
module DataUpdateScripts
class NullifyEmptyStringForAliasFor
def run
Tag.where(alias_for: "").update_all(alias_for: nil)
end
end
end

View file

@ -0,0 +1,16 @@
require "rails_helper"
require Rails.root.join(
"lib/data_update_scripts/20220104165348_nullify_empty_string_for_alias_for.rb",
)
describe DataUpdateScripts::NullifyEmptyStringForAliasFor do
it "converts empty string `alias_for` to nil value" do
Tag.upsert_all([
attributes_for(:tag).merge(id: 1, alias_for: "", created_at: Time.current,
updated_at: Time.current),
])
tag = Tag.first
expect { described_class.new.run }.to change { tag.reload.alias_for }.from("").to(nil)
end
end