Rails6でブログのタグのacts-as-taggable-onがエラーになったので別のタグgem(gutentag tags)を使う

# Rails6にアップデートした時の問題
Rails6になってから、今まで使っていた `acts-as-taggable-on` がエラーになるようになった。

## 原因は
まあ悪い意味での原因というわけではなく

https://github.com/rails/rails/pull/35933

この部分で `set_attribute_was` が消されたため発生したもの

また、これの対応のブランチもあるのだが、初歩的なバグを含むため、ブランチ切り替えの対応してもあまり意味がない。

とはいえ、ライブラリへのPRも全然反映されないし、Gemfileが汚れるのも嫌なので別のやつを実験的に使ってみた。

https://github.com/pat/gutentag

というやつで、スターは315の、RubyGemダウンロード数は54,474というところなので、act-as-taggable-onのわずか6%程度のスター数となり、多少心もとない。

# 開発当初のブログがあるので、読んでみた
個人的には、act-as-taggable-onしか使ったことなかったので、とりあえず調べていたら、どうも2013年頃に開発されたようだ。

https://freelancing-gods.com/2013/07/11/gutentag-simple-rails-tagging.html

https://github.com/pat/gutentag/blob/master/lib/gutentag/persistence.rb

永続化周りをforwardableを使って委譲して実装してある

## 使ってみた

### 環境
- Rails 6.0.0
- ruby 2.6.3

### Gemfile
記載したら、`bundle install` する

```rb
gem 'gutentag', '~> 2.5'
```

### インストール設定
ここは、よくあるお決まりのやつをただ打っていくだけ
migrateすることで、gutentagにより作られたマイグレーションファイルが実行され、使えるようになる
使う側のモデルにカラムを追加することなく使えるところが楽で良い

```rb
bundle exec rake gutentag:install:migrations
rails db:migrate
```

### Schema例

```rb
create_table "blogs", force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end
```

### Blog.rb
今回、ブログにタグ機能をもたせたいので、
ここに`Gutentag::ActiveRecord.call self`を置く

```rb
class Blog < ApplicationRecord
  Gutentag::ActiveRecord.call self
  include TagExtensions
end

FactoryBot.define do
  factory :blog do
    tags_as_string { "hoge,fuga,piyo" }
  end
end
```

### TagExtensions.rb
これは、いわゆる`attr_accessor`の役割を`include`して使うために別途作っただけ
メソッド毎モデルに直で書いても良いが、他のモデルでも使いたかったのでこのようにサクッと作っただけ。（若干気に入らないので後で修正する気がする）

```rb
module TagExtensions
  def tags_as_string
    tag_names.join(', ')
  end

  def tags_as_string=(string)
    self.tag_names = string.split(/,\s*/)
  end
end
```

### 作成時

#### View
一般的なinputタグに先程TagExtensionsで作ったgetterメソッドを配置しておく

```rb
  .form
    = f.label :tags_as_string
    = f.text_field :tags_as_string, value: @blog.tags_as_string
```

#### Controller#create
Scaffoldして、必要なところだけ抜粋しておく
フォームから送られて新規作成するところの処理と、Strong Parametersの設置

```rb
class BlogsController < ApplicationController
  def create
    @blog = Blog.new(blog_params)

    respond_to do |format|
      if @blog.save
        format.html { redirect_to blog_url(@blog), notice: 'Blog was successfully created.' }
        format.json { render :show, status: :created, location: @blog }
      else
        format.html { render :new }
        format.json { render json: @blog.errors, status: :unprocessable_entity }
      end
    end
  end

  def blog_params
    params.require(:blog).permit(:tags_as_string)
  end
end
```

### 一覧表示時

#### View
tag_namesなどで引っ張ってこれる

```rb
- @blogs.each do |blog|
  - blog.tag_names.each do |tag|
    = tag
```

#### Controller#index
例えば`includes`などをしないとN+1で死ぬので注意

```rb
class BlogsController < ApplicationController
  def index
    @blogs = Blog.includes(:tags)
  end
end
```

### 挙動確認
実際にテストなどで挙動見てみるとこんなように使える

```rb
blog = Blog.create! # ブログ用レコード作る

blog.tag_names << 'hogehoge'
blog.tag_names #=> ['hoge']
blog.tag_names << 'fuga' << 'piyo'
blog.tag_names #=> ['hoge', 'fuga', 'piyo']
blog.tag_names -= ['fuga']
blog.tag_names #=> ['hoge', 'piyo']

blog.save # 保存する
```

## 感想
gutentag自体は、rails6にすぐ対応したcommitがあるし、issueのclose率も100%なのでけっこういいかなと思った。懸念点はpatという創造者しかほぼ保守していないところ。
acts-as-taggable-onは、使ってる人が多いので、また修正版が上がりそうなので、そのあたり様子見