DEV Community

Cover image for Organizing YAML files by creating directories like `config/x` in Rails
hoshino tsuyoshi
hoshino tsuyoshi

Posted on • Originally published at hoshinotsuyoshi.com

Organizing YAML files by creating directories like `config/x` in Rails

Hello! In this post, I’ll share a simple yet effective way to organize configuration files in a Rails application.
As your Rails project grows, keeping your config/ directory clean and manageable becomes increasingly important.
We’ll explore how creating custom directories like config/x and using config_for can make your YAML configuration files easier to manage and maintain.

Table of Contents

  1. Leveraging Rails.configuration and config_for for Custom Configurations
    • Understanding config.x.something vs config.something
  2. The Problem: Too Many config/*.yml Files
    • Why the default config/ directory gets cluttered
  3. The Solution: Organizing Custom App Settings with config/x/*.yml

Leveraging Rails.configuration and config_for for Custom Configurations

First, let’s talk about Rails.configuration and config_for.

These features were introduced in Rails 4.2 as an alternative to gems like the config gem (nostalgic) or the settings_logic gem (also nostalgic).

Here’s an excerpt from the Rails 7.0 guide:

Rails.configuration

To set configurations, you define them in config/application.rb or similar files:

config.x.payment_processing.schedule = :daily
config.x.payment_processing.retries  = 3
config.super_debugger = true
Enter fullscreen mode Exit fullscreen mode

You can retrieve these values from anywhere in your app like this:

Rails.configuration.x.payment_processing.schedule # => :daily
Rails.configuration.x.payment_processing.retries  # => 3
Rails.configuration.x.payment_processing.not_set  # => nil
Rails.configuration.super_debugger                # => true
Enter fullscreen mode Exit fullscreen mode

Now, about config_for

config_for is a method for loading YAML files.

It’s often used to set values in Rails.configuration.

For example, if you have a YAML file like this:

# config/payment.yml
production:
  environment: production
  merchant_id: production_merchant_id
  public_key:  production_public_key
  private_key: production_private_key

development:
  environment: sandbox
  merchant_id: development_merchant_id
  public_key:  development_public_key
  private_key: development_private_key
Enter fullscreen mode Exit fullscreen mode

You can set these values in config/application.rb as follows:

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.payment = config_for(:payment)
  end
end
Enter fullscreen mode Exit fullscreen mode

In this case, you’d retrieve the values like this:

Rails.configuration.payment['merchant_id'] # => production_merchant_id or development_merchant_id
Enter fullscreen mode Exit fullscreen mode

You can also set the value using config.x.payment = config_for(:payment) if you prefer, like this:

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.x.payment = config_for(:payment)
  end
end
Enter fullscreen mode Exit fullscreen mode

Then, you can retrieve the values like this:

Rails.configuration.x.payment['merchant_id'] # => production_merchant_id or development_merchant_id
Enter fullscreen mode Exit fullscreen mode

What’s the difference between config.x.something and config.something?

From an implementation standpoint, the key difference is that config.x.something allows for nesting.

# Example
config.x.payment_processing.schedule = :daily
Enter fullscreen mode Exit fullscreen mode

Other than that, the Rails guide doesn’t suggest any major differences. However, I personally feel like adding x gives it a custom, localized feel, kind of like custom HTTP headers.

The Problem: Too Many config/*.yml Files

Whether you use x or not, as you make use of these methods, you’ll end up with lots of YAML files in the config/ directory.

Here’s an example of what the config/ folder might look like:

application.rb
bar.yml
baz.yml
boot.rb
cable.yml
corge.yml
database.yml
environment.rb
environments/
foo.yml
fred.yml
garply.yml
grault.yml
initializers/
locales/
newrelic.yml
plugh.yml
puma.rb
quux.yml
qux.yml
routes.rb
sidekiq.yml
spring.rb
storage.yml
thud.yml
waldo.yml
webpack/
webpacker.yml
Enter fullscreen mode Exit fullscreen mode

The mess

It’s not the sheer number of files that’s the problem. The issue is that it becomes difficult to distinguish between YAML files provided by Rails or third-party gems and your custom configurations.

For instance:

  • config/cable.yml and config/database.yml come from Rails.
  • config/sidekiq.yml comes from the sidekiq gem.
  • config/newrelic.yml comes from the New Relic gem.
  • All other config/*.yml files are custom app configurations set using config_for.

In summary, if we were to comment on the files in config/, it might look like this:

config/
├── application.rb
├── database.yml   # from Rails
├── cable.yml      # from Rails
├── sidekiq.yml    # from Sidekiq gem
├── newrelic.yml   # from New Relic gem
├── bar.yml        # custom app config
└── foo.yml        # custom app config
Enter fullscreen mode Exit fullscreen mode

Now compare that to a cleaner structure with config/x/:

config/
├── application.rb
├── x/           # custom app configs live here
│   ├── bar.yml
│   └── foo.yml
├── database.yml
└── cable.yml
Enter fullscreen mode Exit fullscreen mode

Even experienced developers can have a hard time distinguishing between these files at a glance.

The Solution: Organizing Custom App Settings with config/x/*.yml

Here’s my suggestion: config_for can actually load files from directories other than config/. This changed in Rails 5.0 (see the relevant commit here).

So, we could rewrite config/application.rb like this:

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.x.bar   = config_for(Rails.root.join('config/x/bar.yml'))
    config.x.baz   = config_for(Rails.root.join('config/x/baz.yml'))
    config.x.corge = config_for(Rails.root.join('config/x/corge.yml'))
    # ...snip...
    config.x.waldo = config_for(Rails.root.join('config/x/waldo.yml'))
  end
end
Enter fullscreen mode Exit fullscreen mode

By doing this, we can easily distinguish between YAML files provided by Rails or third-party gems and our custom configurations.

Here’s how the config/ folder would look:

# config/
application.rb
boot.rb
cable.yml        # From Rails
database.yml     # From Rails
environment.rb
environments/
initializers/
locales/
newrelic.yml     # From New Relic gem
puma.rb
routes.rb
sidekiq.yml      # From Sidekiq gem
spring.rb
storage.yml      # From Rails
webpack/
webpacker.yml    # From Rails
x/
Enter fullscreen mode Exit fullscreen mode

And in config/x/:

# config/x/
bar.yml          # Custom for this Rails app
baz.yml          # Custom for this Rails app
corge.yml        # Custom for this Rails app
foo.yml          # Custom for this Rails app
fred.yml         # Custom for this Rails app
garply.yml       # Custom for this Rails app
grault.yml       # Custom for this Rails app
plugh.yml        # Custom for this Rails app
quux.yml         # Custom for this Rails app
qux.yml          # Custom for this Rails app
thud.yml         # Custom for this Rails app
waldo.yml        # Custom for this Rails app
Enter fullscreen mode Exit fullscreen mode

By separating the files like this, everything becomes much clearer!

Conclusion

In this post, we discussed how creating a config/x directory can help you organize custom YAML configuration files more efficiently in Rails. By separating app-specific settings from third-party and framework-provided configurations, you not only make your codebase easier to manage, but also improve the clarity for developers who need to understand the system at a glance.

This structure encourages a cleaner, more modular approach, which is particularly beneficial for scaling and maintaining large Rails applications.

Top comments (0)