Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails initializer that runs *after* routes are loaded?

I want to set a class attribute when my Rails app starts up. It requires inspecting some routes, so the routes need to be loaded before my custom code runs. I am having trouble finding a reliable place to hook in.

This works PERFECTLY in the "test" environment:

config.after_initialize do
  Rails.logger.info "#{Rails.application.routes.routes.map(&:path)}"
end

But it doesn't work in the "development" environment (the routes are empty)

For now I seem to have things working in development mode by running the same code in config.to_prepare which I understand happens before every request. Unfortunately using to_prepare alone doesn't seem to work in test mode, hence the duplication.

I'm curious why the routes are loaded before after_initialize in test mode, but not in development mode. And really, what is the best hook for this? Is there a single hook that will work for all environments?

*EDIT*

mu's suggestion of reloading the routes was great. It gave me consistent access to the routes within after_initialize in all environments. For my use case though, I think I still need to run the code from to_prepare as well, since I'm setting a class attribute on a model and the models are reloaded before each request.

So here's what I ended up doing.

[:after_initialize, :to_prepare].each do |hook|
  config.send(hook) do
    User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
  end 
end 

It seems a bit messy to me. I think I'd rather do something like:

config.after_initialize do
  User.exclude_routes_from_usernames!
end

config.to_prepare do
  User.exclude_routes_from_usernames!
end

But I'm not sure if User is the right place to be examining Rails.application.routes. I guess I could do the same thing with code in lib/ but I'm not sure if that's right either.

Another option is to just apply mu's suggestion on to_prepare. That works but there seems to be a noticeable delay reloading the routes on every request in my dev environment, so I'm not sure if this is a good call, although it's DRY, at least.

config.to_prepare do
  Rails.application.reload_routes!
  User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
end
like image 919
poochenza Avatar asked Jan 03 '12 03:01

poochenza


People also ask

What is initializer in Rails?

An initializer is any file of ruby code stored under /config/initializers in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded.

What is application RB in rails?

The configuration file config/application. rb and environment-specific configuration files (such as config/environments/production. rb ) allow you to specify the various settings that you want to pass down to all of the components. For example, you could add this setting to config/application.rb file: config.


2 Answers

You can force the routes to be loaded before looking at Rails.application.routes with this:

Rails.application.reload_routes!

So try this in your config/application.rb:

config.after_initialize do
  Rails.application.reload_routes!
  Rails.logger.info "#{Rails.application.routes.routes.map(&:path)}"
end

I've done similar things that needed to check the routes (for conflicts with /:slug routes) and I ended up putting the reload_routes! and the checking in a config.after_initialize like you're doing.

like image 176
mu is too short Avatar answered Nov 16 '22 15:11

mu is too short


If you're trying to run code in an initializer after the routes have loaded, you can try using the after: option:

initializer "name_of_initializer", after: :add_routing_paths do |app|
  # do custom logic here
end

You can find initialization events here: http://guides.rubyonrails.org/configuring.html#initialization-events

like image 33
Mieczysław Daniel Dyba Avatar answered Nov 16 '22 13:11

Mieczysław Daniel Dyba