Anyway Config

repository·master·Indexed 21 days ago

https://github.com/palkan/anyway_config

A Ruby configuration library that abstracts the configuration layer using classes to define parameters and defaults. It manages loading data from multiple sources, including YAML, environment variables, Rails secrets, and credentials, into a unified, testable interface. Features include automatic type casting for environment variables, support for computed parameters, and built-in Rails integration with generators and autoloading for static and dynamic configurations.

Tokens
14.3K
Snippets
53
Records
66
Agent score
74%

What's inside anyway_config

  1. What is Anyway Config

    master

    Anyway Config is a configuration library for Ruby gems and applications designed to abstract the configuration layer.

    It introduces configuration classes that define available parameters and their default values. This approach provides several benefits:

    • Testability: Configuration is represented by Ruby classes, making it easy to test.
    • Computed Parameters: You can easily add helper methods and computed parameters to your configuration.
    • Decoupling: It frees your code from direct dependencies on ENV, credentials, or secrets by using configuration classes instead.
    • Multi-source Loading: It automatically handles loading parameters from various sources like YAML, credentials, and environment variables using an internal pipeline pattern.

    For library authors, it enables zero-code configuration (no boilerplate initializers) and built-in support for per-environment and local settings.

  2. Configure multi-environment YAML settings in Rails

    master

    By default, if your YAML file contains keys matching Rails environments (e.g., development, production, test), you must separate all settings per-environment. You cannot mix top-level environment keys with global keys.

    Enabling Multi-env feature

    To enable the feature where non-environment keys are available in all environments, use: config.anyway_config.future.use :unwrap_known_environments.

    Customizing known environments

    To add custom environments (e.g., staging) to the known list: Rails.application.config.anyway_config.known_environments << "staging".

    Using default values via a special key

    To provide default values that merge into environmental settings, define a special top-level key using config.anyway_config.default_environmental_key = "default".

    Example configuration:

    default:
      server:
        host: localhost
        port: 3002
    
    staging:
      server:
        host: staging.example.com

    In this example, staging will have host: staging.example.com and port: 3002 (inherited from default).

  3. Override configuration accessors in v2.0+

    master

    Because attr_config accessors in v2.0+ do not populate instance variables, you should use super when overriding methods to ensure compatibility with the internal values store.

    If you are writing a gem that needs to support both v1.x (which used instance variables) and v2.0+ (which uses super), check for the existence of the on_load class method.

    class MyConfig < Anyway::Config
      attr_config :host, :port, :url
    
      # Correct way to override writer in v2.0+
      def meta=(val)
        super(JSON.parse(val))
      end
    
      # Correct way to override reader in v2.0+
      def url
        super || (self.url = "#{host}:#{port}")
      end
    
      # Compatibility pattern for gems supporting v1.x and v2.0+
      def url
        if respond_to?(:on_load)
          super || (self.url = "#{host}:#{port}")
        else
          @url ||= "#{host}:#{port}"
        end
      end
    end
  4. Handle type hints in on_load blocks

    master

    When using the on_load callback with a block, Anyway::Config uses instance_eval to switch the context. To ensure type checkers like Steep can correctly identify the context, you must provide a type hint (e.g., using a YARD annotation) inside the block.

    class MyConfig < Anyway::Config
      on_load do
        # @type self : MyConfig
        raise_validation_error("host is invalid") if host.start_with?("localhost")
      end
    end
  5. Integrate Doppler as a data loader

    master

    Anyway Config can pull secrets from Doppler.

    Automatic Setup: Set the DOPPLER_TOKEN environment variable with a Doppler service token.

    Manual Setup: If you need to configure the loader manually, use the Anyway.loaders.append method.

    Disabling: Set ANYWAY_CONFIG_DISABLE_DOPPLER=true to opt-out if a DOPPLER_TOKEN is present but should not be used by Anyway::Config.

    # Add loader
    Anyway.loaders.append :Doppler, Anyway::Loaders::Doppler
    
    # Configure API URL and token
    Anyway::Loaders::Doppler.download_url = "https://api.doppler.com/v3/configs/config/secrets/download"
    Anyway::Loaders::Doppler.token = ENV["DOPPLER_TOKEN"]
  6. Organize and autoload configuration classes in Rails

    master

    You can store configuration classes in different folders depending on when they are needed:

    Static Configs (config/configs)

    Use this folder for configurations required during the application initialization phase (e.g., setting up ActionMailer).

    • These are loaded via a custom autoloader.
    • Note: They are not reloaded in development.
    • If you use custom inflection rules, you must require_relative your inflections file in config/application.rb before the application block.

    Dynamic Configs (app/configs)

    Use this folder for configurations that are not needed during initialization. These follow standard Rails autoloading behavior.

    Configuration Options

    • Set the static path: config.anyway_config.autoload_static_config_path = "path/to/configs".
    • To treat everything as dynamic, set it to app/configs.
    # app/configs/heroku_config.rb
    class HerokuConfig < Anyway::Config
      attr_config :app_id, :app_name, :dyno_id, :release_version, :slug_commit
    
      def hostname
        "#{app_name}.herokuapp.com"
      end
    end
    
    # config/application.rb
    config.action_mailer.default_url_options = {host: HerokuConfig.new.hostname}
  7. Use EJSON for encrypted configuration

    master

    You can store configuration in encrypted .ejson files. This requires the ejson executable to be in your PATH (recommended to install via the ejson gem).

    Loading Order:

    1. config/secrets.local.ejson
    2. config/<environment>/secrets.ejson
    3. config/secrets.ejson

    Customizing Namespace: By default, the loader searches under a namespace matching the config name. Use loader_options to change this or disable it.

    Disabling: Set ANYWAY_CONFIG_DISABLE_EJSON=true to opt-out.

    class MyConfig < Anyway::Config
      # Look under the key "foo" instead of the default
      loader_options ejson_namespace: "foo"
    
      # Disable namespacing (search in root object)
      loader_options ejson_namespace: false
    end
  8. Use Anyway Config as an OptionParser for CLI apps

    master

    You can use Anyway::Config to handle command-line arguments by integrating with Ruby's optparse. This allows you to define configuration options that can be populated via CLI flags.

    Key features include:

    • ignore_options: Specifies options that should not be handled by the option parser.
    • describe_options: Provides descriptions for options and allows explicit type specification.
    • flag_options: Marks specific options as boolean flags.
    • extend_options: A block where you can access the OptionParser instance to add banners, custom handlers, or tail options (like --help).
    • parse_options!: The method used to parse an array of arguments (e.g., ARGV).

    Note: Values parsed from the CLI are automatically type-cast using the same rules as environment variables unless explicitly overridden in describe_options.

    class MyConfig < Anyway::Config
      attr_config :host, :log_level, :concurrency, :debug, server_args: {}
    
      ignore_options :server_args
    
      describe_options(
        concurrency: "number of threads to use"
      )
    
      flag_options :debug
    
      extend_options do |parser, config|
        parser.banner = "mycli [options]"
    
        parser.on("--server-args VALUE") do |value|
          config.server_args = JSON.parse(value)
        end
    
        parser.on_tail "-h", "--help" do
          puts parser
        end
      end
    end
    
    config = MyConfig.new
    config.parse_options!(%w[--host localhost --port 3333 --log-level debug])
    
    config.host # => "localhost"
    config.port # => 3333
    config.log_level # => "debug"
    
    # Access the underlying OptionParser instance
    config.option_parser
  9. Use Anyway Config in pure Ruby applications

    master

    In non-Rails applications, anyway_config loads data from ./config/<config-name>.yml with the following priority:

    1. YAML configuration files: ./config/<config-name>.yml.
      • To support environment-specific keys (e.g., development:, production:), you must set the current environment: Anyway::Settings.current_environment = "development" or use the ANYWAY_ENV=development environment variable.
      • If no environment is specified, it assumes the YAML contains global values.
      • ERB is supported if require "erb" is called before loading.
    2. Environment variables: ENV['<CONFIG_NAME>_*'].
    require "anyway_config"
    require "erb"
    
    Anyway::Settings.current_environment = "development"
    # Or via ENV['ANYWAY_ENV'] = 'development'
    
    # Lookup paths can be set via:
    Anyway::Settings.default_config_path = "/etc/configs"
  10. Install Anyway Config

    master

    You can install Anyway Config by adding it to your Gemfile or your gem's .gemspec file.

    For a Ruby project

    Add it to your Gemfile:

    gem "anyway_config", "~> 2.0"

    For a Ruby gem

    Add it as a dependency in your my-cool-gem.gemspec:

    Gem::Specification.new do |spec|
      # ...
      spec.add_dependency "anyway_config", ">= 2.0.0"
      # ...
    end

    Supported Ruby versions

    • Ruby (MRI) >= 2.7.0
    • JRuby >= 9.3.0
    # Gemfile
    gem "anyway_config", "~> 2.0"
  11. Test configuration with `with_env` helper

    master

    The with_env helper allows you to test code within the context of specific environment variables. It ensures variables are set during the block and reset to their original state afterward.

    Availability:

    • Automatically included in RSpec if RAILS_ENV or RACK_ENV is "test" and the test is tagged with type: :config or located in spec/configs/....
    • To use manually, require "anyway/testing/helpers" and include Anyway::Testing::Helpers.
    describe HerokuConfig, type: :config do
      subject { described_class.new }
    
      specify do
        with_env("HEROKU_APP_NAME" => "my-app") do
          expect(subject.app_name).to eq("my-app")
        end
      end
    end