Skip to content

Commit d70be7d

Browse files
kbrockdjberg96claude
committed
Add trilogy adapter support and make mysql2 optional for development
mysql2 requires native MySQL libraries which not all developers have installed. Gate mysql2 behind BUNDLE_INSTALL_MYSQL=1 in the main Gemfile. Appraisal gemfiles keep mysql2 hardcoded for CI. Add trilogy adapter support throughout: - Add "trilogy" to all mysql adapter name checks in format modules - Add trilogy to database.yml configs (example and CI) - Add trilogy test step to CI (runs on the mysql matrix entry) - Add trilogy to gemfile_72 for CI - Normalize DB=trilogy in test environment Co-Authored-By: Daniel Berger <78529+djberg96@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 42b36c2 commit d70be7d

11 files changed

Lines changed: 32 additions & 10 deletions

File tree

.github/workflows/run_test_suite.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,9 @@ jobs:
147147
ANCESTRY_COLUMN_TYPE: binary
148148
run: |
149149
bundle exec rake
150+
- name: run trilogy tests
151+
if: matrix.mysql
152+
env:
153+
DB: trilogy
154+
run: |
155+
bundle exec rake

Appraisals

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
appraise "gemfile-#{ar_version.split('.').first(2).join}" do
88
gem 'activerecord', "~> #{ar_version}"
99
# so we are targeting the ruby version indirectly through active record
10+
# trilogy adapter is built into Rails 7.1+; older versions need the gem
11+
if ar_version < "7.1"
12+
gem "activerecord-trilogy-adapter"
13+
end
1014
if ar_version < "7.0"
1115
gem "sqlite3", "~> 1.6.9"
1216
elsif ar_version < "8.0"

Gemfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ source 'https://rubygems.org'
55
gemspec
66

77
gem "activerecord", "~> 7.2"
8-
gem "mysql2"
8+
gem "mysql2" if ENV["BUNDLE_INSTALL_MYSQL"] == "1" || File.basename($PROGRAM_NAME) == "appraisal"
9+
gem "trilogy"
910
gem "pg"
1011
gem "sqlite3", "~> 1.6.9"

gemfiles/gemfile_72.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ source "https://rubygems.org"
55

66
gem "activerecord", "~> 7.2.1"
77
gem "mysql2"
8+
gem "trilogy"
89
gem "pg"
910
gem "sqlite3", "< 2.0"
1011

lib/ancestry/class_methods.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ def self._rebuild_counter_cache!(klass, column, counter_col, verbose: false)
275275
).count
276276
end
277277

278-
if %w(mysql mysql2).include?(klass.connection.adapter_name.downcase)
278+
if %w(mysql mysql2 trilogy).include?(klass.connection.adapter_name.downcase)
279279
klass.connection.execute %{
280280
UPDATE #{tbl} AS dest
281281
LEFT JOIN (

lib/ancestry/materialized_path.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def self.child_ancestry_sql(table_name, ancestry_column, primary_key, adapter, i
7979
def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter)
8080
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
8181
pk = table_name ? "#{table_name}.#{primary_key}" : primary_key.to_s
82-
if %w(mysql mysql2).include?(adapter)
82+
if %w(mysql mysql2 trilogy).include?(adapter)
8383
"CASE WHEN #{col} IS NULL THEN #{pk} ELSE CAST(SUBSTRING_INDEX(#{col}, '/', 1) AS UNSIGNED) END"
8484
elsif %w(pg postgresql postgis).include?(adapter)
8585
"CASE WHEN #{col} IS NULL THEN #{pk} ELSE CAST(SUBSTR(#{col}, 1, STRPOS(#{col}||'/', '/')-1) AS INTEGER) END"
@@ -92,7 +92,7 @@ def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter
9292
# MP1: ancestry is NULL (root) or "1/2/3" (parent_id=3)
9393
def self.construct_parent_id_sql(table_name, ancestry_column, adapter)
9494
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
95-
if %w(mysql mysql2).include?(adapter)
95+
if %w(mysql mysql2 trilogy).include?(adapter)
9696
"CASE WHEN #{col} IS NULL THEN NULL ELSE CAST(SUBSTRING_INDEX(#{col}, '/', -1) AS UNSIGNED) END"
9797
else
9898
"CASE WHEN #{col} IS NULL THEN NULL ELSE CAST(SUBSTR(#{col}, LENGTH(RTRIM(#{col}, REPLACE(#{col}, '/', ''))) + 1) AS INTEGER) END"
@@ -106,7 +106,7 @@ def self.construct_depth_sql(table_name, ancestry_column)
106106

107107
# mp1 roots are NULL — need NULLS FIRST or COALESCE to sort roots before children
108108
def self.ordered_by_ancestry(arel_column, adapter)
109-
if %w(mysql mysql2 sqlite sqlite3).include?(adapter)
109+
if %w(mysql mysql2 trilogy sqlite sqlite3).include?(adapter)
110110
Arel::Nodes::Ascending.new(arel_column)
111111
elsif ActiveRecord::VERSION::STRING >= "6.1"
112112
Arel::Nodes::Ascending.new(arel_column).nulls_first

lib/ancestry/materialized_path2.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def self.indirects_condition(attr, child_ancestry)
5757
def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter)
5858
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
5959
pk = table_name ? "#{table_name}.#{primary_key}" : primary_key.to_s
60-
if %w(mysql mysql2).include?(adapter)
60+
if %w(mysql mysql2 trilogy).include?(adapter)
6161
"CASE WHEN #{col} = '/' THEN #{pk} ELSE CAST(SUBSTRING_INDEX(SUBSTRING(#{col}, 2), '/', 1) AS UNSIGNED) END"
6262
elsif %w(pg postgresql postgis).include?(adapter)
6363
"CASE WHEN #{col} = '/' THEN #{pk} ELSE CAST(SUBSTR(#{col}, 2, STRPOS(SUBSTR(#{col},2), '/')-1) AS INTEGER) END"
@@ -70,7 +70,7 @@ def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter
7070
# MP2: ancestry is "/" (root) or "/1/2/3/" (parent_id=3)
7171
def self.construct_parent_id_sql(table_name, ancestry_column, adapter)
7272
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
73-
if %w(mysql mysql2).include?(adapter)
73+
if %w(mysql mysql2 trilogy).include?(adapter)
7474
"CASE WHEN #{col} = '/' THEN NULL ELSE CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(#{col}, '/', -2), '/', 1) AS UNSIGNED) END"
7575
else
7676
trimmed = "RTRIM(#{col},'/')"

lib/ancestry/materialized_path3.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def self.generate(ancestor_ids)
3636
def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter)
3737
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
3838
pk = table_name ? "#{table_name}.#{primary_key}" : primary_key.to_s
39-
if %w(mysql mysql2).include?(adapter)
39+
if %w(mysql mysql2 trilogy).include?(adapter)
4040
"CASE WHEN #{col} = '' THEN #{pk} ELSE CAST(SUBSTRING_INDEX(#{col}, '/', 1) AS UNSIGNED) END"
4141
elsif %w(pg postgresql postgis).include?(adapter)
4242
"CASE WHEN #{col} = '' THEN #{pk} ELSE CAST(SUBSTR(#{col}, 1, STRPOS(#{col}, '/')-1) AS INTEGER) END"
@@ -49,7 +49,7 @@ def self.construct_root_id_sql(table_name, ancestry_column, primary_key, adapter
4949
# MP3: ancestry is "" (root) or "1/2/3/" (parent_id=3)
5050
def self.construct_parent_id_sql(table_name, ancestry_column, adapter)
5151
col = table_name ? "#{table_name}.#{ancestry_column}" : ancestry_column.to_s
52-
if %w(mysql mysql2).include?(adapter)
52+
if %w(mysql mysql2 trilogy).include?(adapter)
5353
"CASE WHEN #{col} = '' THEN NULL ELSE CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(#{col}, '/', -2), '/', 1) AS UNSIGNED) END"
5454
else
5555
trimmed = "RTRIM(#{col},'/')"

test/database.ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ mysql2: &mysql
2020
encoding: utf8
2121
mysql:
2222
<<: *mysql
23+
trilogy:
24+
<<: *mysql
25+
adapter: trilogy

test/database.example.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ mysql2:
1919
database: ancestry_test
2020
username: ancestry
2121
password: ancestry
22+
trilogy:
23+
adapter: trilogy
24+
host: localhost
25+
database: ancestry_test
26+
username: ancestry
27+
password: ancestry

0 commit comments

Comments
 (0)