DotenvRailsDbTasksFix

Fix for the issue when ActiveRecord rake db:* tasks are magically executed in both developement and test environments, but environment variables loaded via dotenv are not picking up the change.

You are viewing the README of version v0.1.0.

Branch Status
Development Build Status Coverage Status

If you're using environment variables in your database.yml and load them via Dotenv chances are you also ran into this annoying issue in your development environment:

database.yml

development:
  database:     <%= ENV['DB_NAME'] %>
  # ...
test:
  database:     <%= ENV['DB_NAME'] %>
  # ...

.env.development

DB_NAME=development
# ...

.env.test

DB_NAME=test
# ...
$ rails db:setup
=>
Created database 'app_development'
Database 'app_development' already exists
$ rails db:setup
=>
Database 'app_development' already exists
Database 'app_development' already exists
rails aborted!
ActiveRecord::EnvironmentMismatchError: You are attempting to modify a database that was last run in `test` environment.
You are running in `development` environment. If you are sure you want to continue, first set the environment using:

        bin/rails db:environment:set RAILS_ENV=development

Using this gem you can do:

Rakefile

# ...
require "dotenv_rails_db_tasks_fix"
DotenvRailsDbTasksFix.activate
$ rails db:setup
=>
Created database 'app_development'
Created database 'app_test'

Explanation

ActiveRecord has this feature that it executes DB tasks in test env as well if the current env is development. (see: environments << "test" if environment == "development"). This happens in the middle of execution so there's no way for dotenv to nicely intervene and even if they did there are things already loaded at this point (e.g. env vars are interpolated to configs). Dotenv's recommendation is to use different env var names (e.g. TEST_DB_NAME, DEVELOPMENT_DB_NAME) but that would be counter-intuitive. Instead here we monkey patch ActiveRecord::Tasks::DatabaseTasks to explicitly reload env vars and the DB config when it switches to test env.

It's invasive and ugly but it only affects the development environment (so it's low-risk) and restores the expected behaviour of this widely used feature therefore sparing the annoyance and possible effort of investigation.

See this issue and this article.

Caveats

  • Database config is expected to reside in Rails default #{DatabaseTasks.root}/config/database.yml (if you're using Rails DatabaseTasks.root == Rails.root)
  • Requires ActiveRecord >= 5.1.5, ~> 5.1.6 (because there're slight differences in the private API, althoguh following this solution it would be easy to extend support for other versions)
  • There's some weirdness with Rails.env vs DatabaseTasks.env. From trial-and-error it seems changing DatabaseTasks.env to reflect the current execution env will result in issues (with e.g. db:setup and db:reset), while changing Rails.env is actually required for db:setup to work correctly. This fix seems to work for the use cases I tried but it's good to keep this in mind in case any similar issue presents.

Installation

Add this line to your application's Gemfile:

gem 'dotenv_rails_db_tasks_fix'

And then execute:

$ bundle

Or install it yourself as:

$ gem install dotenv_rails_db_tasks_fix

Feedback

Any feedback is much appreciated.

I can only tailor this project to fit use-cases I know about - which are usually my own ones. If you find that this might be the right direction to solve your problem too but you find that it's suboptimal or lacks features don't hesitate to contact me.

Let me know if you make use of this project so that I can prioritize further efforts.

Conventions

This gem is developed using the following conventions:

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/thisismydesign/dotenv_rails_db_tasks_fix.

License

The gem is available as open source under the terms of the MIT License.