Rails: Tree concern の習作

下記の投稿で紹介されていた Tree concern を用いた木構造(ツリー構造)を実装してみました。

リポジトリ:

アプリの作成

習作用のアプリを作成します。

rails new study_tree_concern
cd study_tree_concern

Pin モデルを生成します。
区別しやすいように string 型 name を持たせます。
記事では名前空間を持つ Bucket::Pin ですが今回の習作では Pin にします。
ジェネレータのコマンドに自テーブルを参照するオプションは無いようですので後から追加します。

bin/rails g model Pin name
code . db/migrate/*_create_pins.rb

外部キーを追加します。
t.references :parent, foreign_key: { to_table: :pins }

class CreatePins < ActiveRecord::Migration[7.0]
  def change
    create_table :pins do |t|
      t.string :name
      t.references :parent, foreign_key: { to_table: :pins }

      t.timestamps
    end
  end
end

マイグレートします。

bin/rails db:migrate

Tree concern を作成して、Pin モデルに include します。

code app/models/concerns/tree.rb
module Tree
  extend ActiveSupport::Concern

  included do |base|
    belongs_to :parent, class_name: base.to_s, touch: true, optional: true, inverse_of: :children
    has_many :children, class_name: base.to_s, foreign_key: :parent_id, dependent: :destroy, inverse_of: :parent

    scope :roots, -> { where(parent: nil) }
    scope :children_of, ->(parent) { where(parent: parent) }
  end

  def adopt(children)
    transaction do
      Array(children).each do |child|
        child.update! parent: self
      end
    end
  end
end
code app/models/pin.rb
class Pin < ApplicationRecord
  include Tree
end

動作確認

次のモデルを作って動作確認します。


動作確認用のモデル

参考:Mermaidのコード

classDiagram
a1 --> b11
a1 --> b12
b12 --> C123
a2 --> b21

コンソールで動作確認します。

bin/rails c -s

木構造のモデルを作成します。

a1 = Pin.create! name: "a1"
b11 = Pin.create! name: "b11"
b12 = Pin.create! name: "b12"
c121 = Pin.create! name: "c121"

a1.adopt [b11, b12]
b12.adopt c121

a2 = Pin.create! name: "a2"
a2.adopt Pin.create! name: "b21"

動作確認します。

a1.children   # => [b11, b12]
b11.parent    # => a1
b12.children  # => [c121]
c121.parent   # => b12

Pin.roots             # => [a1, a2]
Pin.children_of a1    # => [b11, b12]
Pin.children_of b11   # => []
Pin.children_of b12   # => [c121]
Pin.children_of c121  # => []

以上です。

この記事が気に入ったらサポートをしてみませんか?