Providers and prepare vs boot

I have a little prepare/boot misunderstanding it seems:

Unlike the default .env approach, I’m loading access tokens using my dry-credentials gem as a provider:

# config/providers/credentials.rb

Hanami.app.register_provider :credentials do
  prepare do
    require "dry-credentials"

    Dry::Credentials::Extension.new.then do |credentials|
      # gem config stuff

      register "credentials", credentials
    end
  end
end

The Redis URL is such a token and therefore, I can’t setup the Rack session :redis backend in config/app.rb but have to do it at a later point in another provider which makes sure, the credentials provider is prepped:

# config/providers/session.rb

Hanami.app.register_provider :session do
  prepare do
    target.prepare(:credentials)

    target.config.actions.sessions = :redis, {
      redis_server: "#{target['credentials'].current_cache_url}/#{target.settings.cache_namespace}:session",
      expire_after: target.settings.session_timeout
    }
  end
end

This works when serving the site.

But the tests fail since the :session provider prepare block is not called when spec_helper.rb hits require hanami/prepare. (This require does not run all provider prepare blocks as one might think.)

Easy fix: Do a require hanami/boot instead at the cost of booting the entire app on every (even single) test run.

Do you see a better way to tackle this?

For speed reasons, I think I would prefer to keep provider loading lazy. This came up earlier, and here’s my solution

Not directly germane to your question, but my mental model for providers is that prepare is for dependencies, and start is for doing the thing. So I would write yours like this:

# config/providers/credentials.rb

Hanami.app.register_provider :credentials do
  prepare do
    require "dry-credentials"
  end

  start do
    Dry::Credentials::Extension.new.then do |credentials|
      # gem config stuff

      register "credentials", credentials
    end
  end
end
# config/providers/session.rb

Hanami.app.register_provider :session do
  prepare do
    target.start(:credentials)
  end

  start do
    target.config.actions.sessions = :redis, {
      redis_server: "#{target['credentials'].current_cache_url}/#{target.settings.cache_namespace}:session",
      expire_after: target.settings.session_timeout
    }
  end
end

Funny enough, that’s how my providers looked like before intermingling the app prepare and the provider prepares. :grinning: