class Detector::Ruby::Cli

Overview

Detects Ruby command-line applications: programs that parse argv via the stdlib OptionParser, a < Thor subclass, or a CLI gem (gli, slop, tty-option, commander, optimist, clamp, dry-cli), or that index ARGV directly. Gates the Ruby CLI analyzer. Intentionally import/usage-anchored: bare ENV[...] or a plain ARGV reference (without an index) is too common in web apps to qualify.

Defined in:

detector/detectors/ruby/cli.cr

Constant Summary

ARGV_INDEX = /\bARGV\s*\[\s*\d+\s*\]/
CLAMP_SUBCLASS = /<\s*Clamp::Command\b/
CLI_GEMS = ["thor", "gli", "slop", "tty-option", "commander", "optimist", "clamp", "dry-cli"]
CLI_MARKER = Regex.union(REQUIRE_OPTPARSE, OPTION_PARSER, THOR_SUBCLASS, "require \"thor\"", "require 'thor'", GLI_APP, SLOP_USE, TTY_OPTION, COMMANDER_USE, ARGV_INDEX, OPTIMIST_USE, CLAMP_SUBCLASS, DRY_CLI_SUBCLASS)

Single-pass union of every source marker above (the two thor require forms were bare includes? probes; Regex.union escapes string arguments literally). The previous chain scanned the whole file up to 11 times on non-CLI Ruby sources — the common case.

COMMANDER_USE = /\binclude\s+Commander::Methods\b/
DRY_CLI_SUBCLASS = /<\s*Dry::CLI::Command\b/
GLI_APP = /\binclude\s+GLI::App\b/
OPTIMIST_USE = /\bOptimist(?:::|\.)options\b/
OPTION_PARSER = /\bOptionParser\.new\b/
REQUIRE_OPTPARSE = /\brequire\s+["']optparse["']/
SLOP_USE = /\bSlop\.(?:parse|new)\b/
THOR_SUBCLASS = /<\s*Thor\b/
TTY_OPTION = /\binclude\s+TTY::Option\b/

Instance Method Summary

Instance methods inherited from class Detector

applicable?(filename : String) : Bool applicable?, detect(filename : String, file_contents : String) : Bool detect, gemfile_dependency?(file_contents : String, gem_name : String) : Bool gemfile_dependency?, gemspec_dependency?(file_contents : String, gem_name : String) : Bool gemspec_dependency?, idempotent? : Bool idempotent?, logger : NoirLogger logger, name : String name, path_sensitive? : Bool path_sensitive?

Constructor methods inherited from class Detector

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

Instance Method Detail

def applicable?(filename : String) : Bool #
Description copied from class Detector

Cheap filename-only filter the detector pass uses to skip #detect on files the detector cannot possibly match. The default true preserves prior behavior (every detector runs on every file). Override with the same predicate the body of #detect starts with — e.g., filename.ends_with?(".py") for a Python framework detector — so the detector loop avoids the #detect dispatch on files outside the detector's language.

On large codebases (saleor's 4255 .py files) this lifts ~100 virtual #detect calls per file out of the hot loop because most detectors' inner first-line is exactly this kind of cheap filename check.


[View source]
def detect(filename : String, file_contents : String) : Bool #

[View source]
def set_name #

[View source]