dry-rb / dry-web

Lightweight web application stack with pluggable routing front-ends
http://dry-rb.org
MIT License
212 stars 19 forks source link

[WIP] Add app-wide config #25

Closed timriley closed 8 years ago

timriley commented 8 years ago

So I started working on an app config loader, but I'm not sure I like it.

You'd use put it on a container like this:

module Berg
  class Container < Dry::Web::Umbrella
    configure do |config|
      config.name = :core
      config.auto_register = %w[lib/authentication]
    end

    # Boot the config as early as we can
    boot! :config

    load_paths! "lib"
  end
end

Via this boot/config.rb file:

require "dry/web/config"

module Berg
  AppConfig = Dry::Web::Config.new do
    required(:admin_url).filled
    required(:admin_mailer_from_email).filled

    # other schema stuff here
  end
end

Berg::Container.finalize :config do |container|
  path = container.root.join("config/application.yml")
  yaml = File.exist?(path) ? YAML.load_file(path)[container.config.env.to_s] : {}

  container.config.options = Berg::AppConfig.load(yaml, ENV)
end

What I do like is that this makes the config loading much more explicit, and within the control of the app developer, rather than it being hard-coded into the dry-web gem (or anything else). This will make it easier for the app developer to make their own tweaks if they like (e.g. loading from a different file, or from ENV only, or anything like that).

I also like the idea of a dry-v doing some work here because it means we can have quite sophisticated validation of app config (e.g. things beyond single-value configs, like "setting A must be if setting B is ")

However:

It also feels like the the connection of the config to the container is really tenuous. It'd be nice if we had a more formal interface to applying config. I know the idea was to experiment with config a little at this dry-web level before looking at improving it in dry-component, but I'm already feeling like we should make it better. What do I mean by more formal? I'm not entirely sure, but perhaps a proper way to "mount" or "boot" a component with external config (like e.g. what we're loading from YAML/ENV), rather than config being this weird object just hanging off Container.config.options.

What I feel like components should do is declare the config they want, and then pull it off whatever config object is passed to them from the outside.

And maybe related, that naming issue of having a "config" object sit at e.g. config.options would be nice to resolve too. We can't use config because dry-configurable owns it, so what can we do?

Anyway, I thought I'd throw this up if anyone wants to take a look and share any thoughts. I'll have another look at things with a fresh mind tomorrow.

timriley commented 8 years ago

My feeling in general is that it might be better to ship a really simple config loader as part of dry-web, and make it easy for app developers to switch out to their own more sophisticated versions themselves (which is why I quite like the idea of having the config setup/loading be explicitly a part of the generated app-code, rather than something wired up automatically by dry-web, since that makes switching easy – we'd just expect a certain interface of the config object, and the rest could be up to the app developer).

timriley commented 8 years ago

We went with #26 instead of this.