Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

database.yml gets evaluated before initializers run since Rails 7.1.0 #51578

Closed
2called-chaos opened this issue Apr 16, 2024 · 7 comments
Closed

Comments

@2called-chaos
Copy link
Contributor

Steps to reproduce

  1. Add to database.yml <% puts "database.yml" %>
  2. Create initializers/foo.rb with puts "initializer"
  3. Run rails db:migrate or rails db:create on <7.1 (e.g. 7.0.8.1) and >= 7.1.0

Expected behavior (as was the case <7.1)

Initializers run before database.yml gets interpreted by ERB.

initializer
database.yml

Actual behavior (since 7.1)

Initializers now run after database.yml has been interpreted by ERB

database.yml
initializer

System configuration

Rails version: 7.0.8.1, 7.1.0, 7.1.3.2

Ruby version: 3.3.0


Not sure if this is intended but as far as I can tell not documented. This obviously breaks if you attempt to use config data sourced and/or configured by an initializer in your database.yml.

@justinko
Copy link
Contributor

This is expected.

Prior to 7.1, if you had multiple databases in an environment (e.g. shard_1, shard_2), and you ran bin/rake -T, you wouldn't see tasks such as rake db:create:shard_1 because the environment was never loaded therefore database.yml was never loaded.

Rails 7.1 gets around this by loading database.yml with a fake/dummy app config (not the real environment) and when you run bin/rake -T you'll see:

rake db:create:shard_1                  # Create shard_1 database for current environment
rake db:create:shard_2                  # Create shard_2 database for current environment

When you actually run a task like db:create in Rails 7.1, it'll load database.yml with the dummy app config, load the actual app, and then load database.yml again with the non-dummy/real app config.

So no, nothing breaks.

@2called-chaos
Copy link
Contributor Author

Alright so it doesn't matter if the dummy does not get proper credentials? Not sure what the best course of action is for us but it does break if your initializer defines something your yml attempts to use like a config object (that doesn't ignore missing keys).

Like I can use Rails.application.credentials but where would I initialize my custom config if not in an initializer, even environment.rb is too late?

@rafaelfranca
Copy link
Member

That is strange. The dummy config thing should not have changed the order. Did you find the commit that changed this behavior?

@justinko
Copy link
Contributor

@rafaelfranca it's not that the order has changed, it's that the ERB is no longer being stripped out (on initial database.yml load). Here's the commit that changes it: #46134

@rafaelfranca
Copy link
Member

I just wanted to confirm the change that introduced the change of behavior.

@2called-chaos if you custom configuration is inside Rails.application.config things would just work. Closing since this isn't an issue.

@rafaelfranca rafaelfranca closed this as not planned Won't fix, can't repro, duplicate, stale Apr 18, 2024
@justinko
Copy link
Contributor

@justinko
Copy link
Contributor

This is expected.

Prior to 7.1, if you had multiple databases in an environment (e.g. shard_1, shard_2), and you ran bin/rake -T, you wouldn't see tasks such as rake db:create:shard_1 because the environment was never loaded therefore database.yml was never loaded.

Rails 7.1 gets around this by loading database.yml with a fake/dummy app config (not the real environment) and when you run bin/rake -T you'll see:

rake db:create:shard_1                  # Create shard_1 database for current environment
rake db:create:shard_2                  # Create shard_2 database for current environment

When you actually run a task like db:create in Rails 7.1, it'll load database.yml with the dummy app config, load the actual app, and then load database.yml again with the non-dummy/real app config.

So no, nothing breaks.

This was totally wrong. I understand it now. Here's the proper explanation:

Let's say you have a constant in an initializer named FOO and you use it as a value in database.yml. Prior to Rails 7.1, this ERB value would get replaced with an empty string. So on the initial database.yml load, you won't get the "uninitialized constant" error.

In Rails 7.1, the ERB values in database.yml are not replaced, so when it is initially loaded, you'll get NameError: uninitialized constant FOO (NameError). The parsing of database.yml is dependent on the interface of Rails config, so it makes sense to use it for custom config values. An alternative would be to place your code after Bundler.require in application.rb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants