0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-01-13

読んで欲しい人

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

環境

  • Rails8
  • Ruby.3.3.6

やること

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

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

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

acts_as_listメソッド

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

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に追加されるかを指定(デフォルト: bottomnilを指定すると、新しいアイテムは作成時にリストに追加されない

  • 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_classselfを代入しているんですが、ここでいう自分のオブジェクトはなんなのか?
まずここがよくわからないので調査します

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

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

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

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

今後調査するもの

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?