Follow up #2890: harden generator route checks#2891
Conversation
Reduce new-app generator fragility by centralizing root-route detection and making home-page generation resilient to action ordering changes. Also add an explicit railties-major guard around create-app template rendering so unsupported Rails internals fail safely to fallback scaffolds.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughAdds railties-major validation to the embedded Rails template renderer (exits on unsupported majors) and centralizes root-route detection in a new Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as create-react-on-rails CLI
participant Ruby as ruby -e (embedded renderer)
participant Gem as Gem::Specification
participant ERB as ERB/template
CLI->>Ruby: execute renderer script
Ruby->>Gem: find_by_name("railties")
Gem-->>Ruby: spec (version, gem_dir)
Ruby->>Ruby: set railties_major = spec.version.major
alt railties_major in [7,8]
Ruby->>ERB: render template from spec.gem_dir
ERB-->>Ruby: rendered output
Ruby-->>CLI: template applied
else unsupported
Ruby->>Ruby: warn with spec.version
Ruby-->>CLI: exit 1
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ReviewOverall the consolidation is a clean improvement — deduplicating three copies of the file-scan logic into a single Behavioural change: memoization removed from The old implementation cached its result in This is safe today because the double-invocation guard ( Side effect inside a predicate method
Test coverage gap
Fallback coverage for the railties version guard The new TS test verifies the guard expression is present in the script string, but doesn't confirm the fallback scaffold is actually used when the Ruby process exits 1. If that path isn't covered elsewhere, a note or additional test would help. CHANGELOG The checklist item is unchecked — presumably intentional, but worth confirming. |
size-limit report 📦
|
Greptile SummaryThis PR follows up #2890 by hardening the generator's root-route handling in two complementary ways. On the Ruby side it centralises the previously-duplicated
Confidence Score: 5/5Safe to merge — all remaining findings are minor style suggestions with no impact on correctness or runtime behaviour. The refactoring is logically correct: centralized route detection removes duplication, the double-invocation guard is consistent across No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant Gen as Generator Actions
participant ARR as add_root_route
participant CBF as copy_base_files
participant GHP as generate_new_app_home_page?
participant RRP as root_route_present?
Note over Gen: Normal action order
Gen->>ARR: invoke add_root_route
ARR->>RRP: preexisting_root_route? → root_route_present?
RRP-->>ARR: true/false
ARR-->>ARR: set @new_app_root_route_added
Gen->>CBF: invoke copy_base_files
CBF->>GHP: generate_new_app_home_page?
GHP-->>GHP: defined?(@new_app_root_route_added) → true, skip lazy call
GHP-->>CBF: @new_app_root_route_added value
Note over Gen: Resilient (reversed) order
Gen->>CBF: invoke copy_base_files first
CBF->>GHP: generate_new_app_home_page?
GHP-->>GHP: defined?(@new_app_root_route_added) → false
GHP->>ARR: lazy call add_root_route
ARR->>RRP: preexisting_root_route? → root_route_present?
RRP-->>ARR: true/false
ARR-->>ARR: set @new_app_root_route_added
ARR-->>GHP: return
GHP-->>CBF: @new_app_root_route_added value
Gen->>ARR: invoke add_root_route (scheduled)
ARR-->>ARR: defined?(@new_app_root_route_added) → true, no-op return
Reviews (1): Last reviewed commit: "Harden generator route checks and templa..." | Re-trigger Greptile |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb (1)
337-346: Consider adding edge case coverage for alternative root route syntax.The
root_route_present?regex uses\bword boundary, which should also match the shorthand syntaxroot "home#index"(withoutto:). A quick additional test case would confirm this and serve as documentation.💡 Optional: additional test case
it "matches shorthand root syntax without 'to:'" do File.write(routes_path, <<~RUBY) Rails.application.routes.draw do root "home#index" end RUBY expect(root_route_present?).to be(true) end🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb` around lines 337 - 346, Add a spec that verifies the shorthand root syntax without `to:` is detected by root_route_present?: create a new example in generator_helper_spec.rb that writes a routes file containing Rails.application.routes.draw do with a line `root "home#index"` and then asserts expect(root_route_present?).to be(true); reference the existing test pattern (the surrounding examples and routes_path usage) and mirror its File.write/expect structure to ensure the regex handles the alternative shorthand syntax.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb`:
- Around line 337-346: Add a spec that verifies the shorthand root syntax
without `to:` is detected by root_route_present?: create a new example in
generator_helper_spec.rb that writes a routes file containing
Rails.application.routes.draw do with a line `root "home#index"` and then
asserts expect(root_route_present?).to be(true); reference the existing test
pattern (the surrounding examples and routes_path usage) and mirror its
File.write/expect structure to ensure the regex handles the alternative
shorthand syntax.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e833e8fa-0d83-4eef-a97a-234cb6d3a98e
📒 Files selected for processing (8)
packages/create-react-on-rails-app/src/create-app.tspackages/create-react-on-rails-app/tests/create-app.test.tsreact_on_rails/lib/generators/react_on_rails/base_generator.rbreact_on_rails/lib/generators/react_on_rails/demo_page_config.rbreact_on_rails/lib/generators/react_on_rails/generator_helper.rbreact_on_rails/lib/generators/react_on_rails/install_generator.rbreact_on_rails/spec/react_on_rails/generators/base_generator_spec.rbreact_on_rails/spec/react_on_rails/generators/generator_helper_spec.rb
Cover the File.foreach.any? returning false path in root_route_present? specs, where routes.rb exists but contains no root route definition. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
ReviewThe consolidation of the three duplicate file-scan blocks into 1. Side effect inside a predicate — CQS violation
def generate_new_app_home_page?
return false unless options.new_app?
add_root_route unless defined?(@new_app_root_route_added)
new_app_root_route_added?
end
The safer path is to keep 2. Memoization removed from
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@react_on_rails/lib/generators/react_on_rails/base_generator.rb`:
- Around line 329-335: The helper ensure_new_app_root_route_initialized
currently calls add_root_route which performs an unguarded File.read on
config/routes.rb; to prevent a raised error when that file is missing, add a
presence check for the routes file before calling add_root_route (e.g., return
with a warning/skip if File.exist?("config/routes.rb") is false) or
alternatively modify add_root_route to safely handle a missing config/routes.rb
(guard the File.read, bail out gracefully and log a warning). Ensure the change
references ensure_new_app_root_route_initialized and add_root_route so the
generator remains safe when actions are reordered.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a72f66c2-e937-4b11-9cce-2b5ef7843d6a
📒 Files selected for processing (3)
packages/create-react-on-rails-app/tests/create-app.test.tsreact_on_rails/lib/generators/react_on_rails/base_generator.rbreact_on_rails/spec/react_on_rails/generators/base_generator_spec.rb
✅ Files skipped from review due to trivial changes (1)
- react_on_rails/spec/react_on_rails/generators/base_generator_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/create-react-on-rails-app/tests/create-app.test.ts
Code ReviewOverall this is a clean, well-scoped PR. Centralizing Notable issue: silent fallback with no user notification
Consider logging the caught error at info/warn level before returning Minor: CHANGELOG not updatedThe PR checklist has this unchecked — worth confirming whether this release should be captured or if it is intentionally deferred. Nit: version-list/template coupling is undocumented in the test suiteThe embedded Ruby template is baked from |
The root_route_present? helper safely returns false when config/routes.rb is missing, but add_root_route then falls through to an unguarded File.read that raises Errno::ENOENT. Add a File.file? check before the read so the generator warns gracefully instead of crashing. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
| @preexisting_root_route = root_route_present? | ||
| end | ||
|
|
||
| def ensure_new_app_root_route_initialized |
There was a problem hiding this comment.
The defined?(@new_app_root_route_added) guard here is redundant — add_root_route already has the identical guard on line 113. If ordering is wrong and add_root_route hasn't run, calling it here will set @new_app_root_route_added normally. If it has run, add_root_route will no-op immediately.
The early return here adds a tiny short-circuit but also obscures the invariant. Consider dropping it and relying solely on the guard inside add_root_route, or add a comment explaining the optimization:
| def ensure_new_app_root_route_initialized | |
| def ensure_new_app_root_route_initialized | |
| return unless options.new_app? | |
| # add_root_route is idempotent via its own defined? guard; call unconditionally. | |
| add_root_route | |
| end |
|
|
||
| spec = Gem::Specification.find_by_name("railties") | ||
| railties_major = spec.version.segments.first | ||
| unless [${SUPPORTED_RAILTIES_MAJORS.join(', ')}].include?(railties_major) |
There was a problem hiding this comment.
This embeds SUPPORTED_RAILTIES_MAJORS (a TypeScript readonly tuple) directly into a Ruby heredoc via .join(', '). That works for simple integers, but the interpolation is invisible to anyone reading just the Ruby logic later and there's no compile-time or runtime guarantee the generated Ruby is syntactically valid (e.g. if the array ever holds strings or floats).
A minimal safeguard would be an assertion at the TypeScript level:
const majorList = SUPPORTED_RAILTIES_MAJORS.join(', ');
// Sanity-check: values must be plain integers so the Ruby literal stays valid.
if (!/^\d+(, \d+)*$/.test(majorList)) {
throw new Error(`SUPPORTED_RAILTIES_MAJORS contains non-integer values: ${majorList}`);
}Or at minimum a comment noting the constraint so future contributors don't accidentally break the generated Ruby.
| root to: "home#index" | ||
| end | ||
| RUBY | ||
|
|
There was a problem hiding this comment.
The three new examples cover the public routes_path default well. One gap worth adding: the helper accepts a custom routes_path argument (used by callers that pass an explicit path) — a quick example that passes an explicit path and finds a root route would confirm the parameter is wired correctly and not accidentally shadowed.
| @new_app_root_route_added = false | ||
|
|
||
| if preexisting_root_route? | ||
| say_status :skip, "Root route already exists; keeping existing root route", :yellow |
There was a problem hiding this comment.
When a pre-existing root route is found, @new_app_root_route_added is initialized to false (line 115) and the method returns here — so generate_new_app_home_page? will be false, suppressing home-page generation even when the app already has a valid root route.
This is almost certainly the right intent (avoid trampling user-customized roots), but it's non-obvious. A short comment here (and a matching spec example) would make the invariant explicit and protect against someone "fixing" it later.
Review SummaryGood hardening work overall. Centralizing Must-fix / worth discussingCHANGELOG not updated — the PR checklist item is unchecked. Per project policy this should be filled in before merge. Pre-existing root route suppresses home-page generation silently — when Redundant MinorTypeScript→Ruby interpolation in
Looks good
|
Summary
Follow up issue #2890 by hardening generator behavior around new-app root-route handling and removing duplicated root-route detection logic. This centralizes root-route checks in a shared helper used by base/install/demo-page generator paths and makes home-page generation resilient to action-order refactors. It also adds an explicit railties major-version guard around
RAILS_GIT_TEMPLATE_RENDERERincreate-react-on-rails-app, with fallback scaffolds preserved when unsupported.Pull Request checklist
Update documentationOther Information
Validated locally with targeted specs and linting:
bundle exec rspec react_on_rails/spec/react_on_rails/generators/base_generator_spec.rb react_on_rails/spec/react_on_rails/generators/generator_helper_spec.rbbundle exec rspec react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:117 react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:138 react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:1594 react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:1912pnpm --filter create-react-on-rails-app run test -- create-app.test.tsbundle exec rubocopon touched Ruby files andpnpm eslinton touched TS sourceNote
Medium Risk
Touches Rails generator scaffolding paths (root-route injection, landing-page enablement, and git scaffold restoration), so regressions could affect newly generated apps and Windows checkouts. Changes are localized and covered by new unit/spec tests, reducing risk.
Overview
Generator hardening for
--new-app: Centralizes root-route detection into a sharedroot_route_present?helper and reuses it across base/install/demo-page generator flows to remove duplicated parsing.More resilient root-route injection:
add_root_routenow guards against double invocation, warns (instead of raising) whenconfig/routes.rbis missing or unexpected, andcopy_base_filesexplicitly ensures root-route state is initialized so home-page generation won’t break if generator action ordering changes.CLI git scaffold robustness:
create-react-on-rails-appnow only attempts rendering Rails’ installed.gitignore/.gitattributestemplates whenrailtiesmajor is in a supported set (7/8); failures fall back to bundled defaults. Tests were added/updated to cover the railties guard, fallback behavior, and the new root-route helper and warnings.Written by Cursor Bugbot for commit 05aa93a. This will update automatically on new commits. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes
Tests