Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config.after_routes_loaded defined on config/application.rb never called #50720

Closed
everton opened this issue Jan 12, 2024 · 7 comments
Closed

Comments

@everton
Copy link

everton commented Jan 12, 2024

Steps to reproduce

  1. Pass a block to config.after_routes_loaded inside the config/application.rb
  2. Run the application by any means (rails runner, rails console, rails server, etc)
  3. The block will never get called
# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # gem 'rails', '7.1.2'

  # If you want to test against edge Rails replace the previous line with this:
  gem "rails", github: "rails/rails", branch: "main"
end

require "action_controller/railtie"

AFTER_ROUTES_LOADED_CALLBACK_CALLED = false

class TestApp < Rails::Application
  config.root = __dir__
  config.hosts << "example.org"
  config.secret_key_base = "secret_key_base"

  config.logger = Logger.new($stdout)
  Rails.logger  = config.logger

  config.after_routes_loaded do
    ::AFTER_ROUTES_LOADED_CALLBACK_CALLED = true
  end

  routes.draw do
    get "/" => "test#index"
  end
end

class TestController < ActionController::Base
  include Rails.application.routes.url_helpers

  def index
    render plain: "Home"
  end
end

require "minitest/autorun"
require "rack/test"

class BugTest < Minitest::Test
  include Rack::Test::Methods

  def test_returns_success
    get "/"
    assert last_response.ok?
  end

  def test_after_routes_loaded_callback_called
    assert AFTER_ROUTES_LOADED_CALLBACK_CALLED,
      'after_routes_loaded was never called'
  end

  private
    def app
      Rails.application
    end
end

Expected behavior

The block should be called after application routes are loaded (including on each possible reload of the routes)

Actual behavior

The block is never called

System configuration

Rails version: tested against both 7.1.2 and edge

Ruby version: 3.3.0

@bparanj
Copy link
Contributor

bparanj commented Jan 12, 2024

This occurs on Ruby 3.2.2 also. I was testing to see if this happens in Rails 7.0. However I get an error:

/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:63:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'rails'. (Bundler::GemRequireError)
Gem Load Error is: Rails::Engine is abstract, you cannot instantiate it directly.
Backtrace for gem load error is:
/usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:246:in `initialize'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:184:in `new'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:184:in `instance'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:223:in `method_missing'
/usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/descendants_tracker.rb:90:in `descendants'
/usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:923:in `block in define_callbacks'
/usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `each'
/usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `define_callbacks'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:427:in `<class:Engine>'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:349:in `<module:Rails>'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:11:in `<top (required)>'
<internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
<internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/application.rb:11:in `<top (required)>'
<internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
<internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
/usr/local/bundle/gems/railties-7.0.0/lib/rails.rb:13:in `<top (required)>'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:60:in `require'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:60:in `block (2 levels) in require'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `each'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `block in require'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `each'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `require'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:66:in `block (2 levels) in gemfile'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/settings.rb:158:in `temporary'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:51:in `block in gemfile'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `block in with_unbundled_env'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:658:in `with_env'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `with_unbundled_env'
/usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:42:in `gemfile'
test.rb:5:in `<main>'
Bundler Error Backtrace:

	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:59:in `block (2 levels) in require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `each'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `block in require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `each'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:66:in `block (2 levels) in gemfile'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/settings.rb:158:in `temporary'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:51:in `block in gemfile'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `block in with_unbundled_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:658:in `with_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `with_unbundled_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:42:in `gemfile'
	from test.rb:5:in `<main>'
/usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:246:in `initialize': Rails::Engine is abstract, you cannot instantiate it directly. (RuntimeError)
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:184:in `new'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:184:in `instance'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/railtie.rb:223:in `method_missing'
	from /usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/descendants_tracker.rb:90:in `descendants'
	from /usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:923:in `block in define_callbacks'
	from /usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `each'
	from /usr/local/bundle/gems/activesupport-7.0.0/lib/active_support/callbacks.rb:920:in `define_callbacks'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:427:in `<class:Engine>'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:349:in `<module:Rails>'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/engine.rb:11:in `<top (required)>'
	from <internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from <internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails/application.rb:11:in `<top (required)>'
	from <internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from <internal:/usr/local/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from /usr/local/bundle/gems/railties-7.0.0/lib/rails.rb:13:in `<top (required)>'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:60:in `require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:60:in `block (2 levels) in require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `each'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:55:in `block in require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `each'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/runtime.rb:44:in `require'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:66:in `block (2 levels) in gemfile'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/settings.rb:158:in `temporary'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:51:in `block in gemfile'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `block in with_unbundled_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:658:in `with_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler.rb:403:in `with_unbundled_env'
	from /usr/local/bundle/gems/bundler-2.5.4/lib/bundler/inline.rb:42:in `gemfile'
	from test.rb:5:in `<main>'

Test case:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem 'rails', '7.0.0'
  gem 'timeout', '0.4.1'
  # If you want to test against edge Rails replace the previous line with this:
end

require "action_controller/railtie"

AFTER_ROUTES_LOADED_CALLBACK_CALLED = false

class TestApp < Rails::Application
  config.root = __dir__
  config.hosts << "example.org"
  config.secret_key_base = "secret_key_base"

  config.logger = Logger.new($stdout)
  Rails.logger  = config.logger

  config.after_routes_loaded do
    ::AFTER_ROUTES_LOADED_CALLBACK_CALLED = true
  end

  routes.draw do
    get "/" => "test#index"
  end
end

class TestController < ActionController::Base
  include Rails.application.routes.url_helpers

  def index
    render plain: "Home"
  end
end

require "minitest/autorun"
require "rack/test"

class BugTest < Minitest::Test
  include Rack::Test::Methods

  def test_returns_success
    get "/"
    assert last_response.ok?
  end

  def test_after_routes_loaded_callback_called
    assert AFTER_ROUTES_LOADED_CALLBACK_CALLED,
      'after_routes_loaded was never called'
  end

  private
    def app
      Rails.application
    end
end

Dockerfile:

# Use an official Ruby runtime as a parent image
FROM ruby:3.2.2

RUN gem update --system
RUN gem install bundler
RUN gem install stringio -v 3.1.0
RUN gem install psych -v 5.1.2
# Install SQLite3
RUN apt-get update -qq && apt-get install -y sqlite3 libsqlite3-dev

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy the script into the container at /usr/src/app
COPY . .
# Run the script when the container launches
CMD ["ruby", "test.rb"]

@rafaelfranca
Copy link
Member

I'm pretty sure this works because I use it every day. Let me try in a new app.

@rafaelfranca
Copy link
Member

Yes. It does work, just not in the way you are expecting. In your test script there is no routes being reloaded. It only runs when they are reloaded, not when routes are first loaded.

Now, if it should also run when routes are first loaded is a good question. @shioyama do you remember?

@everton
Copy link
Author

everton commented Jan 12, 2024

@rafaelfranca Yes, I can confirm it works on reload, but since the name is *_loaded and the guides says:

config.after_routes_loaded
Takes a block which will be run after Rails has finished loading the application routes. This block will also be run whenever routes are reloaded.

https://guides.rubyonrails.org/configuring.html#config-after-routes-loaded

I just assumed it was supposed to run on both cases (I need to overwrite some helpers on my application globally, and it must work on load or reloads).

To circumvent the issue, I forced triggering it in the after_initialize, but this doesn't seem to be the best/proper way to do it:

    config.after_initialize do
      Rails.application.reload_routes!
      ActiveSupport.run_load_hooks(:after_routes_loaded, self)
    end

@rafaelfranca
Copy link
Member

Right. I'm just trying to figure out if it is the name that is wrong, or it is the implementation that is wrong.

rafaelfranca added a commit that referenced this issue Jan 12, 2024
rafaelfranca added a commit that referenced this issue Jan 12, 2024
@shioyama
Copy link
Contributor

Sorry, it seems that the behaviour is wrong here. It should run after the routes are loaded in boot. I was thinking too narrowly of our own use case.

rafaelfranca added a commit that referenced this issue Jan 15, 2024
This hook was only running when routes were reloaded, but not on boot.

The goal was to run any time routes are loaded. This commit fixes it
and adds a test.

Fixes #50720.
@everton
Copy link
Author

everton commented Jan 17, 2024

We just upgraded our application to 7.1.3 and it is working as expected. Thank you!

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

No branches or pull requests

5 participants