dry-rb / dry-rails

The official dry-rb railtie
https://dry-rb.org/gems/dry-rails
MIT License
270 stars 27 forks source link

[WIP] Add support for Rails::Engine #47

Closed zlw closed 3 years ago

zlw commented 3 years ago

Closed in favour of #49

Don't mind me yet, just looking what's what 👀

image

Adding dry-rails to monolith Rails app works like a charm, thanks for all the work 🙇‍♂️

However, it's not so great when used with Engines :bowtie:

I think each Engine should define its own Container, otherwise we're loosing whole namespace isolation that Engines provide

MainApp::Container.resolve('users.create')        #> Dry::Container::Error: Nothing registered with the key "users.create"
SuperComponent::Container.resolve('users.create') #> #<SomeComponent::Users::Create:0x00007fa6d963e368>

🥳

# /components/some_component/lib/some_component/engine.rb
module SuperComponent
  class Engine < ::Rails::Engine
    isolate_namespace SuperComponent

    initializer 'super_component.dry_container' do |_app|
      Dry::Rails::Engine.container(:super_component) do
        config.features = %i[application_contract]
        config.auto_registrar = Dry::System::AutoRegistrar
        auto_register!('domain')
      end
    end

    config.to_prepare do
      Engine.finalize!
    end

    # Code-reloading-aware finalization process
    #
    # This sets up `Container` and `Deps` constants, reloads them if this is in reloading mode,
    # and registers default components like the railtie itself or the inflector
    #
    # @api public
    #
    delegate :finalize!, to: :finalizer
    alias reload finalize!

    # Stops all configured features (bootable components)
    #
    # This is *crucial* when reloading code in development mode. Every bootable component
    # should be able to clear the runtime from any constants that it created in its `stop`
    # lifecycle step
    #
    # @api public
    delegate :stop_features, to: :finalizer

    # Exposes the container constant
    #
    # @return [Dry::Rails::Container]
    #
    # @api public
    delegate :container, to: :finalizer

    # @api private
    delegate :set_or_reload, to: :finalizer

    # @api private
    delegate :remove_constant, to: :finalizer

    private

    def finalizer
      @finalizer ||= Dry::Rails::Engine::Finalizer.new(
        railtie: self,
        app_namespace: SuperComponent,
        root_path: ::Rails.root.join('components/super_component'),
      )
    end
  end
end

# /components/some_component/domain/some_component/users/create.rb
module SuperComponent
  module Users
    class Create
    end
  end
end
zlw commented 3 years ago

Closing in favour of #49