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

2 Likes

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
1 Like

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