Acts-as-taggable-on を拡張する

タグの管理にmbleigh/acts-as-taggable-on を使っています。 非常に使いやすくて重宝しているのですが、一部のタグのみ他のテーブルと関連づけしたかったので 拡張してみました。

やりたいこと

schema.rb
acts-as-taggable-on を使うと以下のようなテーブルが作成されます。

  create_table "tags", force: :cascade do |t|
    t.string  "name"
    t.integer "taggings_count", default: 0
    t.index ["name"], name: "index_tags_on_name", unique: true
  end

データとしては以下のような感じです。
id=1,2 はカテゴリーですが、id=3 は人物です。 別途、人物を管理するpeopleテーブルを保有しているとすると、 このテーブルとのreferenceを付与するだけで、「通常のタグ一覧」と「人物一覧」が簡単に取り出せます。

id name taggings_count
1 アート 10
2 歴史 20
3 ジェイミー・ヴァーディ 3

referenceを追加すると以下のようなイメージです。

id name taggings_count person_id
1 アート 10 null
2 歴史 20 null
3 ジェイミー・ヴァーディ 3 100

やり方

$ rails g migration AddPersonRefToTags person:references

# migration file
class AddPersonRefToTags < ActiveRecord::Migration[5.0]
  def change
    add_reference :tags, :person, foreign_key: true
  end
end

config/initializers/acts_as_taggable_on.rb

ActsAsTaggableOn::Tag.class_eval do
  belongs_to :person
end

model
point は class_name で ActsAsTaggableOn::Tag を指定すること。

class Person < ApplicationRecord
  has_one :tag, class_name: "ActsAsTaggableOn::Tag"
end

データ抽出
記事にタグ付けされたタグの一覧と、人物タグの一覧を取り出します。(Articleにタグ付けをできるようにするのは、acts_as_taggable_on の基本機能なのでここでは割愛)
 
pointは、acts_as_taggable_on利用時にtags_on というメソッドが提供されるのですが、この引数に condisions が渡せるので以下のようにすれば良いです。
acts_as_taggable_on 親切設計ですね!

    @tags = Article.tags_on(:tags, conditions: { person_id: nil })
    @people = Article.tags_on(:tags, conditions: "person_id is not null")

あとは、has_one / belongs_to を設定しているので、各モデルのオブジェクトから辿ることができます。