Skip to content

Ruby: .rake files (plain Ruby) are never extracted — and the Ruby cross-file resolution passes are .rb-gated too #1784

Description

@krishnateja7

Setup: 0.9.12, uv, macOS, same Rails repo as #1634 / #1640 / #1666.

.rake files are plain Ruby (Rake's task DSL is ordinary Ruby method calls), but the extension is absent from CODE_EXTENSIONS (detect.py L30), so classify_file returns None and every rake task is skipped. The 0.9.9 not-classified warning does surface them (nice: this is not a silent #1666-style drop), but the graph still ends up blind to them.

Expected: .rake routes to the Ruby extractor and participates in Ruby cross-file resolution, exactly like .rb.

Repro (two files):

# ops.rake
class RakeHelper
  def self.run
    Widget.tally
  end
end

namespace :ops do
  task fix: :environment do
    RakeHelper.run
  end
end

# widget.rb
class Widget
  def self.tally
    42
  end
end

graphify extract . --code-onlyfound 1 code, 1 file(s) not classified (no supported extension or shebang), skipped: ops.rake. Only widget.rb's 3 nodes exist; the Widget.tally call site is invisible.

The extractor already handles the content: copy ops.rake to ops_renamed.rb and re-run, and you get the RakeHelper class node, its .run() method, and the cross-file calls edge to widget_widget_tally. So this is purely extension routing, not parsing.

Fix map: the extension is gated in more places than the obvious two. Surveying 0.9.12 for functional .rb gates:

  1. detect.py L30: CODE_EXTENSIONS (classification)
  2. extract.py L3764: ".rb": extract_ruby (extractor dispatch)
  3. extract.py L1781: ".rb": "ruby" (language-name map)
  4. extract.py L2708: LanguageResolver("ruby_member_calls", frozenset({".rb"}), …), the corpus-wide member-call resolver, is extension-gated
  5. ruby_resolution.py L47 + L86: the Ruby cross-file resolution passes filter candidate nodes by sf.endswith(".rb")
  6. analyze.py L31: language stats map
  7. build.py L52: ".rb": "rb" tag map

Worth calling out: patching only 1-3 makes .rake files appear fixed (nodes show up) while 4-5 silently exclude them from cross-file call/constant resolution: nodes without their edges. A shared RUBY_EXTENSIONS = {".rb", ".rake"} constant (or an is_ruby_source() helper) would close all seven at once and make future Ruby-family additions a one-line change.

Why it matters on a real Rails repo: we have 42 tracked .rake files (+ Rakefile). In a 12-entity blast-radius grading on our graph (affected --depth 1 vs exhaustive grep ground truth), ~17% of all true dependents (40/242) were rake tasks, and rake tasks are exactly the files you most want in a blast radius: data backfills, recovery tooling, ops entry points that break silently when a model changes.

Possible follow-up in the same vein (separate scope): extensionless Ruby basenames (Rakefile, Gemfile, Guardfile) currently fall through to the shebang probe and are skipped too; basename-based routing already exists for package manifests, so the same mechanism would cover them.

Happy to re-run our census + recall grading the day this ships.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions