abstract class Analyzer::Ruby::RubyEngine

Direct Known Subclasses

Defined in:

analyzer/engines/ruby_engine.cr

Constant Summary

HANAMI_ROUTER_RE = /Hanami::Rout|Hanami\.app\b|Hanami\.routes\b/
HTTP_VERBS = ["get", "post", "put", "delete", "patch", "head", "options"]
RAILS_ROUTER_RE = /\.routes\.draw\b|ActionDispatch::Routing/

Rails, Hanami and Sinatra all spell a route get "/books" …, and every Ruby analyzer is handed every .rb file in the scan, so each one could parse the others' route table with its own DSL. These two predicates identify a file as unmistakably belonging to Rails or to Hanami; an analyzer that is neither uses them to step aside.

Rails.application.routes.draw and Hanami::Routes are positive markers — a router that carries one is that framework's and nobody else's. There is deliberately no Sinatra equivalent: a Sinatra route file can be a bare get "/x" do … end with no framework reference at all, which is why the checks run in this direction only.

RUBY_NON_PRODUCTION_DIRS = Set {"spec", "test", "tests", "features", "benchmark", "benchmarks", "examples", "coverage", "tmp"}
RUBY_NON_PRODUCTION_PATH_RE = begin alternation = RUBY_NON_PRODUCTION_DIRS.join("|") do |dir| Regex.escape(dir) end Regex.new("\\A(?:#{alternation})(?:/|\\z)|/(?:#{alternation})/") end

Whole-tree Ruby analyzers (Sinatra/Grape/Roda/WEBrick) otherwise see Rack test helpers, benchmark apps, and framework examples as live services. Match these directories only relative to the configured scan root so noir's own fixture tree under spec/functional_test/fixtures is not accidentally skipped when the fixture directory itself is the base path. Exact translation of the three-way check this replaces: relative == dir and relative.starts_with?("#{dir}/") collapse to the leading branch, relative.includes?("/#{dir}/") to the second. Deliberately NOT (?:\A|\/)dir(?:\/|\z), which would also match a trailing segment (app/spec) the old chain rejected.

The chain built two interpolated Strings per directory per call — 18 allocations on every Ruby file, for every whole-tree Ruby analyzer — before scanning for each of them separately.

RUBY_SOURCE_EXTENSIONS = [".rb", ".ru"]

Walk Ruby sources (.rb / .ru) in parallel. Used by analyzers that scan the whole tree (Sinatra, Grape, Roda, WEBrick); Rails/Hanami target specific config files directly. Candidates come from the extension index so monorepo file_map entries in other languages never enter the channel. Paths are detector- registered regular files — no per-path File.exists? / File.directory?.

Name-consistent with the other engines' parallel_file_scan helpers.

VERB_ROUTE_PATTERNS = HTTP_VERBS.map do |verb| {verb, /^#{verb}\s*\(?\s*['"](.+?)['"]/} end

Crystal recompiles an interpolated regex literal on every evaluation (a full PCRE2 JIT compile). The verb set is fixed, so precompile the <verb> "<path>" matchers once at load time.

Class Method Summary

Instance Method Summary

Instance methods inherited from class Analyzer

analyze analyze, base_path : String base_path, base_paths : Array(String) base_paths, base_relative_path(path : String) : String base_relative_path, callees_needed? : Bool callees_needed?, content_matches?(content : String, markers : Regex) : Bool content_matches?, http_header_name(name : String) : String | Nil http_header_name, line_number_for_index(content : String, char_index : Int32) : Int32 line_number_for_index, logger : NoirLogger logger, parallel_analyze(files : Array(String), &block : String -> Nil) parallel_analyze, read_file_content(path : String) : String read_file_content, result : Array(Endpoint) result, unique_params(params : Array(Param)) : Array(Param) unique_params, url : String url, web_root_path(path : String, markers : Array(String)) : String web_root_path

Constructor methods inherited from class Analyzer

new(options : Hash(String, YAML::Any)) new

Macros inherited from class Analyzer

analyzer_for(tech) analyzer_for

Instance methods inherited from module FileHelper

all_files : Array(String) all_files, get_files_by_basename(basename : String) : Array(String) get_files_by_basename, get_files_by_extension(extension : String) : Array(String) get_files_by_extension, get_files_by_extensions(extensions : Array(String)) : Array(String) get_files_by_extensions, get_files_by_prefix(prefix : String) : Array(String) get_files_by_prefix, get_files_by_prefix_and_extension(prefix : String, extension : String) : Array(String) get_files_by_prefix_and_extension, get_files_by_relative_path(relative_path : String, root : String = "") : Array(String) get_files_by_relative_path, get_public_dir_files(base_path : String, folder : String) : Array(String) get_public_dir_files, get_public_files(base_path : String, anchors : Array(String) = ["shard.yml", "Gemfile"]) : Array(String) get_public_files, walked_path(expanded : String) : String walked_path

Class Method Detail

def self.ruby_test_path?(path : String) : Bool #

Minitest's *_test.rb and RSpec's *_spec.rb conventions are rigid in Ruby — rake test / mri test only run files matching the first, and rspec discovers the second. Production Ruby never adopts either filename, so the suffix check is safe for every Ruby analyzer (sinatra, grape, roda, hanami). Promoted from Analyzer::Ruby::Sinatra (#1571) so the rest of the family stays in sync.

Test::Unit / Minitest equally support the inverse test_*.rb prefix (rake test globs test/test_*.rb). gollum, for instance, exercises its modular Sinatra app exclusively through test/test_app.rb-style files whose inline Rack::Test requests (get "/wiki/Home", post "/gollum/upload_file") are indistinguishable from real route registrations to a line-based matcher — ~70 phantom endpoints per scan. Production route files never carry the test_ prefix, so it is as safe as the suffix forms. We match on the basename only (never the full path) so a legitimate app that merely lives under a spec/-suffixed absolute path — e.g. noir's own fixtures — is untouched.


[View source]

Instance Method Detail

def line_to_endpoint(content : String, details : Details | Nil = nil) : Endpoint #

Match the <verb> "<path>" idiom on a single line and return the first endpoint found, or an empty endpoint if none match. Shared by Hanami and Sinatra (Rails uses a different per-line-multi-match shape).


[View source]