RubyMine’s Static Rails Model: Jump‑to‑Definition Without Booting
RubyMine builds a static Rails model by parsing routes.rb, scanning app/ directories, and applying conventions — enabling jump‑to‑definition for route helpers, associations, partials, and helpers without booting the app.
02 Jun 2026, 06:27 UTC

The problem: Rails magic is opaque to static analysis
Rails developers know the friction: you’re in a view typing render 'shared/header' and need to find the partial. Or you see redirect_to user_path(@user) and want to jump to the controller action. The framework relies on conventions, metaprogramming, and runtime evaluation — routes.rb draws helpers dynamically, has_many :orders generates association methods at boot, and engines mount at paths only known when the server starts. Traditional editors treat these as plain text. RubyMine takes a different approach: it builds a project‑wide Rails model statically, without booting the application.
How the static Rails model is built
On project open, RubyMine parses config/routes.rb as Ruby code (not just regex) to extract every resources, namespace, mount, and custom get/post declaration. It scans app/models, app/controllers, app/views, app/helpers, and lib for class and module definitions. It reads db/schema.rb or db/structure.sql for column metadata. It applies Rails inflections (person → people, analysis → analyses) and engine mount points declared statically in routes. All of this happens in the IDE’s indexer — no rails server, no database connection, no environment boot required.
The model updates incrementally on file save. If you add a new route or model association, the change propagates in sub‑second increments. A full re‑index (File → Invalidate Caches / Restart) is only needed when conventions shift: a new engine added, custom inflections defined in config/initializers/inflections.rb, or a generated route file appears.
Route helpers resolve to controller actions
When you type user_ in a controller or view, completion shows user_path, user_url, edit_user_path, new_user_path — each mapped to its UsersController#show, #edit, #new. Ctrl/Cmd‑click (or F4) on any helper navigates directly to the action method. This works for namespaced routes too: admin_product_path resolves to Admin::ProductsController#show.
Worked example: Open app/views/users/index.html.erb. Type <%= link_to 'Edit', edit_user_path(user) %>. Place the cursor on edit_user_path and press Ctrl/Cmd+B (Navigate → Declaration). RubyMine opens app/controllers/users_controller.rb at the edit action. No server running, no request cycle — the indexer already knows the route maps to that controller and action.
ActiveRecord associations become navigable symbols
Model files are parsed for has_many, belongs_to, has_one, has_and_belongs_to_many calls. The indexer cross‑references schema.rb for foreign keys and table names. In a view or controller, typing user.orders. triggers completion with build, create, where, find — the methods generated by has_many :orders. Ctrl/Cmd‑click on orders jumps to the Order model class. Find Usages (Alt+F7) on orders in User reveals every call site across views, controllers, services, and specs.
Partials and helpers resolve across namespaces and engines
render 'shared/header' resolves to app/views/shared/_header.html.erb. render partial: 'form', collection: @posts finds app/views/posts/_form.html.erb. If an engine mounts at mount BlogEngine => '/blog' in routes.rb, its partials under blog_engine/app/views appear in completion when rendering from the main app. Helper methods from ApplicationHelper, UsersHelper, and included modules like Devise::Controllers::Helpers surface in views and controllers with jump‑to‑definition.
Where the model falls short
- Dynamic route constraints:
get '/:slug', to: 'pages#show', constraints: { slug: ->(s) { Page.exists?(slug: s) } }— the lambda isn’t evaluated statically, so the helper may not appear until the route file is analyzable. - Metaprogrammed associations: Concerns or macros like
define_association :commentsorinclude Commentablethat injecthas_manyat runtime are not inferred. Adding YARD@!method commentsor@!attributetags in the concern can improve coverage. - Large monoliths: Projects over ~5,000 files may see initial indexing take 1–3 minutes. Incremental updates after that are typically sub‑second.
- Runtime engine mounts: If
mount MyEngine => '/admin'is wrapped in a conditional or loaded from a config variable, the mount line isn’t statically visible and helper resolution fails.
RubyMine 2024.1+ uses a new Rust‑based indexer for Ruby. Projects upgrading from 2023.x should trigger a full re‑index once to benefit from improved accuracy on complex route drawings and association inference.
Verify it in your project
- Open a Rails project in RubyMine 2024.2+.
- Press Ctrl/Cmd+Shift+A, type Show Rails Model, and inspect the inferred routes, models, and helpers tree.
- In a view file, type
render 'and observe completion listing partials underapp/views. Ctrl/Cmd‑click a partial name to jump to the file. - In a controller, type
redirect_to user_and verify route helpers appear. Click one to navigate to the corresponding controller action. - Run Find Usages (Alt+F7) on a model’s association method (e.g.,
ordersonUser) to confirm call sites across views, controllers, and specs.
If any of these checks miss expected targets, run File → Invalidate Caches / Restart → Invalidate and Restart. That forces a clean rebuild of the static model.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.