palkan/anyway_config

Requiring configurations on specific environments only.

corroded opened this issue · 3 comments

Is your feature request related to a problem? Please describe.

This is probably a small and unpopular feature - mainly because there's an easy workaround. Still, it's a nice to have so suggesting it here. Basically we have some configurations that are only required in production (i.e. SomeConfig.new.private_key).

Describe the solution you'd like

I would suggest being able to set the required config to an environment. Given you have some.yml

default: &default
  host: "http://localhost"
  user: "user"

development:
  <<: *default

production:
  <<: *default

and in some_config.rb:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
    password: {only: :production}, # also consider {except: :development}
  )
end

Describe alternatives you've considered

We have some couple of alternatives:

Option A: Environment specific check

This is what we have, but it feels like it goes against the idea of AnywayConfig:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
  )
  if Rails.env.production?
    required(:password)
  end
end

Option B: Require it in all environments anyway

Not ideal, but also not a deal breaker:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
    :password,
  )
end

WDYT?

I think, the feature makes sense as a part of our Rails integration; and even beyond.

Here is how we can do that:

  • We can add env: parameter to the required method:
# only in production
required :api_key, env: :production
# in production and staging envs
required :api_key, env: %w[production staging]
# excluding test
required :api_key, env: { except: :test } 

Thus, our signature (yeah, we now have RBS support) would be:

def self.required: (*Symbol, env: String | Symbol | Array[String | Symbol] | {except: String | Symbol | Array[String | Symbol]) -> void
  • Then, we need to introduce the notion of environment. For example, we can add new setting (settings.rb), current_environment. It could be set by a user or, in Rails context, could be inferred from Rails.env.
  • Then, we need to introduce the notion of environment. For example, we can add new setting (settings.rb), current_environment. It could be set by a user or, in Rails context, could be inferred from Rails.env.

Does this mean we need a special config called settings.rb? We actually did this by making a custom loader:

# frozen_string_literal: true

# This file is a customized loader based on this file:
#
# https://github.com/palkan/anyway_config/blob/46d5f19814edbba2230dde970abbc73e6bd3ad37/lib/anyway/loaders/yaml.rb
module MyModule
  module Anyway
    module Rails
      module Loaders
        class YAML < ::Anyway::Loaders::YAML
          def load_base_yml(stuff)
            parsed_yml = super
            return parsed_yml unless environmental?(parsed_yml)

            super[custom_env] || {}
          end

          private

          def environmental?(parsed_yml)
            return true if parsed_yml.key?(custom_env)
          end

          def custom_env
            if ::Rails.env.production?
              return 'sandbox' if ENV['sandbox'].to_s == 'true'
              return 'preprod' if ENV['preprod'].present?
            end

            ::Rails.env
          end
        end
      end
    end
  end
end

I meant adding a new parameter to https://github.com/palkan/anyway_config/blob/master/lib/anyway/settings.rb
(and also setting it to Rails.env here https://github.com/palkan/anyway_config/blob/master/lib/anyway/rails/settings.rb).

Regarding your custom loader, I think it would make sense then to use the same Anyway::Settings.current_environment in our YAML loader as well, so you won't need to build your own (and we can even re-use the base YAML loader and drop the Rails specific one).