Skip to content

Commit 1a0aaa9

Browse files
committed
Add top-level module in route file pattern
Rails 4's route loader does not reliably persist top-level module definitions across the routes-reload boundary. A pattern like module Constraints class Admin ... end end defined inside a config/routes/*.rb file may evaluate at boot but the Constraints constant does not survive subsequent reloads, and downstream uses like `constraints Constraints::Admin do ... end` raise NameError: uninitialized constant Constraints. Rails 3.2 handled this more leniently — modules defined in route files persisted across reload boundaries. Fix is two-part, branched on NextRails.next?: 1. Move the module's class definitions to convention paths (e.g. config/routes/constraints/admin.rb for Constraints::Admin) so Rails autoload finds them. 2. Add the parent directory to config.autoload_paths, also gated by NextRails.next?, so the new layout only applies on the Rails 4+ side. Rails 3.2 still wants the modules at the original path inside the route file. The exclude regex skips RSpec:: and Constraints:: references that match the leading `module` pattern but are not actual module definitions. Classified high_priority / kind: breaking — Rails 4 raises NameError on the first reload that hits the dropped constant.
1 parent 142d137 commit 1a0aaa9

1 file changed

Lines changed: 10 additions & 0 deletions

File tree

rails-upgrade/detection-scripts/patterns/rails-40-patterns.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ upgrade_findings:
192192
fix: "Replace hyphens with underscores and CamelCase with snake_case in `to:` values. URL paths keep their original form. The snake_case form resolves to the same controller class on Rails 3.2, so the rewrite ships pre-bump and is backwards-compatible. Example: `to: \"my-pages#show\"` → `to: \"my_pages#show\"`; `to: \"MyPages#show\"` → `to: \"my_pages#show\"`"
193193
variable_name: "ROUTES_CONTROLLER_NAMES"
194194

195+
- name: "Top-level module defined in a route file (autoload-on-reload boundary)"
196+
kind: "breaking"
197+
pattern: "^\\s*module\\s+[A-Z]\\w*"
198+
exclude: "RSpec::|Constraints::"
199+
search_paths:
200+
- "config/routes/"
201+
explanation: "Rails 4's route loader does not reliably persist top-level module definitions across the routes-reload boundary. A pattern like `module Constraints; class Admin; ...; end; end` defined inside a `config/routes/*.rb` file may evaluate at boot but the `Constraints` constant does not survive subsequent reloads — downstream uses (e.g. `constraints Constraints::Admin do ... end`) raise `NameError: uninitialized constant Constraints`. Rails 3.2 was more lenient — modules defined in route files persisted across reload boundaries"
202+
fix: "Two-part fix, gated by `if NextRails.next?`. (1) Move the module's class definitions to convention paths (e.g. `config/routes/constraints/admin.rb` for `Constraints::Admin`) so Rails autoload finds them. (2) Add the parent directory to `config.autoload_paths` in `config/application.rb`: `if NextRails.next?; config.autoload_paths += [Rails.root.join(\"config/routes\")]; end`. Do NOT move the files unconditionally — Rails 3.2 still wants them at the original path inside the route file. Branching the autoload_paths assignment without moving the files means the modules load via Rails convention on the next side and route-file evaluation on the current side"
203+
variable_name: "ROUTES_TOP_LEVEL_MODULE_AUTOLOAD"
204+
195205
medium_priority:
196206
- name: "rescue_action method"
197207
kind: "breaking"

0 commit comments

Comments
 (0)