@@ -46,6 +46,7 @@ for a description of different tree storage algorithms.
46
46
- [ Polymorphic hierarchies with STI] ( #polymorphic-hierarchies-with-sti )
47
47
- [ Deterministic ordering] ( #deterministic-ordering )
48
48
- [ Concurrency] ( #concurrency )
49
+ - [ Multi-Database Support] ( #multi-database-support )
49
50
- [ FAQ] ( #faq )
50
51
- [ Testing] ( #testing )
51
52
- [ Change log] ( #change-log )
@@ -61,11 +62,11 @@ Note that closure_tree only supports ActiveRecord 7.2 and later, and has test co
61
62
3 . Add ` has_closure_tree ` (or ` acts_as_tree ` , which is an alias of the same method) to your hierarchical model:
62
63
63
64
``` ruby
64
- class Tag < ActiveRecord :: Base
65
+ class Tag < ApplicationRecord
65
66
has_closure_tree
66
67
end
67
68
68
- class AnotherTag < ActiveRecord :: Base
69
+ class AnotherTag < ApplicationRecord
69
70
acts_as_tree
70
71
end
71
72
```
@@ -82,7 +83,7 @@ Note that closure_tree only supports ActiveRecord 7.2 and later, and has test co
82
83
You may want to also [add a column for deterministic ordering of children](#deterministic-ordering), but that' s optional.
83
84
84
85
` ` ` ruby
85
- class AddParentIdToTag < ActiveRecord::Migration
86
+ class AddParentIdToTag < ActiveRecord::Migration[7.2]
86
87
def change
87
88
add_column :tags, :parent_id, :integer
88
89
end
@@ -384,7 +385,7 @@ Polymorphic models using single table inheritance (STI) are supported:
384
385
2. Subclass the model class. You only need to add ```has_closure_tree``` to your base class:
385
386
386
387
```ruby
387
- class Tag < ActiveRecord::Base
388
+ class Tag < ApplicationRecord
388
389
has_closure_tree
389
390
end
390
391
class WhenTag < Tag ; end
@@ -411,7 +412,7 @@ By default, children will be ordered by your database engine, which may not be w
411
412
If you want to order children alphabetically, and your model has a ```name``` column, you' d do this:
412
413
413
414
` ` ` ruby
414
- class Tag < ActiveRecord::Base
415
+ class Tag < ApplicationRecord
415
416
has_closure_tree order: 'name'
416
417
end
417
418
` ` `
@@ -425,7 +426,7 @@ t.integer :sort_order
425
426
and in your model:
426
427
427
428
` ` ` ruby
428
- class OrderedTag < ActiveRecord::Base
429
+ class OrderedTag < ApplicationRecord
429
430
has_closure_tree order: 'sort_order', numeric_order: true
430
431
end
431
432
` ` `
@@ -525,14 +526,106 @@ If you are already managing concurrency elsewhere in your application, and want
525
526
of with_advisory_lock, pass ` ` ` with_advisory_lock: false` ` ` in the options hash:
526
527
527
528
` ` ` ruby
528
- class Tag
529
+ class Tag < ApplicationRecord
529
530
has_closure_tree with_advisory_lock: false
530
531
end
531
532
` ` `
532
533
533
534
Note that you * will eventually have data corruption* if you disable advisory locks, write to your
534
535
database with multiple threads, and don' t provide an alternative mutex.
535
536
537
+ ### Customizing Advisory Lock Names
538
+
539
+ By default, closure_tree generates advisory lock names based on the model class name. You can customize
540
+ this behavior in several ways:
541
+
542
+ ```ruby
543
+ # Static string
544
+ class Tag < ApplicationRecord
545
+ has_closure_tree advisory_lock_name: ' custom_tag_lock'
546
+ end
547
+
548
+ # Dynamic via Proc
549
+ class Tag < ApplicationRecord
550
+ has_closure_tree advisory_lock_name: ->(model_class) { "#{Rails.env}_#{model_class.name.underscore}" }
551
+ end
552
+
553
+ # Delegate to model method
554
+ class Tag < ApplicationRecord
555
+ has_closure_tree advisory_lock_name: :custom_lock_name
556
+
557
+ def self.custom_lock_name
558
+ "tag_lock_#{current_tenant_id}"
559
+ end
560
+ end
561
+ ```
562
+
563
+ This is particularly useful when:
564
+ * You need environment-specific lock names
565
+ * You' re using multi- tenancy and need tenant- specific locks
566
+ * You want to avoid lock name collisions between similar model names
567
+
568
+ # # Multi-Database Support
569
+
570
+ Closure Tree fully supports running with multiple databases simultaneously, including mixing different database engines (PostgreSQL , MySQL , SQLite ) in the same application. This is particularly useful for:
571
+
572
+ * Applications with read replicas
573
+ * Sharding strategies
574
+ * Testing with different database engines
575
+ * Gradual database migrations
576
+
577
+ # ## Database-Specific Behaviors
578
+
579
+ # ### PostgreSQL
580
+ * Full support for advisory locks via ` with_advisory_lock`
581
+ * Excellent concurrency support with row- level locking
582
+ * Best overall performance for tree operations
583
+
584
+ # ### MySQL
585
+ * Advisory locks supported via ` with_advisory_lock`
586
+ * Note: MySQL ' s row-level locking may incorrectly report deadlocks in some cases
587
+ * Requires MySQL 5.7.12+ to avoid hierarchy maintenance errors
588
+
589
+ #### SQLite
590
+ * **No advisory lock support** - always returns false from `with_advisory_lock`
591
+ * Falls back to file-based locking for tests
592
+ * Suitable for development and testing, but not recommended for production with concurrent writes
593
+
594
+ ### Configuration
595
+
596
+ When using multiple databases, closure_tree automatically detects the correct adapter for each connection:
597
+
598
+ ```ruby
599
+ class Tag < ApplicationRecord
600
+ connects_to database: { writing: :primary, reading: :replica }
601
+ has_closure_tree
602
+ end
603
+
604
+ class Category < ApplicationRecord
605
+ connects_to database: { writing: :sqlite_db }
606
+ has_closure_tree
607
+ end
608
+ ```
609
+
610
+ Each model will use the appropriate database-specific SQL syntax and features based on its connection adapter.
611
+
612
+ ### Testing with Multiple Databases
613
+
614
+ You can run the test suite against different databases:
615
+
616
+ ```bash
617
+ # Run with PostgreSQL
618
+ DATABASE_URL=postgres://localhost/closure_tree_test rake test
619
+
620
+ # Run with MySQL
621
+ DATABASE_URL=mysql2://localhost/closure_tree_test rake test
622
+
623
+ # Run with SQLite (default)
624
+ rake test
625
+ ```
626
+
627
+ For simultaneous multi-database testing, the test suite automatically sets up connections to all three database types when available.
628
+
536
629
## I18n
537
630
538
631
You can customize error messages using [I18n](http://guides.rubyonrails.org/i18n.html):
0 commit comments