15

Je souhaite définir un attribut de classe lorsque mon application Rails démarre. Cela nécessite d'inspecter certaines routes, donc les routes doivent être chargées avant l'exécution de mon code personnalisé. Je ne parviens pas à trouver un endroit fiable pour accrocher dansInitialiseur Rails qui exécute * après * les routes sont chargées?

Cela fonctionne parfaitement dans l'environnement « test »:.

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

Mais il ne le fait pas travail dans l'environnement « développement » (les routes sont vides)

Pour l'instant je semble avoir des choses fonctionnant en mode de développement en exécutant le même code dans config.to_prepare qui je crois arrive avant chaque demande. Malheureusement, en utilisant to_prepare seul ne semble pas fonctionner en mode test, d'où la duplication.

Je suis curieux de savoir pourquoi les routes sont chargées avant after_initialize en mode test, mais pas en mode développement. Et vraiment, quel est le meilleur crochet pour cela? Y a-t-il un seul crochet qui fonctionnera pour tous les environnements?

* EDIT *

suggestion de mu de rechargement des routes était super. Cela m'a donné un accès cohérent aux routes dans after_initialize dans tous les environnements. Pour mon cas d'utilisation cependant, je pense que je dois encore exécuter le code de to_prepare, puisque je mets un attribut de classe sur un modèle et que les modèles sont rechargés avant chaque requête.

Alors voici ce que j'ai fini par faire.

[: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 

Cela me semble un peu compliqué. Je pense que je préfère faire quelque chose comme:

config.after_initialize do 
    User.exclude_routes_from_usernames! 
end 

config.to_prepare do 
    User.exclude_routes_from_usernames! 
end 

Mais je ne sais pas si User est le bon endroit pour être examiner Rails.application.routes. Je suppose que je pourrais faire la même chose avec du code dans lib/mais je ne suis pas sûr que ce soit correct non plus.

Une autre option consiste à appliquer simplement la suggestion de mu sur to_prepare. Cela fonctionne mais il semble y avoir un retard notable recharger les routes sur chaque demande dans mon environnement de développement, donc je ne suis pas sûr si c'est un bon appel, même si c'est sec, au moins.

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 

Répondre

22

Vous pouvez forcer les routes à charger avant de regarder Rails.application.routes avec ceci:

Rails.application.reload_routes! 

Donc, essayez dans votre config/application.rb:

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

J'ai fait des choses similaires nécessaire pour vérifier les routes (pour les conflits avec /:slug routes) et j'ai fini par mettre le reload_routes! et la vérification dans un config.after_initialize comme vous le faites.

+0

Excellente idée! Il s'avère que nous l'utilisons pour la même chose (je mets à jour un 'class_attribute' appelé' User.invalid_usernames' qui est utilisé comme liste d'exclusion avec 'validates_exclusion_of'). Je pense que j'ai encore besoin du to_prepare pour le mode développement. Sans cela, cela fonctionne bien sur la première requête (maintenant que j'utilise votre suggestion), mais après cela, je pense que mon 'User.invalid_usernames = Set.new' l'écrase. Il semble que vous n'utilisiez que after_initialize, donc je me demande s'il y a une façon intelligente de contourner cela? – poochenza

+0

Cette réponse m'a beaucoup aidé alors je l'accepte même si je n'ai pas fini par l'utiliser (ouvert à la critique si vous pensez que je devrais bien!) Voici ma solution complète, aimerais vos commentaires si vous avez le temps: http: //stackoverflow.com/a/8713207/1126857 – poochenza

+0

@poochenza: J'ai utilisé 'after_initialize' pour m'assurer qu'aucun nouveau conflit n'est apparu entre les versions: si vous ajoutez une route'/pancakes' et publiez deux semaines plus tard, vous voulez savoir si Quelqu'un a créé un utilisateur de "crêpes" sur le système de production en même temps. Ensuite, je compare les nouveaux noms d'utilisateur par rapport aux itinéraires lorsque les noms d'utilisateur sont créés ou mis à jour. –

2

Si vous essayez d'exécuter du code dans un initialiseur après les routes ont chargé, vous pouvez essayer d'utiliser l'option after::

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

Vous pouvez trouver des événements d'initialisation ici: http://guides.rubyonrails.org/configuring.html#initialization-events

+0

Cet initialiseur ne sera jamais exécuté pour moi dans les rails 4.1.14.1 – nolith

Questions connexes