Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby 3.4.4
ruby 3.4.7
15 changes: 13 additions & 2 deletions lib/closure_tree/support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,19 @@ def hierarchy_table_name
# We need to use the table_name, not something like ct_class.to_s.demodulize + "_hierarchies",
# because they may have overridden the table name, which is what we want to be consistent with
# in order for the schema to make sense.
tablename = options[:hierarchy_table_name] ||
"#{remove_prefix_and_suffix(table_name, model_class).singularize}_hierarchies"
if options[:hierarchy_table_name]
tablename = options[:hierarchy_table_name]
else
base_table = remove_prefix_and_suffix(table_name, model_class)

# Handle PostgreSQL schema-qualified table names (e.g., "my_schema.table_name")
schema, _, table = base_table.rpartition('.')
if schema.present?
tablename = "#{schema}.#{table.singularize}_hierarchies"
else
tablename = "#{table.singularize}_hierarchies"
end
end

[model_class.table_name_prefix, tablename, model_class.table_name_suffix].join
end
Expand Down
67 changes: 67 additions & 0 deletions test/closure_tree/schema_type_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require 'test_helper'

describe SchemaType do
before do
skip 'PostgreSQL only' unless postgresql?
end

def assert_lineage(parent, child)
assert_equal parent, child.parent
assert_equal [child, parent], child.self_and_ancestors

# make sure reloading doesn't affect the self_and_ancestors:
child.reload
assert_equal [child, parent], child.self_and_ancestors
end

it 'properly handles schema-qualified table names' do
assert_equal 'test_schema.schema_types', SchemaType.table_name
assert_equal 'test_schema.schema_type_hierarchies', SchemaTypeHierarchy.table_name
end

it 'finds self and parents when children << is used' do
parent = SchemaType.new(name: 'Electronics')
child = SchemaType.new(name: 'Phones')
parent.children << child
parent.save
assert_lineage(parent, child)
end

it 'finds self and parents properly if the constructor is used' do
parent = SchemaType.create(name: 'Electronics')
child = SchemaType.create(name: 'Phones', parent: parent)
assert_lineage(parent, child)
end

it 'creates hierarchy records in the schema-qualified table' do
parent = SchemaType.create!(name: 'Electronics')
child = SchemaType.create!(name: 'Phones', parent: parent)

hierarchy = SchemaTypeHierarchy.where(ancestor_id: parent.id, descendant_id: child.id).first
refute_nil hierarchy
assert_equal 1, hierarchy.generations
end

it 'fixes self_and_ancestors properly on reparenting' do
a = SchemaType.create! name: 'Electronics'
b = SchemaType.create! name: 'Phones'
assert_equal([b], b.self_and_ancestors.to_a)
a.children << b
assert_equal([b, a], b.self_and_ancestors.to_a)
end

it 'supports tree operations with schema-qualified tables' do
root = SchemaType.create!(name: 'Electronics')
child1 = SchemaType.create!(name: 'Computers', parent: root)
child2 = SchemaType.create!(name: 'Phones', parent: root)
grandchild = SchemaType.create!(name: 'Laptops', parent: child1)

assert_equal 2, root.children.count
assert_equal 1, child1.children.count
assert_equal 0, child2.children.count
assert_equal [grandchild, child1, root], grandchild.self_and_ancestors
assert_equal [root, child1, child2, grandchild], root.self_and_descendants.order(:name)
end
end
10 changes: 10 additions & 0 deletions test/dummy/app/models/schema_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class SchemaType < ApplicationRecord
self.table_name = 'test_schema.schema_types'
has_closure_tree order: :name

def to_s
name
end
end
14 changes: 14 additions & 0 deletions test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@
t.integer 'generations', null: false
end

# PostgreSQL schema-qualified tables
execute "CREATE SCHEMA IF NOT EXISTS test_schema"

create_table 'test_schema.schema_types', force: true do |t|
t.string 'name'
t.references 'parent'
end

create_table 'test_schema.schema_type_hierarchies', id: false, force: true do |t|
t.references 'ancestor', null: false
t.references 'descendant', null: false
t.integer 'generations', null: false
end
Comment on lines +123 to +127
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema_type_hierarchies table is manually created, but the PR's purpose is to auto-generate hierarchy tables with schema prefixes. If the library should automatically create this table, consider removing this manual definition to ensure the feature works as intended and to avoid duplication between manual and auto-generated tables.

Suggested change
create_table 'test_schema.schema_type_hierarchies', id: false, force: true do |t|
t.references 'ancestor', null: false
t.references 'descendant', null: false
t.integer 'generations', null: false
end

Copilot uses AI. Check for mistakes.

create_table 'metal' do |t|
t.references 'parent'
t.string 'metal_type'
Expand Down