In Rails projects I often use YAML files to organize app-wide values or configurations.
# config/settings.yml shared: important_value: 42 development: default_from_email: 'firstname.lastname@example.org' test: default_from_email: 'email@example.com' production: default_from_email: 'firstname.lastname@example.org'
In the application configuration we create a new configuration:
# config/application.rb module RailsApp class Application < Rails::Application # ... config.settings = config_for(:settings) end end
We can access these values using
Rails.configuration.settings, for example:
# $ RAILS_ENV=test bin/rails c # Loading test environment (Rails 7.0.0) Rails.configuration.settings[:important_value] # => 42 Rails.configuration.settings[:default_from_email] # => "email@example.com"
Unfortunately the configuration is not reloaded automatically. If we change the
important_value and refresh the page, we'd still see the old value. In order to see the value, we'd need to restart the server first. This can be confusing and tedious, particularly in development.
Here's a way to reload the configuration every time the file changes.
Create a new file
# config/initializers/config_for_reloader.rb if Rails.env.development? config_for_reloader = ActiveSupport::FileUpdateChecker.new(["config/settings.yml"]) do Rails.application.config.settings = Rails.application.config_for(:settings) end ActiveSupport::Reloader.to_prepare do config_for_reloader.execute_if_updated end end
ActiveSupport::FileUpdateCheckertakes an Array of file paths and a block containing the instructions to be executed on change. (Documentation for
In our case the block contains the same instruction as in
config/application.rb for loading the YAML file.
ActiveSupport::Reloader.to_prepareregisters a callback that will run once at application startup and every time the code is reloaded. (Documentation for
At this point, I would have expected that Rails picks up the changes automatically after the file
config/settings.yml is modififed, but for some reason it does not. It only reloads the file and sets the configuration after an "application file" (a model, template, controller, routes, etc.) is modified.
We can workaround this by adding
config/settings.yml to the
# config/application.rb module RailsApp class Application < Rails::Application # ... config.settings = config_for(:settings) config.eager_load_paths << Rails.root.join("config/settings.yml") end end
(If anybody knows why that is necessary, I'd be interested.)
Now, the configuration file is reloaded after changing it and a page refresh would show the latest value.
(Cover image by Christopher Alvarenga on Unsplash)
Top comments (3)
sharedkey wasn't working for me on a Rails 5 app. I needed to use YAML anchors as mentioned in this SO answer.
Thank you @colindean , that's useful information!
sharedkey was introduced in PR #37913 which was in the early stages of Rails 6.
I'm just wondering how awful this is if I use this solution production.
I have a case where I need to update a config that is called in middleware. e.g. creating a hostname redirect for a multi-tenant application based on a YAML config. It would be wasteful to restart the whole application, but inefficient to read a file in on every request.