Skip to content

Commit 845978f

Browse files
committed
Fix conflicting annotations for multiple-database scenario
Given a Rails application with multiple databases, tables in two databases with the same table name, and corresponding models with different names, some files get annotated with the wrong table's schema. The issue is that some of the patterns include `%TABLE_NAME%`, which will be identical between the two tables, even though they have different model names. This fixes the issue by eliminating the use of the table name when the model is not connected to the primary database. Fixes #891.
1 parent 7863949 commit 845978f

File tree

8 files changed

+113
-4
lines changed

8 files changed

+113
-4
lines changed

lib/annotate/annotate_models.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ def annotate(klass, file, header, options = {})
480480
klass.reset_column_information
481481
info = get_schema_info(klass, header, options)
482482
model_name = klass.name.underscore
483-
table_name = klass.table_name
483+
table_name = klass.table_name if klass.connection_specification_name == ActiveRecord::Base.name
484484
model_file_name = File.join(file)
485485
annotated = []
486486

@@ -702,7 +702,7 @@ def remove_annotations(options = {})
702702
klass = get_model_class(file)
703703
if klass < ActiveRecord::Base && !klass.abstract_class?
704704
model_name = klass.name.underscore
705-
table_name = klass.table_name
705+
table_name = klass.table_name if klass.connection_specification_name == ActiveRecord::Base.name
706706
model_file_name = file
707707
deannotated_klass = true if remove_annotation_of_file(model_file_name, options)
708708

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class SecondaryRecord < ActiveRecord::Base
2+
self.abstract_class = true
3+
4+
connects_to database: { reading: :secondary, writing: :secondary }
5+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class SecondaryTask < SecondaryRecord
2+
self.table_name = 'tasks'
3+
end

spec/integration/rails_6.0.2.1/config/multi-database.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ development:
1616
secondary:
1717
<<: *default
1818
database: db/development-secondary.sqlite3
19+
migrations_paths: db/secondary_migrate
1920

2021
# Warning: The database defined as "test" will be erased and
2122
# re-generated from your development database when you run "rake".
@@ -27,6 +28,7 @@ test:
2728
secondary:
2829
<<: *default
2930
database: db/test-secondary.sqlite3
31+
migrations_paths: db/secondary_migrate
3032

3133
production:
3234
primary:
@@ -35,3 +37,4 @@ production:
3537
secondary:
3638
<<: *default
3739
database: db/production-secondary.sqlite3
40+
migrations_paths: db/secondary_migrate
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class CreateTasks < ActiveRecord::Migration[6.0]
2+
def change
3+
create_table :tasks do |t|
4+
t.boolean :completed, null: false
5+
t.string :description
6+
7+
t.timestamps
8+
end
9+
end
10+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# This file is auto-generated from the current state of the database. Instead
2+
# of editing this file, please use the migrations feature of Active Record to
3+
# incrementally modify your database, and then regenerate this schema definition.
4+
#
5+
# This file is the source Rails uses to define your schema when running `rails
6+
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7+
# be faster and is potentially less error prone than running all of your
8+
# migrations from scratch. Old migrations may fail to apply correctly if those
9+
# migrations use external dependencies or application code.
10+
#
11+
# It's strongly recommended that you check this file into your version control system.
12+
13+
ActiveRecord::Schema.define(version: 2020_02_01_204456) do
14+
15+
create_table "tasks", force: :cascade do |t|
16+
t.boolean "completed", null: false
17+
t.string "description"
18+
t.datetime "created_at", precision: 6, null: false
19+
t.datetime "updated_at", precision: 6, null: false
20+
end
21+
22+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2+
3+
one:
4+
description: MyString
5+
completed: false
6+
7+
two:
8+
description: MyString
9+
completed: false

spec/integration/rails_6.0.2.1_spec.rb

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,46 @@
7272
patch: include(patch)
7373
}
7474
end
75+
let(:secondary_task_model) do
76+
patch = <<~PATCH
77+
+# == Schema Information
78+
+#
79+
+# Table name: tasks
80+
+#
81+
+# id :integer not null, primary key
82+
+# completed :boolean not null
83+
+# description :string
84+
+# created_at :datetime not null
85+
+# updated_at :datetime not null
86+
+#
87+
PATCH
88+
89+
path = 'app/models/secondary_task.rb'
90+
{
91+
path: include(path),
92+
patch: include(patch)
93+
}
94+
end
95+
let(:secondary_task_fixture) do
96+
patch = <<~PATCH
97+
+# == Schema Information
98+
+#
99+
+# Table name: tasks
100+
+#
101+
+# id :integer not null, primary key
102+
+# completed :boolean not null
103+
+# description :string
104+
+# created_at :datetime not null
105+
+# updated_at :datetime not null
106+
+#
107+
PATCH
108+
109+
path = 'test/fixtures/secondary_tasks.yml'
110+
{
111+
path: include(path),
112+
patch: include(patch)
113+
}
114+
end
75115

76116
before(:all) do
77117
Bundler.with_clean_env do
@@ -108,6 +148,21 @@
108148
an_object_having_attributes(task_fixture)
109149
)
110150
end
151+
152+
context 'with multi-db environment' do
153+
let(:command) { 'bundle exec annotate --models app/models/secondary_task.rb' }
154+
155+
it 'does not annotate files based on table name when the model is from a secondary database' do
156+
expect(git.diff.any?).to be_falsy
157+
158+
system({ 'MULTI_DB' => 'true' }, command)
159+
160+
expect(git.diff.entries).to contain_exactly(
161+
an_object_having_attributes(secondary_task_model),
162+
an_object_having_attributes(secondary_task_fixture)
163+
)
164+
end
165+
end
111166
end
112167

113168
describe 'annotate --routes' do
@@ -181,7 +236,7 @@
181236
end
182237

183238
context 'with multi-db environment' do
184-
let(:migrate_command) { 'bin/rails db:migrate:primary' }
239+
let(:migrate_command) { 'bin/rails db:migrate' }
185240

186241
it 'hooks database-specific commands and annotates models' do
187242
expect(git.diff.any?).to be_falsy
@@ -192,7 +247,9 @@
192247
expect(git.diff.entries).to contain_exactly(
193248
an_object_having_attributes(task_model),
194249
an_object_having_attributes(task_test),
195-
an_object_having_attributes(task_fixture)
250+
an_object_having_attributes(task_fixture),
251+
an_object_having_attributes(secondary_task_model),
252+
an_object_having_attributes(secondary_task_fixture)
196253
)
197254
end
198255
end

0 commit comments

Comments
 (0)