acts_as_list gemの`acts_as_list`メソッドが気になったので追っかけてみる①

## 読んで欲しい人
- acts_as_list gemの仕組みを知りたい人へ
- `acts_as_list`メソッドを追加するとなぜゆえ色々な処理をよしなにやってくれているのか？気になる人
- 過去というか、絶賛気になっている自分へ

## 環境
- Rails8
- Ruby.3.3.6

## やること

acts_as_list gemで提供されているメソッドがどんな仕組みになっているかを探索しようと思っています
特に`acts_as_list`が気になっているので、そのコードを読んでいこうかなと

使い方はまとめたけど、仕組みは分からずじまいだったので

https://qiita.com/MrTT/items/bee34a943760622d75aa

~~ネタもないし~~
長いので、シリーズ的に出してきます

## `acts_as_list`メソッド

早速、`acts_as_list`が定義されている場所へ

https://github.com/brendon/acts_as_list/blob/02fe238ab4cdfe66fb93994526ef3c3ff1f7fc2f/lib/acts_as_list/active_record/acts/list.rb#L12

### optionsについて確認

まぁ長いので最初のコメントアウトを確認します

```acts_as_list/lib/acts_as_list/active_record/acts/list.rb
module ClassMethods
    # Configuration options are:
    #
    # * +column+ - specifies the column name to use for keeping the position integer (default: +position+)
    # * +scope+ - restricts what is to be considered a list. Given a symbol, it'll attach <tt>_id</tt>
    #   (if it hasn't already been added) and use that as the foreign key restriction. It's also possible
    #   to give it an entire string that is interpolated if you need a tighter scope than just a foreign key.
    #   Example: <tt>acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0'</tt>
    # * +top_of_list+ - defines the integer used for the top of the list. Defaults to 1. Use 0 to make the collection
    #   act more like an array in its indexing.
    # * +add_new_at+ - specifies whether objects get added to the :top or :bottom of the list. (default: +bottom+)
    #                   `nil` will result in new items not being added to the list on create.
    # * +sequential_updates+ - specifies whether insert_at should update objects positions during shuffling
    #   one by one to respect position column unique not null constraint.
    #   Defaults to true if position column has unique index, otherwise false.
    #   If constraint is <tt>deferrable initially deferred<tt>, overriding it with false will speed up insert_at.
    # * +touch_on_update+ - configuration to disable the update of the model timestamps when the positions are updated.
```

オプションについての説明すね

- `column`: 位置を保持するために使用するカラム名を指定（デフォルト: `position`）

- `scope`: リストとして考慮する範囲を制限。シンボルを指定すると、`_id`が追加されていない場合は追加され、それを外部キーの制限として使用します。より厳密な範囲が必要な場合は、文字列全体を指定することも可能 例: `acts_as_list scope: 'todo_list_id = #{todo_list_id} AND completed = 0'`

- `top_of_list`: リストの先頭として使用される整数を定義。デフォルトは1。0を使用すると、コレクションが配列のインデックスのように動作

- `add_new_at`: オブジェクトがリストの`top`または`bottom`に追加されるかを指定（デフォルト: `bottom`）`nil`を指定すると、新しいアイテムは作成時にリストに追加されない

- `sequential_updates`: `insert_at`が位置カラムの一意の非NULL制約を尊重するために、オブジェクトの位置を一つずつ更新するかどうかを指定。位置カラムに一意のインデックスがある場合はデフォルトで`true`、そうでない場合は`false`。制約が`deferrable initially deferred`の場合、`false`に設定すると`insert_at`の速度が早くなる

- `touch_on_update`: 位置が更新されたときにモデルのタイムスタンプを更新しないようにする設定

なんで`acts_as_list`メソッドを定義すると、新たらしいレコードを作成した時に最後に良い感じに追加してくれるのだろう？と思っていましたがここでオプションが`bottom`で指定されたんだ〜

とか思いつつ`acts_as_list`を見に行きます

### `acts_as_list`メソッド

```acts_as_list/lib/acts_as_list/active_record/acts/list.rb
def acts_as_list(options = {})
  configuration = { column: "position", scope: "1 = 1", top_of_list: 1, add_new_at: :bottom, touch_on_update: true }
  configuration.update(options) if options.is_a?(Hash)

  caller_class = self

  ActiveRecord::Acts::List::PositionColumnMethodDefiner.call(caller_class, configuration[:column], configuration[:touch_on_update])
  ActiveRecord::Acts::List::ScopeMethodDefiner.call(caller_class, configuration[:scope])
  ActiveRecord::Acts::List::TopOfListMethodDefiner.call(caller_class, configuration[:top_of_list])
  ActiveRecord::Acts::List::AddNewAtMethodDefiner.call(caller_class, configuration[:add_new_at])

  ActiveRecord::Acts::List::AuxMethodDefiner.call(caller_class)
  ActiveRecord::Acts::List::CallbackDefiner.call(caller_class, configuration[:add_new_at])
  ActiveRecord::Acts::List::SequentialUpdatesMethodDefiner.call(caller_class, configuration[:column], configuration[:sequential_updates])

  include ActiveRecord::Acts::List::InstanceMethods
  include ActiveRecord::Acts::List::NoUpdate
end
```

`configuration`にディフォルトの値を設定、その後、ユーザーから受け取った`option`でディフォルトの値を更新します。
何も指定しなかったら、ディフォルト値のまま

次に`caller_class`に`self`を代入しているんですが、ここでいう自分のオブジェクトはなんなのか？
まずここがよくわからないので調査します

#### selfのオブジェクトについて

Railsでは`ClassMethods`モジュール（特定のクラスにクラスメソッドを追加するためのモジュール）なるものがよく使用されるみたいです

https://qiita.com/pekepek/items/4f5ad8c3003672765846

詳しくは触れませんが、`acts_as_list`メソッドを使いたいクラス内で`acts_as_list`を定義してあげると、そのクラスのクラスメソッドになるというスーパー便利なことをやっているらしいです

https://github.com/brendon/acts_as_list/blob/02fe238ab4cdfe66fb93994526ef3c3ff1f7fc2f/lib/acts_as_list/active_record/acts/no_update.rb#L24

ということで、`self`には、`acts_as_list`を定義したオブジェクトが含まれています。


### 今後調査するもの

1つ1つ深掘りしていくとかなり時間がかかりそうなので`scope`だったり、`position`はどんな仕組みで実装されているのかを見て行こうと思います