Skip to content

Commit 5ba8aa5

Browse files
committed
Facilitate use of any regular ERB in database.yml
Commit 37d1429 introduced the DummyERB to avoid loading the environment when running `rake -T`. The DummyCompiler simply replaced all output from `<%=` with a fixed string and removed everything else. This worked okay when it was used for YAML values. When using `<%=` within a YAML key, it caused an error in the YAML parser, making it impossible to use ERB as you would expect. For example a `database.yml` file containing the following should be possible: development: <% 5.times do |i| %> shard_<%= i %>: database: db/development_shard_<%= i %>.sqlite3 adapter: sqlite3 <% end %> Instead of using a broken ERB compiler we can temporarily use a `Rails.application.config` that does not raise an error when configurations are accessed which have not been set as described in rails#35468. This change removes the `DummyCompiler` and uses the standard `ERB::Compiler`. It introduces the `DummyConfig` which delegates all known configurations to the real `Rails::Application::Configuration` instance and returns a dummy string for everything else. This restores the full ERB capabilities without compromising on speed when generating the rake tasks for multiple databases. Deprecates `config.active_record.suppress_multiple_database_warning`.
1 parent 876977d commit 5ba8aa5

File tree

11 files changed

+111
-72
lines changed

11 files changed

+111
-72
lines changed

actionmailbox/test/dummy/config/environments/development.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@
4646
# Highlight code that triggered database queries in logs.
4747
config.active_record.verbose_query_logs = true
4848

49-
# Show a warning when Rails couldn't parse your database.yml
50-
# for multiple databases.
51-
config.active_record.suppress_multiple_database_warning = false
52-
5349
# Debug mode disables concatenation and preprocessing of assets.
5450
# This option may cause significant delays in view rendering with a large
5551
# number of complex assets.

actiontext/test/dummy/config/environments/development.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@
4646
# Highlight code that triggered database queries in logs.
4747
config.active_record.verbose_query_logs = true
4848

49-
# Show a warning when Rails couldn't parse your database.yml
50-
# for multiple databases.
51-
config.active_record.suppress_multiple_database_warning = false
52-
5349
# Debug mode disables concatenation and preprocessing of assets.
5450
# This option may cause significant delays in view rendering with a large
5551
# number of complex assets.

activerecord/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
* Allow any ERB in the database.yml when creating rake tasks.
2+
3+
Any ERB can be used in `database.yml` even if it accesses environment
4+
configurations.
5+
6+
Deprecates `config.active_record.suppress_multiple_database_warning`.
7+
8+
*Eike Send*
9+
110
* Add table to error for duplicate column definitions.
211

312
If a migration defines duplicate columns for a table, the error message

activerecord/lib/active_record.rb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,19 @@ def self.global_executor_concurrency # :nodoc:
336336
singleton_class.attr_accessor :dump_schemas
337337
self.dump_schemas = :schema_search_path
338338

339-
##
340-
# :singleton-method:
341-
# Show a warning when Rails couldn't parse your database.yml
342-
# for multiple databases.
343-
singleton_class.attr_accessor :suppress_multiple_database_warning
344-
self.suppress_multiple_database_warning = false
339+
def self.suppress_multiple_database_warning
340+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
341+
config.active_record.suppress_multiple_database_warning is deprecated and will be removed in Rails 7.2.
342+
It no longer has any effect and should be removed from the configuration file.
343+
MSG
344+
end
345+
346+
def self.suppress_multiple_database_warning=(value)
347+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
348+
config.active_record.suppress_multiple_database_warning= is deprecated and will be removed in Rails 7.2.
349+
It no longer has any effect and should be removed from the configuration file.
350+
MSG
351+
end
345352

346353
##
347354
# :singleton-method:

activerecord/lib/active_record/tasks/database_tasks.rb

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,7 @@ def create_all
140140
def setup_initial_database_yaml
141141
return {} unless defined?(Rails)
142142

143-
begin
144-
Rails.application.config.load_database_yaml
145-
rescue
146-
unless ActiveRecord.suppress_multiple_database_warning
147-
$stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
148-
end
149-
150-
{}
151-
end
143+
Rails.application.config.load_database_yaml
152144
end
153145

154146
def for_each(databases)

railties/lib/rails/application/configuration.rb

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,24 +376,34 @@ def paths
376376
end
377377
end
378378

379-
# Load the database YAML without evaluating ERB. This allows us to
380-
# create the rake tasks for multiple databases without filling in the
381-
# configuration values or loading the environment. Do not use this
382-
# method.
379+
# Load the <tt>config/database.yml</tt> to create the Rake tasks for
380+
# multiple databases without loading the environment and filling in the
381+
# environment specific configuration values.
383382
#
384-
# This uses a DummyERB custom compiler so YAML can ignore the ERB
385-
# tags and load the database.yml for the rake tasks.
383+
# Do not use this method, use #database_configuration instead.
386384
def load_database_yaml # :nodoc:
387385
if path = paths["config/database"].existent.first
388-
require "rails/application/dummy_erb_compiler"
386+
require "rails/application/dummy_config"
389387

390-
yaml = DummyERB.new(Pathname.new(path).read).result
388+
original_rails_config = Rails.application.config
389+
dummy_config = DummyConfig.new(original_rails_config)
390+
database_config = {}
391391

392-
if YAML.respond_to?(:unsafe_load)
393-
YAML.unsafe_load(yaml) || {}
394-
else
395-
YAML.load(yaml) || {}
392+
begin
393+
Rails.application.config = dummy_config
394+
395+
yaml = ERB.new(Pathname.new(path).read).result
396+
397+
if YAML.respond_to?(:unsafe_load)
398+
database_config = YAML.unsafe_load(yaml) || {}
399+
else
400+
database_config = YAML.load(yaml) || {}
401+
end
402+
ensure
403+
Rails.application.config = original_rails_config
396404
end
405+
406+
database_config
397407
else
398408
{}
399409
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
class DummyConfig # :nodoc:
4+
def initialize(config)
5+
@config = config
6+
end
7+
8+
def to_s
9+
"DummyConfig"
10+
end
11+
12+
def method_missing(selector, *args, &blk)
13+
if @config.respond_to?(selector)
14+
@config.send(selector, *args, &blk)
15+
else
16+
self
17+
end
18+
end
19+
end

railties/lib/rails/application/dummy_erb_compiler.rb

Lines changed: 0 additions & 18 deletions
This file was deleted.

railties/test/application/configuration_test.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,9 +1741,16 @@ def index
17411741
assert_not ActiveRecord.verbose_query_logs
17421742
end
17431743

1744-
test "config.active_record.suppress_multiple_database_warning is false by default in development" do
1744+
test "config.active_record.suppress_multiple_database_warning getter is deprecated" do
17451745
app "development"
1746-
assert_not ActiveRecord.suppress_multiple_database_warning
1746+
1747+
assert_deprecated { ActiveRecord.suppress_multiple_database_warning }
1748+
end
1749+
1750+
test "config.active_record.suppress_multiple_database_warning setter is deprecated" do
1751+
app "development"
1752+
1753+
assert_deprecated { ActiveRecord.suppress_multiple_database_warning = true }
17471754
end
17481755

17491756
test "config.active_record.use_yaml_unsafe_load is false by default" do

railties/test/application/rake/dbs_test.rb

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,6 @@ def db_create_and_drop(expected_database, environment_loaded: true)
4040
end
4141
end
4242

43-
def db_create_with_warning(expected_database)
44-
Dir.chdir(app_path) do
45-
output = rails("db:create")
46-
assert_match(/Rails couldn't infer whether you are using multiple databases/, output)
47-
assert_match(/Created database/, output)
48-
assert File.exist?(expected_database)
49-
end
50-
end
51-
5243
test "db:create and db:drop without database URL" do
5344
require "#{app_path}/config/environment"
5445
db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: "primary")
@@ -100,6 +91,25 @@ def db_create_with_warning(expected_database)
10091
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
10192
end
10293

94+
test "db:create and db:drop don't raise errors when loading YAML with alias ERB" do
95+
app_file "config/database.yml", <<-YAML
96+
sqlite: &sqlite
97+
adapter: sqlite3
98+
database: db/development.sqlite3
99+
100+
development:
101+
<<: *<%= ENV["DB"] || "sqlite" %>
102+
YAML
103+
104+
app_file "config/environments/development.rb", <<-RUBY
105+
Rails.application.configure do
106+
config.database = "db/development.sqlite3"
107+
end
108+
RUBY
109+
110+
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
111+
end
112+
103113
test "db:create and db:drop don't raise errors when loading YAML with multiline ERB" do
104114
app_file "config/database.yml", <<-YAML
105115
development:
@@ -118,23 +128,21 @@ def db_create_with_warning(expected_database)
118128
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
119129
end
120130

121-
test "db:create and db:drop show warning but doesn't raise errors when loading YAML with alias ERB" do
131+
test "db:create and db:drop don't raise errors when loading ERB accessing nested configurations" do
122132
app_file "config/database.yml", <<-YAML
123-
sqlite: &sqlite
124-
adapter: sqlite3
125-
database: db/development.sqlite3
126-
127133
development:
128-
<<: *<%= ENV["DB"] || "sqlite" %>
134+
database: db/development.sqlite3
135+
adapter: sqlite3
136+
other: <%= Rails.application.config.other.value %>
129137
YAML
130138

131139
app_file "config/environments/development.rb", <<-RUBY
132140
Rails.application.configure do
133-
config.database = "db/development.sqlite3"
141+
config.other = OpenStruct.new(value: 123)
134142
end
135143
RUBY
136144

137-
db_create_with_warning("db/development.sqlite3")
145+
db_create_and_drop("db/development.sqlite3", environment_loaded: false)
138146
end
139147

140148
test "db:create and db:drop don't raise errors when loading YAML containing conditional statements in ERB" do

0 commit comments

Comments
 (0)