Class: Hone::Patterns::ConstantRegexp

Inherits:
Base
  • Object
show all
Defined in:
lib/hone/patterns/constant_regexp.rb

Overview

Pattern: /pattern/ inside method -> extract to constant

Regexp literals are recompiled each time the code is executed. Extracting to a constant compiles the regexp once at load time.

Example:

# Bad - recompiles regexp on each call
def process(str)
str.gsub(/\s+/, ' ')
end

# Good - compiled once at load time
WHITESPACE = /\s+/
def process(str)
str.gsub(WHITESPACE, ' ')
end

Note: Only flags regexps without interpolation, as interpolated regexps may need to be dynamic.

Instance Attribute Summary

Attributes inherited from Base

#findings

Instance Method Summary collapse

Methods inherited from Base

#add_finding, inherited, scan_file

Constructor Details

#initialize(file_path) ⇒ ConstantRegexp

Returns a new instance of ConstantRegexp.



28
29
30
31
# File 'lib/hone/patterns/constant_regexp.rb', line 28

def initialize(file_path)
  super
  @in_method = false
end

Instance Method Details

#visit_def_node(node) ⇒ Object

Track when we're inside a method definition



34
35
36
# File 'lib/hone/patterns/constant_regexp.rb', line 34

def visit_def_node(node)
  with_context(:@in_method, true) { super }
end

#visit_interpolated_regular_expression_node(node) ⇒ Object

Skip interpolated regexps as they may need to be dynamic



57
58
59
60
# File 'lib/hone/patterns/constant_regexp.rb', line 57

def visit_interpolated_regular_expression_node(node)
  # Don't call super - we intentionally don't flag interpolated regexps
  # as they often need to be dynamic
end

#visit_regular_expression_node(node) ⇒ Object

Detect static regexp literals inside methods



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/hone/patterns/constant_regexp.rb', line 39

def visit_regular_expression_node(node)
  super

  return unless @in_method

  # Skip if the regexp is very short/simple - the overhead is minimal
  # and extracting trivially small regexps hurts readability
  content = node.content
  return if content.length < 3

  add_finding(
    node,
    message: "Consider extracting regexp `/#{escape_for_message(content)}/` to a constant",
    speedup: "Avoids recompiling regexp on each call"
  )
end