In addition to the production environment and local development environment, there are generally multiple environments such as staging environment and QA environment. How to manage the different setting values for each environment (for example, database settings, URLs and IDs of linked services, etc.) is a point to worry about when operating a Rails application. In this article, I will show you how to manage the setting values for each environment with yml by using Rails :: Application.config_for prepared by Rails without increasing RAILS_ENV.
https://speakerdeck.com/spring_mt/deep-environment-parity-cdnt-2019 Assuming that there are multiple environments, please refer to this slide for the story of trying to eliminate the difference in the sense of environment and match them.
module MyApp
class Application < Rails::Application
config.application= config_for(:application)
end
end
If you write it like this, it will read /application.yml under the config directory and read the setting value that matches RAILS_ENV.
Rails.configuration.x.application[:hoge]
After reading, you can get the value of the key called hoge in yml by calling it as above at the place you want to use. This feature is very convenient and recommended by Rails, so I would like to actively use it.
To use Rails :: Application :: config_for as is, increase RAILS_ENV and it's simple. Certainly, it is often said that config / environments / staging.rb etc. are prepared to increase environment variables. However, I don't think this method is very good. The reason is that RAILS_ENV is an environment variable for changing the behavior of the framework called Rails, so I think that using it to change the setting value of the application like this time is out of the idea of Rails. Because I think.
Since confidential information such as database passwords is often passed through environment variables, it is natural to use all setting values as environment variables as well. However, environment variables will be set on the infrastructure side, and since it is an application setting value, you may want to manage it in the application repository, so avoid managing it with environment variables.
If you can add environment variables casually and manage them in the repository nicely, this method is also good. In our project, we manage the infrastructure using CloudFormation, but CloudFormation is managed in a separate repository, and the timing of deployment is different, so we have stopped managing it with environment variables.
https://speakerdeck.com/spring_mt/deep-environment-parity-cdnt-2019?slide=47 Do it as if it were on pages 47-51 of the slides I introduced earlier. However, only the part that uses the prehook of Entrykit is tampered with. I will explain in detail for the time being. As mentioned above, I want to manage with yml for config_for, so place yml with the following directory structure. The contents of yml are omitted, but the key at the beginning of yml must be the same key as RAILS_ENV (development, test or production)
config/stage_settings
├── circleci
│ ├── application.yml
│ ├── database.yml
│ ├── secrets.yml
│ └── sentry.yml
├── local
│ ├── application.yml
│ ├── database.yml
│ ├── secrets.yml
│ └── sentry.yml
├── production
│ ├── application.yml
│ ├── database.yml
│ ├── secrets.yml
│ └── sentry.yml
└── staging
├── application.yml
├── database.yml
├── secrets.yml
└── sentry.yml
Rails :: Application.config_for is a method that can read the yml file under config, so you need to move yml under the stage_settings directory nicely under config.
In the slide introduced earlier, it was realized by using the prehook of Entrykit, but if it is a process that is always performed when Rails starts, I thought that it would be easier to describe it in config / boot.rb, so I changed it. (Config / boot.rb should be the only place to put processing before starting Rails ...)
# config/boot.rb
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
#↑ is the code automatically generated by Rails. Original processing from here
stage = ENV.fetch('APP_STAGE', 'local')
path = File.join('config', 'stage_settings', stage)
raise "#{path} is not a directory." unless File.exist?(path)
FileUtils.cp_r(Dir.glob("#{path}/*"), File.join('config'))
Describe it like this. A new environment variable called APP_STAGE has been added, which is an environment variable to represent the environment such as staging and qa environment.
Now every time you start up, all the files under stage_settings will be copied under the config directory. In order to decide which yml to use at startup, add it to .gitignore so that yml is not committed directly under the config directory.
The reason for this is that some people in the team are developing with Docker and others are not, and I don't want to increase the hassle of having to do this to set up a local development environment. I came up with this because I thought there was a good way to do it.
--RAILS_ENV does not increase even if you want to change the setting value for each environment --I want to manage the setting value for each environment with yml --I plugged the process into boot.rb and made it feel good