Skip to content

fix(rails): load create_agent.rb explicitly instead of autoloading the whole lib/#314

Open
bexchauveto wants to merge 1 commit into
mainfrom
fix/autoload-lib-pollution
Open

fix(rails): load create_agent.rb explicitly instead of autoloading the whole lib/#314
bexchauveto wants to merge 1 commit into
mainfrom
fix/autoload-lib-pollution

Conversation

@bexchauveto
Copy link
Copy Markdown
Member

@bexchauveto bexchauveto commented Jun 8, 2026

Problem

A customer migrating to forest_admin_rails reported that the gem breaks their boot: their entire lib/ directory ends up managed by Zeitwerk autoloading, conflicting with their explicit loading strategy (explicit require/include).

Root cause: the engine added the host app's whole lib/ directory to Zeitwerk autoload_paths, via this initializer:

initializer 'forest_admin_rails.add_autoload_paths', before: :set_autoload_paths do |app|
  lib_path = Rails.root.join('lib')
  app.config.autoload_paths << lib_path unless app.config.autoload_paths.frozen?
end

The only reason this existed was to resolve the constant ForestAdminRails::CreateAgent from the host's lib/forest_admin_rails/create_agent.rb. But since Rails 6, lib/ is intentionally kept out of autoload_paths precisely because it often contains files that aren't Zeitwerk-compatible or are loaded explicitly. Forcing the host's entire lib/ into autoload is intrusive and is what breaks these apps.

Fix

  • Remove the add_autoload_paths initializer.
  • Load only the file we actually need, explicitly, during load_configuration — right after the existing create_agent_file_exists? guard:
require create_agent_path.to_s

Since lib/ is no longer in autoload_paths, a plain require is safe (no Zeitwerk conflict), and the host's lib/ is left entirely under the app's own control. The path is centralized in a create_agent_path helper reused by create_agent_file_exists?.

Tests

Added to engine_spec.rb:

  • Regression test: the engine no longer registers the forest_admin_rails.add_autoload_paths initializer.
  • create_agent_path points to the expected location under Rails.root.
  • create_agent_file_exists? (present / absent).
  • load_configuration: skips when no web server / file absent; requires the host file explicitly then runs setup when both conditions hold.

All engine_spec.rb examples pass (28, 0 failures) and RuboCop is clean.

…e whole lib/

The engine added the host app's entire `lib/` directory to Zeitwerk
autoload_paths, only to resolve `ForestAdminRails::CreateAgent`. This
forced Zeitwerk on the rest of the host's `lib/`, breaking apps that
manage their own loading (explicit requires/includes).

Drop the `add_autoload_paths` initializer and instead `require` the host's
`lib/forest_admin_rails/create_agent.rb` explicitly during configuration,
right after the existing-file check. The host's `lib/` is left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bexchauveto
Copy link
Copy Markdown
Member Author

✅ Manual verification on a real Rails app (forest-poc / gateway)

Tested against the gateway service of forest-poc, which loads forest_admin_rails from a local path — so it picks up this branch directly.

Note: the gateway opts into config.autoload_lib (the Rails 7.1+ default), so in its stock config lib/ is autoloaded by the app's own choice. To isolate Forest's behaviour and mirror the reporting customer (whose app does not autoload lib/), I temporarily disabled config.autoload_lib and inspected autoload_paths at boot:

Scenario (app does not opt into autoload_lib) lib/ in autoload_paths ForestAdminRails::CreateAgent loads
main (old code) truebug reproduced
this branch falsefixed true

Conclusions:

  1. Bug reproduced on main: Forest forces the host's lib/ into autoload_paths even when the app never asked for it — exactly the customer's report.
  2. Fix confirmed: the gem no longer touches the host's autoload_paths.
  3. No regression: ForestAdminRails::CreateAgent still loads correctly via the explicit require.
  4. The nominal case is unaffected: when the app does opt into autoload_lib, that keeps working — Forest just no longer makes that choice on the app's behalf.

Copy link
Copy Markdown
Member

@matthv matthv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants